@hubfluencer/mcp 0.9.1 → 0.11.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.
- package/README.md +19 -11
- package/dist/index.js +283 -53
- package/package.json +1 -1
- package/src/core.ts +111 -37
- package/src/index.ts +393 -66
- package/src/output-schemas.ts +57 -2
- package/src/uploads.ts +2 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hubfluencer/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Model Context Protocol server for Hubfluencer — let AI agents generate post-ready shorts and editor ads.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Monocursive <contact@monocursive.com>",
|
package/src/core.ts
CHANGED
|
@@ -265,6 +265,16 @@ export interface NormalizedStatus {
|
|
|
265
265
|
ready: boolean;
|
|
266
266
|
video_url: string | null;
|
|
267
267
|
error: string | null;
|
|
268
|
+
/** Short only: exact API failure phase instead of a collapsed terminal state. */
|
|
269
|
+
failed_stage?: string;
|
|
270
|
+
/** Short only: stable machine-readable terminal failure classification. */
|
|
271
|
+
failure_code?: string;
|
|
272
|
+
/** Short only: structured provider/QC details for recovery decisions. */
|
|
273
|
+
failure_details?: Record<string, unknown>;
|
|
274
|
+
/** Short only: worker/recovery layer that finalized the failed attempt. */
|
|
275
|
+
failure_source?: string;
|
|
276
|
+
/** Short only: paid generation attempt number that failed. */
|
|
277
|
+
failure_attempt?: number;
|
|
268
278
|
/** Editor only: the newest delivered render no longer matches the live project. */
|
|
269
279
|
stale?: boolean;
|
|
270
280
|
/** Editor only: current scene ids that are still generating. */
|
|
@@ -341,6 +351,16 @@ export function normalizeStatus(
|
|
|
341
351
|
video_url: videoUrl,
|
|
342
352
|
error: (d.error_message as string) ?? null,
|
|
343
353
|
};
|
|
354
|
+
if (typeof d.failed_stage === "string")
|
|
355
|
+
status.failed_stage = d.failed_stage;
|
|
356
|
+
if (typeof d.failure_code === "string")
|
|
357
|
+
status.failure_code = d.failure_code;
|
|
358
|
+
if (d.failure_details && typeof d.failure_details === "object")
|
|
359
|
+
status.failure_details = d.failure_details as Record<string, unknown>;
|
|
360
|
+
if (typeof d.failure_source === "string")
|
|
361
|
+
status.failure_source = d.failure_source;
|
|
362
|
+
if (typeof d.failure_attempt === "number")
|
|
363
|
+
status.failure_attempt = d.failure_attempt;
|
|
344
364
|
// Additive free-re-render bookkeeping (2026-07 API): surfaced only when
|
|
345
365
|
// the server actually sends the booleans, so older payloads normalize
|
|
346
366
|
// byte-identically. A failed free re-render does NOT flip stage to
|
|
@@ -437,15 +457,14 @@ export function normalizeStatus(
|
|
|
437
457
|
);
|
|
438
458
|
const batchStatus = (d.batch_generation_status as string) ?? null;
|
|
439
459
|
// The batch enum's ACTIVE states (video_factory.ex): preparing (spinning up),
|
|
440
|
-
// awaiting_previews (previews generating), running
|
|
441
|
-
//
|
|
442
|
-
//
|
|
443
|
-
// idle and could be reported terminal mid-run.
|
|
460
|
+
// awaiting_previews (previews generating), and running. "queued" is not a real
|
|
461
|
+
// value and was dropped. paused_for_retry is deliberately NOT active: no worker
|
|
462
|
+
// owns it until the user explicitly retries or cancels the paused batch.
|
|
444
463
|
const batchActive =
|
|
445
464
|
batchStatus === "running" ||
|
|
446
465
|
batchStatus === "preparing" ||
|
|
447
|
-
batchStatus === "awaiting_previews"
|
|
448
|
-
|
|
466
|
+
batchStatus === "awaiting_previews";
|
|
467
|
+
const batchRetryRequired = batchStatus === "paused_for_retry";
|
|
449
468
|
// A parked batch that ran out of credits: it cannot progress without the user
|
|
450
469
|
// topping up or reducing scope, so it is a needs-user-action TERMINAL state
|
|
451
470
|
// (not active work to wait on).
|
|
@@ -546,14 +565,39 @@ export function normalizeStatus(
|
|
|
546
565
|
currentMusic.status === "processing" ||
|
|
547
566
|
newerAudioVersions.some(versionActive) ||
|
|
548
567
|
newerMusicVersions.some(versionActive);
|
|
549
|
-
const
|
|
550
|
-
autopilotActive ||
|
|
568
|
+
const childWorkActive =
|
|
551
569
|
batchActive ||
|
|
552
570
|
renderActive ||
|
|
553
571
|
scenarioActive ||
|
|
554
572
|
narrationActive ||
|
|
555
573
|
audioActive ||
|
|
556
574
|
activeSegments.length > 0;
|
|
575
|
+
// A resumed Autopilot briefly exposes the previous attempt's failed child
|
|
576
|
+
// states before its new worker reaches and clears them. Suppress that history
|
|
577
|
+
// only while real child work is active or for a short, timestamped hand-off
|
|
578
|
+
// window. A parent stuck "running" with no worker must eventually surface the
|
|
579
|
+
// child failure instead of making wait_for_completion poll forever.
|
|
580
|
+
const autopilotStartedAt =
|
|
581
|
+
typeof d.autopilot_started_at === "string"
|
|
582
|
+
? Date.parse(d.autopilot_started_at)
|
|
583
|
+
: Number.NaN;
|
|
584
|
+
const now = Date.now();
|
|
585
|
+
const autopilotResumeGraceActive =
|
|
586
|
+
autopilotActive &&
|
|
587
|
+
Number.isFinite(autopilotStartedAt) &&
|
|
588
|
+
autopilotStartedAt <= now &&
|
|
589
|
+
now - autopilotStartedAt <= 120_000;
|
|
590
|
+
const autopilotFailed = autopilot === "failed" || autopilot === "cancelled";
|
|
591
|
+
// Retrying a paused batch deliberately leaves the failed parent Autopilot
|
|
592
|
+
// marker in place while the already-paid child batch finishes. In that one
|
|
593
|
+
// state combination the parent/old scene failures are history, not a reason
|
|
594
|
+
// to return terminal immediately. Once the batch settles, the parent failure
|
|
595
|
+
// becomes actionable again and the agent can quote/relaunch Autopilot.
|
|
596
|
+
const batchRecoveryActive = autopilotFailed && batchActive;
|
|
597
|
+
const childFailureActionable =
|
|
598
|
+
!batchRecoveryActive &&
|
|
599
|
+
(!autopilotActive || (!childWorkActive && !autopilotResumeGraceActive));
|
|
600
|
+
const workActive = autopilotActive || childWorkActive;
|
|
557
601
|
// Orchestration failures are historical once a repaired timeline has a fresh,
|
|
558
602
|
// completed MP4. Keeping them terminal would permanently block download_result
|
|
559
603
|
// after a successful granular recovery.
|
|
@@ -566,17 +610,26 @@ export function normalizeStatus(
|
|
|
566
610
|
failedSegments.length === 0 &&
|
|
567
611
|
!audioFailed;
|
|
568
612
|
const ready = deliveredArtifactSupersedesOrchestrationFailure;
|
|
569
|
-
const
|
|
570
|
-
const renderFailed = renderStatus === "failed";
|
|
613
|
+
const parentFailureActionable = autopilotFailed && !batchRecoveryActive;
|
|
614
|
+
const renderFailed = renderStatus === "failed" && childFailureActionable;
|
|
571
615
|
const failed =
|
|
572
|
-
(
|
|
616
|
+
(parentFailureActionable &&
|
|
617
|
+
!deliveredArtifactSupersedesOrchestrationFailure) ||
|
|
573
618
|
renderFailed ||
|
|
574
|
-
(
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
(
|
|
578
|
-
|
|
579
|
-
|
|
619
|
+
(childFailureActionable &&
|
|
620
|
+
scenarioApplyFailed &&
|
|
621
|
+
!deliveredArtifactSupersedesOrchestrationFailure) ||
|
|
622
|
+
(childFailureActionable &&
|
|
623
|
+
scenarioFailed &&
|
|
624
|
+
!deliveredArtifactSupersedesOrchestrationFailure) ||
|
|
625
|
+
(childFailureActionable &&
|
|
626
|
+
narrationFailed &&
|
|
627
|
+
!deliveredArtifactSupersedesOrchestrationFailure) ||
|
|
628
|
+
(childFailureActionable &&
|
|
629
|
+
batchFailed &&
|
|
630
|
+
!deliveredArtifactSupersedesOrchestrationFailure) ||
|
|
631
|
+
(childFailureActionable && failedSegments.length > 0) ||
|
|
632
|
+
(childFailureActionable && audioFailed);
|
|
580
633
|
// Terminal = control should return to the agent because nothing will progress
|
|
581
634
|
// without another tool call. With every async job type folded into workActive
|
|
582
635
|
// above (autopilot, batch, segments, scenario, narration, audio, render), NO
|
|
@@ -585,21 +638,35 @@ export function normalizeStatus(
|
|
|
585
638
|
// a new tool call can move it, so wait_for_completion must return instead of
|
|
586
639
|
// polling its full budget. (ready is subsumed by !workActive but kept for
|
|
587
640
|
// clarity; explicit failure states are terminal even while cleanup work spins.)
|
|
588
|
-
const terminal =
|
|
641
|
+
const terminal =
|
|
642
|
+
ready ||
|
|
643
|
+
failed ||
|
|
644
|
+
batchRetryRequired ||
|
|
645
|
+
batchInsufficientCredits ||
|
|
646
|
+
!workActive;
|
|
589
647
|
let stage = autopilot;
|
|
590
648
|
// Failure always wins over an unrelated active flag. Workers can leave an
|
|
591
649
|
// outer autopilot/batch state active after a child stage has failed; reporting
|
|
592
650
|
// the active stage would make agents poll forever instead of taking action.
|
|
593
651
|
if (ready) stage = "completed";
|
|
594
|
-
|
|
595
|
-
|
|
652
|
+
// A paused child batch is the immediate recovery gate even when its parent
|
|
653
|
+
// Autopilot run has already failed/cancelled. Reporting only the generic
|
|
654
|
+
// parent terminal state hides the one action that must happen before a new
|
|
655
|
+
// Autopilot run is allowed to start.
|
|
656
|
+
else if (batchRetryRequired) stage = "batch_retry_required";
|
|
657
|
+
else if (parentFailureActionable && autopilot === "failed") stage = "failed";
|
|
658
|
+
else if (parentFailureActionable && autopilot === "cancelled")
|
|
659
|
+
stage = "cancelled";
|
|
596
660
|
else if (renderFailed) stage = "render_failed";
|
|
597
|
-
else if (scenarioApplyFailed)
|
|
598
|
-
|
|
599
|
-
else if (
|
|
600
|
-
else if (
|
|
601
|
-
|
|
602
|
-
else if (
|
|
661
|
+
else if (childFailureActionable && scenarioApplyFailed)
|
|
662
|
+
stage = "scenario_apply_failed";
|
|
663
|
+
else if (childFailureActionable && scenarioFailed) stage = "scenario_failed";
|
|
664
|
+
else if (childFailureActionable && narrationFailed)
|
|
665
|
+
stage = "narration_failed";
|
|
666
|
+
else if (childFailureActionable && batchFailed) stage = "batch_failed";
|
|
667
|
+
else if (childFailureActionable && failedSegments.length > 0)
|
|
668
|
+
stage = "segment_failed";
|
|
669
|
+
else if (childFailureActionable && audioFailed) stage = "audio_failed";
|
|
603
670
|
else if (batchInsufficientCredits) stage = "insufficient_credits";
|
|
604
671
|
else if (activeSegments.length > 0) stage = "segment_processing";
|
|
605
672
|
else if (batchActive) stage = "segment_generation";
|
|
@@ -613,6 +680,15 @@ export function normalizeStatus(
|
|
|
613
680
|
.find(
|
|
614
681
|
(value): value is string => typeof value === "string" && value.length > 0,
|
|
615
682
|
);
|
|
683
|
+
const actionableChildError = childFailureActionable
|
|
684
|
+
? ((latest.error_message as string) ??
|
|
685
|
+
(d.scenario_apply_error_message as string) ??
|
|
686
|
+
(d.scenario_error_message as string) ??
|
|
687
|
+
(d.narration_error_message as string) ??
|
|
688
|
+
(d.batch_generation_error_message as string) ??
|
|
689
|
+
segmentError ??
|
|
690
|
+
audioError)
|
|
691
|
+
: null;
|
|
616
692
|
return {
|
|
617
693
|
kind,
|
|
618
694
|
slug,
|
|
@@ -622,17 +698,15 @@ export function normalizeStatus(
|
|
|
622
698
|
video_url: videoUrl,
|
|
623
699
|
error: ready
|
|
624
700
|
? null
|
|
625
|
-
:
|
|
626
|
-
|
|
627
|
-
(
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
? "Batch generation paused: not enough credits to finish. Top up or reduce scope, then resume."
|
|
635
|
-
: null)),
|
|
701
|
+
: batchRetryRequired
|
|
702
|
+
? "Batch generation is paused after a failed scene. Call retry_editor_batch or cancel_editor_batch before continuing."
|
|
703
|
+
: ((parentFailureActionable
|
|
704
|
+
? (d.autopilot_error_message as string)
|
|
705
|
+
: null) ??
|
|
706
|
+
actionableChildError ??
|
|
707
|
+
(batchInsufficientCredits
|
|
708
|
+
? "Batch generation paused: not enough credits to finish. Top up or reduce scope, then resume."
|
|
709
|
+
: null)),
|
|
636
710
|
stale,
|
|
637
711
|
active_segment_ids: activeSegments
|
|
638
712
|
.map((segment) => segment.id)
|