@hubfluencer/mcp 0.10.0 → 0.12.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 +36 -12
- package/dist/index.js +276 -89
- package/package.json +1 -1
- package/src/core.ts +103 -44
- package/src/index.ts +381 -133
- package/src/output-schemas.ts +6 -0
- package/src/uploads.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hubfluencer/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.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
|
@@ -457,15 +457,21 @@ export function normalizeStatus(
|
|
|
457
457
|
);
|
|
458
458
|
const batchStatus = (d.batch_generation_status as string) ?? null;
|
|
459
459
|
// The batch enum's ACTIVE states (video_factory.ex): preparing (spinning up),
|
|
460
|
-
// awaiting_previews (previews generating), running
|
|
461
|
-
//
|
|
462
|
-
//
|
|
463
|
-
// 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.
|
|
464
463
|
const batchActive =
|
|
465
464
|
batchStatus === "running" ||
|
|
466
465
|
batchStatus === "preparing" ||
|
|
467
|
-
batchStatus === "awaiting_previews"
|
|
468
|
-
|
|
466
|
+
batchStatus === "awaiting_previews";
|
|
467
|
+
// The scene batch commits `completed` before the durable auto-render handoff
|
|
468
|
+
// creates its VideoResult row. While the persisted intent remains armed, the
|
|
469
|
+
// server still owns the project and a reconciler may create the render at any
|
|
470
|
+
// moment. Treat that narrow gap as active work so wait_for_completion cannot
|
|
471
|
+
// return an idle/terminal result just before the render appears.
|
|
472
|
+
const autoRenderHandoffActive =
|
|
473
|
+
batchStatus === "completed" && d.auto_render_after_batch === true;
|
|
474
|
+
const batchRetryRequired = batchStatus === "paused_for_retry";
|
|
469
475
|
// A parked batch that ran out of credits: it cannot progress without the user
|
|
470
476
|
// topping up or reducing scope, so it is a needs-user-action TERMINAL state
|
|
471
477
|
// (not active work to wait on).
|
|
@@ -566,14 +572,40 @@ export function normalizeStatus(
|
|
|
566
572
|
currentMusic.status === "processing" ||
|
|
567
573
|
newerAudioVersions.some(versionActive) ||
|
|
568
574
|
newerMusicVersions.some(versionActive);
|
|
569
|
-
const
|
|
570
|
-
autopilotActive ||
|
|
575
|
+
const childWorkActive =
|
|
571
576
|
batchActive ||
|
|
577
|
+
autoRenderHandoffActive ||
|
|
572
578
|
renderActive ||
|
|
573
579
|
scenarioActive ||
|
|
574
580
|
narrationActive ||
|
|
575
581
|
audioActive ||
|
|
576
582
|
activeSegments.length > 0;
|
|
583
|
+
// A resumed Autopilot briefly exposes the previous attempt's failed child
|
|
584
|
+
// states before its new worker reaches and clears them. Suppress that history
|
|
585
|
+
// only while real child work is active or for a short, timestamped hand-off
|
|
586
|
+
// window. A parent stuck "running" with no worker must eventually surface the
|
|
587
|
+
// child failure instead of making wait_for_completion poll forever.
|
|
588
|
+
const autopilotStartedAt =
|
|
589
|
+
typeof d.autopilot_started_at === "string"
|
|
590
|
+
? Date.parse(d.autopilot_started_at)
|
|
591
|
+
: Number.NaN;
|
|
592
|
+
const now = Date.now();
|
|
593
|
+
const autopilotResumeGraceActive =
|
|
594
|
+
autopilotActive &&
|
|
595
|
+
Number.isFinite(autopilotStartedAt) &&
|
|
596
|
+
autopilotStartedAt <= now &&
|
|
597
|
+
now - autopilotStartedAt <= 120_000;
|
|
598
|
+
const autopilotFailed = autopilot === "failed" || autopilot === "cancelled";
|
|
599
|
+
// Retrying a paused batch deliberately leaves the failed parent Autopilot
|
|
600
|
+
// marker in place while the already-paid child batch finishes. In that one
|
|
601
|
+
// state combination the parent/old scene failures are history, not a reason
|
|
602
|
+
// to return terminal immediately. Once the batch settles, the parent failure
|
|
603
|
+
// becomes actionable again and the agent can quote/relaunch Autopilot.
|
|
604
|
+
const batchRecoveryActive = autopilotFailed && batchActive;
|
|
605
|
+
const childFailureActionable =
|
|
606
|
+
!batchRecoveryActive &&
|
|
607
|
+
(!autopilotActive || (!childWorkActive && !autopilotResumeGraceActive));
|
|
608
|
+
const workActive = autopilotActive || childWorkActive;
|
|
577
609
|
// Orchestration failures are historical once a repaired timeline has a fresh,
|
|
578
610
|
// completed MP4. Keeping them terminal would permanently block download_result
|
|
579
611
|
// after a successful granular recovery.
|
|
@@ -586,17 +618,26 @@ export function normalizeStatus(
|
|
|
586
618
|
failedSegments.length === 0 &&
|
|
587
619
|
!audioFailed;
|
|
588
620
|
const ready = deliveredArtifactSupersedesOrchestrationFailure;
|
|
589
|
-
const
|
|
590
|
-
const renderFailed = renderStatus === "failed";
|
|
621
|
+
const parentFailureActionable = autopilotFailed && !batchRecoveryActive;
|
|
622
|
+
const renderFailed = renderStatus === "failed" && childFailureActionable;
|
|
591
623
|
const failed =
|
|
592
|
-
(
|
|
624
|
+
(parentFailureActionable &&
|
|
625
|
+
!deliveredArtifactSupersedesOrchestrationFailure) ||
|
|
593
626
|
renderFailed ||
|
|
594
|
-
(
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
(
|
|
598
|
-
|
|
599
|
-
|
|
627
|
+
(childFailureActionable &&
|
|
628
|
+
scenarioApplyFailed &&
|
|
629
|
+
!deliveredArtifactSupersedesOrchestrationFailure) ||
|
|
630
|
+
(childFailureActionable &&
|
|
631
|
+
scenarioFailed &&
|
|
632
|
+
!deliveredArtifactSupersedesOrchestrationFailure) ||
|
|
633
|
+
(childFailureActionable &&
|
|
634
|
+
narrationFailed &&
|
|
635
|
+
!deliveredArtifactSupersedesOrchestrationFailure) ||
|
|
636
|
+
(childFailureActionable &&
|
|
637
|
+
batchFailed &&
|
|
638
|
+
!deliveredArtifactSupersedesOrchestrationFailure) ||
|
|
639
|
+
(childFailureActionable && failedSegments.length > 0) ||
|
|
640
|
+
(childFailureActionable && audioFailed);
|
|
600
641
|
// Terminal = control should return to the agent because nothing will progress
|
|
601
642
|
// without another tool call. With every async job type folded into workActive
|
|
602
643
|
// above (autopilot, batch, segments, scenario, narration, audio, render), NO
|
|
@@ -605,24 +646,39 @@ export function normalizeStatus(
|
|
|
605
646
|
// a new tool call can move it, so wait_for_completion must return instead of
|
|
606
647
|
// polling its full budget. (ready is subsumed by !workActive but kept for
|
|
607
648
|
// clarity; explicit failure states are terminal even while cleanup work spins.)
|
|
608
|
-
const terminal =
|
|
649
|
+
const terminal =
|
|
650
|
+
ready ||
|
|
651
|
+
failed ||
|
|
652
|
+
batchRetryRequired ||
|
|
653
|
+
batchInsufficientCredits ||
|
|
654
|
+
!workActive;
|
|
609
655
|
let stage = autopilot;
|
|
610
656
|
// Failure always wins over an unrelated active flag. Workers can leave an
|
|
611
657
|
// outer autopilot/batch state active after a child stage has failed; reporting
|
|
612
658
|
// the active stage would make agents poll forever instead of taking action.
|
|
613
659
|
if (ready) stage = "completed";
|
|
614
|
-
|
|
615
|
-
|
|
660
|
+
// A paused child batch is the immediate recovery gate even when its parent
|
|
661
|
+
// Autopilot run has already failed/cancelled. Reporting only the generic
|
|
662
|
+
// parent terminal state hides the one action that must happen before a new
|
|
663
|
+
// Autopilot run is allowed to start.
|
|
664
|
+
else if (batchRetryRequired) stage = "batch_retry_required";
|
|
665
|
+
else if (parentFailureActionable && autopilot === "failed") stage = "failed";
|
|
666
|
+
else if (parentFailureActionable && autopilot === "cancelled")
|
|
667
|
+
stage = "cancelled";
|
|
616
668
|
else if (renderFailed) stage = "render_failed";
|
|
617
|
-
else if (scenarioApplyFailed)
|
|
618
|
-
|
|
619
|
-
else if (
|
|
620
|
-
else if (
|
|
621
|
-
|
|
622
|
-
else if (
|
|
669
|
+
else if (childFailureActionable && scenarioApplyFailed)
|
|
670
|
+
stage = "scenario_apply_failed";
|
|
671
|
+
else if (childFailureActionable && scenarioFailed) stage = "scenario_failed";
|
|
672
|
+
else if (childFailureActionable && narrationFailed)
|
|
673
|
+
stage = "narration_failed";
|
|
674
|
+
else if (childFailureActionable && batchFailed) stage = "batch_failed";
|
|
675
|
+
else if (childFailureActionable && failedSegments.length > 0)
|
|
676
|
+
stage = "segment_failed";
|
|
677
|
+
else if (childFailureActionable && audioFailed) stage = "audio_failed";
|
|
623
678
|
else if (batchInsufficientCredits) stage = "insufficient_credits";
|
|
624
679
|
else if (activeSegments.length > 0) stage = "segment_processing";
|
|
625
680
|
else if (batchActive) stage = "segment_generation";
|
|
681
|
+
else if (autoRenderHandoffActive) stage = "rendering";
|
|
626
682
|
else if (renderActive) stage = "rendering";
|
|
627
683
|
else if (scenarioActive) stage = "scenario_processing";
|
|
628
684
|
else if (narrationActive) stage = "narration_processing";
|
|
@@ -633,6 +689,15 @@ export function normalizeStatus(
|
|
|
633
689
|
.find(
|
|
634
690
|
(value): value is string => typeof value === "string" && value.length > 0,
|
|
635
691
|
);
|
|
692
|
+
const actionableChildError = childFailureActionable
|
|
693
|
+
? ((latest.error_message as string) ??
|
|
694
|
+
(d.scenario_apply_error_message as string) ??
|
|
695
|
+
(d.scenario_error_message as string) ??
|
|
696
|
+
(d.narration_error_message as string) ??
|
|
697
|
+
(d.batch_generation_error_message as string) ??
|
|
698
|
+
segmentError ??
|
|
699
|
+
audioError)
|
|
700
|
+
: null;
|
|
636
701
|
return {
|
|
637
702
|
kind,
|
|
638
703
|
slug,
|
|
@@ -642,17 +707,15 @@ export function normalizeStatus(
|
|
|
642
707
|
video_url: videoUrl,
|
|
643
708
|
error: ready
|
|
644
709
|
? null
|
|
645
|
-
:
|
|
646
|
-
|
|
647
|
-
(
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
? "Batch generation paused: not enough credits to finish. Top up or reduce scope, then resume."
|
|
655
|
-
: null)),
|
|
710
|
+
: batchRetryRequired
|
|
711
|
+
? "Batch generation is paused after a failed scene. Call retry_editor_batch or cancel_editor_batch before continuing."
|
|
712
|
+
: ((parentFailureActionable
|
|
713
|
+
? (d.autopilot_error_message as string)
|
|
714
|
+
: null) ??
|
|
715
|
+
actionableChildError ??
|
|
716
|
+
(batchInsufficientCredits
|
|
717
|
+
? "Batch generation paused: not enough credits to finish. Top up or reduce scope, then invoke the generation/render path again; retry_editor_batch only applies to paused_for_retry."
|
|
718
|
+
: null)),
|
|
656
719
|
stale,
|
|
657
720
|
active_segment_ids: activeSegments
|
|
658
721
|
.map((segment) => segment.id)
|
|
@@ -802,7 +865,6 @@ export interface EditorAdKeyArgs {
|
|
|
802
865
|
logo_treatment?: string;
|
|
803
866
|
logo_position?: string;
|
|
804
867
|
logo_duration_seconds?: number;
|
|
805
|
-
cast_mode?: string;
|
|
806
868
|
/** Identities of the retained, pre-create asset byte snapshots. */
|
|
807
869
|
product_image_identity?: string;
|
|
808
870
|
closing_image_identity?: string;
|
|
@@ -842,7 +904,6 @@ export function editorAdCreateKey(a: EditorAdKeyArgs): string {
|
|
|
842
904
|
a.logo_duration_seconds === undefined
|
|
843
905
|
? ""
|
|
844
906
|
: String(a.logo_duration_seconds),
|
|
845
|
-
a.cast_mode ?? "",
|
|
846
907
|
);
|
|
847
908
|
}
|
|
848
909
|
|
|
@@ -877,7 +938,6 @@ export function editorAdAutopilotKey(slug: string, a: EditorAdKeyArgs): string {
|
|
|
877
938
|
a.logo_duration_seconds === undefined
|
|
878
939
|
? ""
|
|
879
940
|
: String(a.logo_duration_seconds),
|
|
880
|
-
a.cast_mode ?? "",
|
|
881
941
|
a.max_credits === undefined ? "" : String(a.max_credits),
|
|
882
942
|
);
|
|
883
943
|
}
|
|
@@ -1022,10 +1082,9 @@ export function validateLanguage(language: string | undefined): string | null {
|
|
|
1022
1082
|
export function validateProductPrompt(
|
|
1023
1083
|
prompt: string | undefined,
|
|
1024
1084
|
): string | null {
|
|
1025
|
-
//
|
|
1026
|
-
//
|
|
1027
|
-
//
|
|
1028
|
-
// as undefined. Still reject 1–9 non-blank chars (too short to be a brief).
|
|
1085
|
+
// Empty is valid but its meaning is endpoint-specific: draft creation omits
|
|
1086
|
+
// it, while Autopilot treats an explicitly present blank as an atomic clear.
|
|
1087
|
+
// Still reject 1–9 non-blank chars (too short to be a brief).
|
|
1029
1088
|
if (prompt === undefined) return null;
|
|
1030
1089
|
const len = prompt.trim().length;
|
|
1031
1090
|
if (len === 0) return null;
|