comfyui-mcp 0.52.62 → 0.52.63
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.
|
@@ -814,6 +814,10 @@ export const __panelToolsTestHooks = {
|
|
|
814
814
|
setRunLateAckGraceMs(ms) {
|
|
815
815
|
runLateAckGraceMsOverride = ms;
|
|
816
816
|
},
|
|
817
|
+
/** Shrink the #2078 save-timeout settle grace so a still-dirty follow-up does not wait 5s. */
|
|
818
|
+
setSaveTimeoutSettleGraceMs(ms) {
|
|
819
|
+
saveTimeoutSettleGraceMsOverride = ms;
|
|
820
|
+
},
|
|
817
821
|
isRetrySafeCmd,
|
|
818
822
|
isTransientReconnectError,
|
|
819
823
|
// CivitAI sample-image gating (#623): the predicate that decides which results'
|
|
@@ -3615,30 +3619,87 @@ function saveLandedAfterTimeout(list) {
|
|
|
3615
3619
|
const rec = active;
|
|
3616
3620
|
return rec.modified === false && rec.persisted === true;
|
|
3617
3621
|
}
|
|
3622
|
+
/**
|
|
3623
|
+
* A first save legitimately changes `tmp:<instance>` into `wf:<path>`. The
|
|
3624
|
+
* updated panel publishes the save command's eventual success under its exact
|
|
3625
|
+
* rid, so only that receipt can bridge the identity change. If the receipt field
|
|
3626
|
+
* is absent or does not match, the outcome remains unknown rather than crediting
|
|
3627
|
+
* a clean workflow that may belong to a tab the user switched to.
|
|
3628
|
+
*/
|
|
3629
|
+
function saveProbeMatchesLateReceipt(saveRid, list) {
|
|
3630
|
+
if (!saveRid || !Array.isArray(list?.late_save_receipts))
|
|
3631
|
+
return false;
|
|
3632
|
+
if (!list?.active || typeof list.active !== "object" || Array.isArray(list.active))
|
|
3633
|
+
return false;
|
|
3634
|
+
const receipt = list.late_save_receipts.find((candidate) => candidate &&
|
|
3635
|
+
typeof candidate === "object" &&
|
|
3636
|
+
!Array.isArray(candidate) &&
|
|
3637
|
+
candidate.rid === saveRid &&
|
|
3638
|
+
candidate.cmd === "workflow_save");
|
|
3639
|
+
if (!receipt || typeof receipt !== "object" || Array.isArray(receipt))
|
|
3640
|
+
return false;
|
|
3641
|
+
const result = receipt.result;
|
|
3642
|
+
if (!result || typeof result !== "object" || Array.isArray(result))
|
|
3643
|
+
return false;
|
|
3644
|
+
if (result.saved !== true)
|
|
3645
|
+
return false;
|
|
3646
|
+
// The panel's receipt is the save command's own eventual success. Still require
|
|
3647
|
+
// its produced workflow identity to match the currently observed active canvas;
|
|
3648
|
+
// a valid late save on A must not be credited after the user switched to B.
|
|
3649
|
+
return identityVerdict(result, list.active) === true;
|
|
3650
|
+
}
|
|
3618
3651
|
const SAVE_TIMEOUT_OUTCOME_UNKNOWN = `\n\nOUTCOME UNKNOWN: the save was already in flight when this budget fired. ` +
|
|
3619
3652
|
`Confirm by reading the saved file (or panel_list_workflows) before retrying — a re-issue may write twice.`;
|
|
3620
3653
|
/**
|
|
3621
|
-
* #
|
|
3622
|
-
*
|
|
3623
|
-
*
|
|
3624
|
-
*
|
|
3654
|
+
* #2078 — how long to keep reading tab state after the 13s save budget.
|
|
3655
|
+
*
|
|
3656
|
+
* #2004 already listed once. The reporter's PUT landed a moment later: that
|
|
3657
|
+
* snapshot was still dirty, the next panel_list_workflows was already clean.
|
|
3658
|
+
* Paid only on a path that has already spent its full bound. A canvas that
|
|
3659
|
+
* is clean on the first list returns immediately.
|
|
3660
|
+
*/
|
|
3661
|
+
const SAVE_TIMEOUT_SETTLE_GRACE_MS = 5_000;
|
|
3662
|
+
const SAVE_TIMEOUT_SETTLE_POLL_MS = 200;
|
|
3663
|
+
let saveTimeoutSettleGraceMsOverride = null;
|
|
3664
|
+
function saveTimeoutSettleGraceMs() {
|
|
3665
|
+
return saveTimeoutSettleGraceMsOverride ?? SAVE_TIMEOUT_SETTLE_GRACE_MS;
|
|
3666
|
+
}
|
|
3667
|
+
function saveAckAfterTimeout(list) {
|
|
3668
|
+
return ok({
|
|
3669
|
+
saved: true,
|
|
3670
|
+
late_ack: true,
|
|
3671
|
+
acknowledged_after_timeout: true,
|
|
3672
|
+
active: list?.active,
|
|
3673
|
+
});
|
|
3674
|
+
}
|
|
3675
|
+
/**
|
|
3676
|
+
* #2004 / #2078 — the panel's 13s budget fired while userdata PUT was still
|
|
3677
|
+
* running. A list that shows modified:false persisted:true is the save
|
|
3678
|
+
* completing. One snapshot is not enough: the timeout-time canvas is still
|
|
3679
|
+
* dirty, and the PUT can land between that read and the caller's next list.
|
|
3680
|
+
* Keep reading until the grace, then stay outcome-unknown. persisted:true
|
|
3681
|
+
* alone is the timeout-time snapshot of a previously-saved dirty tab, not proof.
|
|
3625
3682
|
*/
|
|
3626
|
-
async function settleWorkflowSaveTimeout(res, ctx) {
|
|
3683
|
+
async function settleWorkflowSaveTimeout(res, ctx, saveRid) {
|
|
3627
3684
|
if (!isWorkflowSaveBudgetTimeout(res))
|
|
3628
3685
|
return res;
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
}
|
|
3686
|
+
const deadline = Date.now() + saveTimeoutSettleGraceMs();
|
|
3687
|
+
for (;;) {
|
|
3688
|
+
try {
|
|
3689
|
+
const listRes = await ctx.call({ cmd: "workflow_list" }, 6000);
|
|
3690
|
+
const list = parseToolResultJson(listRes);
|
|
3691
|
+
if (saveLandedAfterTimeout(list) &&
|
|
3692
|
+
saveProbeMatchesLateReceipt(saveRid, list)) {
|
|
3693
|
+
return saveAckAfterTimeout(list);
|
|
3694
|
+
}
|
|
3638
3695
|
}
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3696
|
+
catch {
|
|
3697
|
+
/* keep polling until the grace; the note below is the honest remainder */
|
|
3698
|
+
}
|
|
3699
|
+
const left = deadline - Date.now();
|
|
3700
|
+
if (left <= 0)
|
|
3701
|
+
break;
|
|
3702
|
+
await sleep(Math.max(1, Math.min(SAVE_TIMEOUT_SETTLE_POLL_MS, left)));
|
|
3642
3703
|
}
|
|
3643
3704
|
return appendToolResultText(res, SAVE_TIMEOUT_OUTCOME_UNKNOWN);
|
|
3644
3705
|
}
|
|
@@ -13812,9 +13873,12 @@ export function buildPanelToolDefs() {
|
|
|
13812
13873
|
if (ctx.awaitReachable && !(await ctx.awaitReachable())) {
|
|
13813
13874
|
return noReachableTabFail(args.name ? "workflow_save_as" : "workflow_save", ctx);
|
|
13814
13875
|
}
|
|
13876
|
+
let saveRid;
|
|
13815
13877
|
const saveOnce = () => args.name
|
|
13816
13878
|
? ctx.call({ cmd: "workflow_save_as", name: args.name }, 15000)
|
|
13817
|
-
: ctx.call({ cmd: "workflow_save" }, 15000)
|
|
13879
|
+
: ctx.call({ cmd: "workflow_save" }, 15000, (rid) => {
|
|
13880
|
+
saveRid = rid;
|
|
13881
|
+
});
|
|
13818
13882
|
let res = await saveOnce();
|
|
13819
13883
|
// #1710 — dest tab + dest nodes, extra still stamped as the save-as SOURCE.
|
|
13820
13884
|
// Rebind extra to the confirmed dest, then retry the same save once.
|
|
@@ -13830,7 +13894,7 @@ export function buildPanelToolDefs() {
|
|
|
13830
13894
|
}
|
|
13831
13895
|
}
|
|
13832
13896
|
if (res.isError && !args.name)
|
|
13833
|
-
res = await settleWorkflowSaveTimeout(res, ctx);
|
|
13897
|
+
res = await settleWorkflowSaveTimeout(res, ctx, saveRid);
|
|
13834
13898
|
if (res.isError) {
|
|
13835
13899
|
// #1873 — named Save-As 409s by contract; the panel's "choose a different
|
|
13836
13900
|
// name" is what forced a third file. Keep the 409 and name the rename path.
|