@mediaruntime/cli 1.1.0 → 1.3.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/CHANGELOG.md +27 -0
- package/README.md +269 -1
- package/dist/cli.d.ts +125 -1
- package/dist/cli.js +1068 -25
- package/dist/cli.js.map +1 -1
- package/docs/AUTH.md +8 -0
- package/docs/CLI_SDD.md +54 -4
- package/docs/STICKERS.md +98 -0
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
MediaRuntimeApiError,
|
|
21
21
|
MediaRuntimeConnectionError
|
|
22
22
|
} from "@mediaruntime/node";
|
|
23
|
+
import * as MediaRuntimeSdk from "@mediaruntime/node";
|
|
23
24
|
|
|
24
25
|
// src/errors.ts
|
|
25
26
|
var CliError = class extends Error {
|
|
@@ -78,6 +79,7 @@ async function post(baseUrl, path, body, apiKey) {
|
|
|
78
79
|
try {
|
|
79
80
|
response = await fetch(endpoint(baseUrl, path), {
|
|
80
81
|
method: "POST",
|
|
82
|
+
// Never forward a credential or device code through an unexpected redirect.
|
|
81
83
|
redirect: "error",
|
|
82
84
|
headers: {
|
|
83
85
|
"Content-Type": "application/json",
|
|
@@ -467,6 +469,7 @@ function detailsProjection(job) {
|
|
|
467
469
|
retentionDays: job.bundle.retentionDays
|
|
468
470
|
},
|
|
469
471
|
media: job.media,
|
|
472
|
+
recipe: job.recipe,
|
|
470
473
|
metadata: job.metadata,
|
|
471
474
|
error: job.error,
|
|
472
475
|
createdAt: job.createdAt,
|
|
@@ -632,16 +635,38 @@ function parseMetadata(value) {
|
|
|
632
635
|
}
|
|
633
636
|
return parsed;
|
|
634
637
|
}
|
|
638
|
+
function boundedNumber(args, index, option, minimum, maximum, integer = false) {
|
|
639
|
+
const value = Number(optionValue2(args, index, option));
|
|
640
|
+
if (!Number.isFinite(value) || value < minimum || value > maximum || integer && !Number.isInteger(value)) {
|
|
641
|
+
throw new UsageError(`${option} must be ${integer ? "an integer " : ""}between ${minimum} and ${maximum}`);
|
|
642
|
+
}
|
|
643
|
+
return value;
|
|
644
|
+
}
|
|
645
|
+
function hexColor(args, index, option) {
|
|
646
|
+
const value = optionValue2(args, index, option);
|
|
647
|
+
if (!/^#[0-9A-Fa-f]{6}$/.test(value)) {
|
|
648
|
+
throw new UsageError(`${option} must be a six-digit hex colour such as #5B5CFF`);
|
|
649
|
+
}
|
|
650
|
+
return value;
|
|
651
|
+
}
|
|
635
652
|
function parseRunOptions(args) {
|
|
636
653
|
let source;
|
|
637
654
|
const outputs = [];
|
|
638
655
|
let metadata;
|
|
656
|
+
let recipe;
|
|
639
657
|
let idempotencyKey;
|
|
640
658
|
let wait = false;
|
|
641
659
|
let timeoutMs;
|
|
642
660
|
let download;
|
|
643
661
|
let force = false;
|
|
644
662
|
let json = false;
|
|
663
|
+
const animation = {};
|
|
664
|
+
const placeholders = {};
|
|
665
|
+
const contactSheet = {};
|
|
666
|
+
const image = {};
|
|
667
|
+
const audiogram = {};
|
|
668
|
+
const privacyRedaction = {};
|
|
669
|
+
const privacyDetectors = [];
|
|
645
670
|
for (let index = 0; index < args.length; index += 1) {
|
|
646
671
|
const argument = args[index];
|
|
647
672
|
if (argument === "--output" || argument === "-o") {
|
|
@@ -650,6 +675,9 @@ function parseRunOptions(args) {
|
|
|
650
675
|
} else if (argument === "--preset") {
|
|
651
676
|
outputs.push({ kind: "preset", value: optionValue2(args, index, argument) });
|
|
652
677
|
index += 1;
|
|
678
|
+
} else if (argument === "--recipe") {
|
|
679
|
+
recipe = optionValue2(args, index, argument);
|
|
680
|
+
index += 1;
|
|
653
681
|
} else if (argument === "--metadata") {
|
|
654
682
|
metadata = parseMetadata(optionValue2(args, index, argument));
|
|
655
683
|
index += 1;
|
|
@@ -673,6 +701,204 @@ function parseRunOptions(args) {
|
|
|
673
701
|
force = true;
|
|
674
702
|
} else if (argument === "--json") {
|
|
675
703
|
json = true;
|
|
704
|
+
} else if (argument === "--animation-width") {
|
|
705
|
+
animation.width = boundedNumber(args, index, argument, 1, 1920, true);
|
|
706
|
+
index += 1;
|
|
707
|
+
} else if (argument === "--animation-fps") {
|
|
708
|
+
animation.fps = boundedNumber(args, index, argument, 0.01, 30);
|
|
709
|
+
index += 1;
|
|
710
|
+
} else if (argument === "--animation-start") {
|
|
711
|
+
animation.startTime = boundedNumber(args, index, argument, 0, 604800);
|
|
712
|
+
index += 1;
|
|
713
|
+
} else if (argument === "--animation-duration") {
|
|
714
|
+
animation.duration = boundedNumber(args, index, argument, 0.01, 30);
|
|
715
|
+
index += 1;
|
|
716
|
+
} else if (argument === "--animation-loop") {
|
|
717
|
+
animation.loop = boundedNumber(args, index, argument, 0, 65535, true);
|
|
718
|
+
index += 1;
|
|
719
|
+
} else if (argument === "--animation-quality") {
|
|
720
|
+
animation.quality = boundedNumber(args, index, argument, 1, 100, true);
|
|
721
|
+
index += 1;
|
|
722
|
+
} else if (argument === "--placeholder-max-dimension") {
|
|
723
|
+
placeholders.maxDimension = boundedNumber(args, index, argument, 8, 100, true);
|
|
724
|
+
index += 1;
|
|
725
|
+
} else if (argument === "--placeholder-time") {
|
|
726
|
+
placeholders.sourceTimeSec = boundedNumber(args, index, argument, 0, 604800);
|
|
727
|
+
index += 1;
|
|
728
|
+
} else if (argument === "--lqip-quality") {
|
|
729
|
+
placeholders.lqipQuality = boundedNumber(args, index, argument, 1, 100, true);
|
|
730
|
+
index += 1;
|
|
731
|
+
} else if (argument === "--lqip-max-bytes") {
|
|
732
|
+
placeholders.lqipMaxBytes = boundedNumber(args, index, argument, 256, 16384, true);
|
|
733
|
+
index += 1;
|
|
734
|
+
} else if (argument === "--contact-columns") {
|
|
735
|
+
contactSheet.columns = boundedNumber(args, index, argument, 1, 10, true);
|
|
736
|
+
index += 1;
|
|
737
|
+
} else if (argument === "--contact-rows") {
|
|
738
|
+
contactSheet.rows = boundedNumber(args, index, argument, 1, 10, true);
|
|
739
|
+
index += 1;
|
|
740
|
+
} else if (argument === "--contact-tile-width") {
|
|
741
|
+
contactSheet.tileWidth = boundedNumber(args, index, argument, 64, 640, true);
|
|
742
|
+
index += 1;
|
|
743
|
+
} else if (argument === "--contact-tile-height") {
|
|
744
|
+
contactSheet.tileHeight = boundedNumber(args, index, argument, 36, 640, true);
|
|
745
|
+
index += 1;
|
|
746
|
+
} else if (argument === "--contact-interval") {
|
|
747
|
+
contactSheet.intervalSec = boundedNumber(args, index, argument, 0.5, 3600);
|
|
748
|
+
index += 1;
|
|
749
|
+
} else if (argument === "--contact-start") {
|
|
750
|
+
contactSheet.startTimeSec = boundedNumber(args, index, argument, 0, 604800);
|
|
751
|
+
index += 1;
|
|
752
|
+
} else if (argument === "--contact-duration") {
|
|
753
|
+
contactSheet.durationSec = boundedNumber(args, index, argument, 0, 21600);
|
|
754
|
+
index += 1;
|
|
755
|
+
} else if (argument === "--contact-max-sheets") {
|
|
756
|
+
contactSheet.maxSheets = boundedNumber(args, index, argument, 1, 20, true);
|
|
757
|
+
index += 1;
|
|
758
|
+
} else if (argument === "--contact-format") {
|
|
759
|
+
const value = optionValue2(args, index, argument);
|
|
760
|
+
if (value !== "jpg" && value !== "png" && value !== "webp") {
|
|
761
|
+
throw new UsageError("--contact-format must be jpg, png, or webp");
|
|
762
|
+
}
|
|
763
|
+
contactSheet.format = value;
|
|
764
|
+
index += 1;
|
|
765
|
+
} else if (argument === "--contact-quality") {
|
|
766
|
+
contactSheet.quality = boundedNumber(args, index, argument, 1, 100, true);
|
|
767
|
+
index += 1;
|
|
768
|
+
} else if (argument === "--image-width") {
|
|
769
|
+
image.width = boundedNumber(args, index, argument, 1, 16384, true);
|
|
770
|
+
index += 1;
|
|
771
|
+
} else if (argument === "--image-height") {
|
|
772
|
+
image.height = boundedNumber(args, index, argument, 1, 16384, true);
|
|
773
|
+
index += 1;
|
|
774
|
+
} else if (argument === "--image-mode") {
|
|
775
|
+
const value = optionValue2(args, index, argument);
|
|
776
|
+
if (value !== "fit" && value !== "fill" && value !== "cover" && value !== "contain") {
|
|
777
|
+
throw new UsageError("--image-mode must be fit, fill, cover, or contain");
|
|
778
|
+
}
|
|
779
|
+
image.mode = value;
|
|
780
|
+
index += 1;
|
|
781
|
+
} else if (argument === "--image-format") {
|
|
782
|
+
const value = optionValue2(args, index, argument);
|
|
783
|
+
if (value !== "jpg" && value !== "png" && value !== "webp" && value !== "avif") {
|
|
784
|
+
throw new UsageError("--image-format must be jpg, png, webp, or avif");
|
|
785
|
+
}
|
|
786
|
+
image.format = value;
|
|
787
|
+
index += 1;
|
|
788
|
+
} else if (argument === "--image-quality") {
|
|
789
|
+
image.quality = boundedNumber(args, index, argument, 1, 100, true);
|
|
790
|
+
index += 1;
|
|
791
|
+
} else if (argument === "--image-max-bytes") {
|
|
792
|
+
image.maxBytes = boundedNumber(args, index, argument, 256, 1e8, true);
|
|
793
|
+
index += 1;
|
|
794
|
+
} else if (argument === "--image-min-quality") {
|
|
795
|
+
image.minQuality = boundedNumber(args, index, argument, 1, 100, true);
|
|
796
|
+
index += 1;
|
|
797
|
+
} else if (argument === "--audiogram-artwork") {
|
|
798
|
+
audiogram.artworkSource = optionValue2(args, index, argument);
|
|
799
|
+
index += 1;
|
|
800
|
+
} else if (argument === "--audiogram-captions") {
|
|
801
|
+
audiogram.captionsSource = optionValue2(args, index, argument);
|
|
802
|
+
audiogram.burnCaptions = true;
|
|
803
|
+
index += 1;
|
|
804
|
+
} else if (argument === "--audiogram-layout") {
|
|
805
|
+
const value = optionValue2(args, index, argument);
|
|
806
|
+
if (value !== "square" && value !== "portrait" && value !== "landscape") {
|
|
807
|
+
throw new UsageError("--audiogram-layout must be square, portrait, or landscape");
|
|
808
|
+
}
|
|
809
|
+
audiogram.layout = value;
|
|
810
|
+
index += 1;
|
|
811
|
+
} else if (argument === "--audiogram-background") {
|
|
812
|
+
audiogram.backgroundColor = hexColor(args, index, argument);
|
|
813
|
+
index += 1;
|
|
814
|
+
} else if (argument === "--audiogram-waveform") {
|
|
815
|
+
audiogram.waveformColor = hexColor(args, index, argument);
|
|
816
|
+
index += 1;
|
|
817
|
+
} else if (argument === "--audiogram-fit") {
|
|
818
|
+
const value = optionValue2(args, index, argument);
|
|
819
|
+
if (value !== "contain" && value !== "cover" && value !== "blurred_background") {
|
|
820
|
+
throw new UsageError("--audiogram-fit must be contain, cover, or blurred_background");
|
|
821
|
+
}
|
|
822
|
+
audiogram.artworkFit = value;
|
|
823
|
+
index += 1;
|
|
824
|
+
} else if (argument === "--audiogram-waveform-gain") {
|
|
825
|
+
audiogram.waveformGain = boundedNumber(args, index, argument, 0.5, 4);
|
|
826
|
+
index += 1;
|
|
827
|
+
} else if (argument === "--audiogram-caption-position") {
|
|
828
|
+
const value = optionValue2(args, index, argument);
|
|
829
|
+
if (value !== "top" && value !== "bottom") {
|
|
830
|
+
throw new UsageError("--audiogram-caption-position must be top or bottom");
|
|
831
|
+
}
|
|
832
|
+
audiogram.captionPosition = value;
|
|
833
|
+
index += 1;
|
|
834
|
+
} else if (argument === "--audiogram-caption-scale") {
|
|
835
|
+
audiogram.captionFontScale = boundedNumber(args, index, argument, 0.75, 1.5);
|
|
836
|
+
index += 1;
|
|
837
|
+
} else if (argument === "--audiogram-normalize") {
|
|
838
|
+
audiogram.normalizeAudio = true;
|
|
839
|
+
} else if (argument === "--audiogram-loudness-target") {
|
|
840
|
+
audiogram.loudnessTargetLufs = boundedNumber(args, index, argument, -24, -12);
|
|
841
|
+
index += 1;
|
|
842
|
+
} else if (argument === "--audiogram-start") {
|
|
843
|
+
audiogram.startTimeSec = boundedNumber(args, index, argument, 0, 604800);
|
|
844
|
+
index += 1;
|
|
845
|
+
} else if (argument === "--audiogram-duration") {
|
|
846
|
+
audiogram.durationSec = boundedNumber(args, index, argument, 0.1, 300);
|
|
847
|
+
index += 1;
|
|
848
|
+
} else if (argument === "--audiogram-fps") {
|
|
849
|
+
audiogram.fps = boundedNumber(args, index, argument, 15, 30, true);
|
|
850
|
+
index += 1;
|
|
851
|
+
} else if (argument === "--privacy-detector") {
|
|
852
|
+
const value = optionValue2(args, index, argument);
|
|
853
|
+
if (value !== "face" && value !== "license_plate" && value !== "text") {
|
|
854
|
+
throw new UsageError("--privacy-detector must be face, license_plate, or text");
|
|
855
|
+
}
|
|
856
|
+
if (privacyDetectors.includes(value)) {
|
|
857
|
+
throw new UsageError(`Duplicate --privacy-detector: ${value}`);
|
|
858
|
+
}
|
|
859
|
+
privacyDetectors.push(value);
|
|
860
|
+
index += 1;
|
|
861
|
+
} else if (argument === "--privacy-style") {
|
|
862
|
+
const value = optionValue2(args, index, argument);
|
|
863
|
+
if (value !== "blur" && value !== "pixelate" && value !== "solid") {
|
|
864
|
+
throw new UsageError("--privacy-style must be blur, pixelate, or solid");
|
|
865
|
+
}
|
|
866
|
+
privacyRedaction.style = value;
|
|
867
|
+
index += 1;
|
|
868
|
+
} else if (argument === "--privacy-failure-mode") {
|
|
869
|
+
const value = optionValue2(args, index, argument);
|
|
870
|
+
if (value !== "fail_closed" && value !== "report_only") {
|
|
871
|
+
throw new UsageError("--privacy-failure-mode must be fail_closed or report_only");
|
|
872
|
+
}
|
|
873
|
+
privacyRedaction.failureMode = value;
|
|
874
|
+
index += 1;
|
|
875
|
+
} else if (argument === "--privacy-min-confidence") {
|
|
876
|
+
privacyRedaction.minConfidence = boundedNumber(args, index, argument, 0.3, 0.99);
|
|
877
|
+
index += 1;
|
|
878
|
+
} else if (argument === "--privacy-sample-interval") {
|
|
879
|
+
privacyRedaction.sampleIntervalSec = boundedNumber(args, index, argument, 0.1, 30);
|
|
880
|
+
index += 1;
|
|
881
|
+
} else if (argument === "--privacy-max-frames") {
|
|
882
|
+
privacyRedaction.maxFrames = boundedNumber(args, index, argument, 1, 18e3, true);
|
|
883
|
+
index += 1;
|
|
884
|
+
} else if (argument === "--privacy-padding") {
|
|
885
|
+
privacyRedaction.boxPaddingRatio = boundedNumber(args, index, argument, 0, 0.5);
|
|
886
|
+
index += 1;
|
|
887
|
+
} else if (argument === "--privacy-solid-color") {
|
|
888
|
+
privacyRedaction.solidColor = hexColor(args, index, argument);
|
|
889
|
+
index += 1;
|
|
890
|
+
} else if (argument === "--privacy-pixel-block-size") {
|
|
891
|
+
privacyRedaction.pixelBlockSize = boundedNumber(args, index, argument, 4, 128, true);
|
|
892
|
+
index += 1;
|
|
893
|
+
} else if (argument === "--privacy-strength") {
|
|
894
|
+
const value = optionValue2(args, index, argument);
|
|
895
|
+
if (value !== "standard" && value !== "strong") {
|
|
896
|
+
throw new UsageError("--privacy-strength must be standard or strong");
|
|
897
|
+
}
|
|
898
|
+
privacyRedaction.privacyStrength = value;
|
|
899
|
+
index += 1;
|
|
900
|
+
} else if (argument === "--privacy-debug-observations") {
|
|
901
|
+
privacyRedaction.includeDebugObservations = true;
|
|
676
902
|
} else if (argument?.startsWith("-")) {
|
|
677
903
|
throw new UsageError(`Unknown run option: ${argument}`);
|
|
678
904
|
} else if (source === void 0) {
|
|
@@ -682,40 +908,183 @@ function parseRunOptions(args) {
|
|
|
682
908
|
}
|
|
683
909
|
}
|
|
684
910
|
if (!source) {
|
|
685
|
-
throw new UsageError("Usage: mediaruntime run <source> (--output <alias> | --preset <name>) [--wait]");
|
|
911
|
+
throw new UsageError("Usage: mediaruntime run <source> (--recipe <name[@version]> | --output <alias> | --preset <name>) [--wait]");
|
|
686
912
|
}
|
|
687
|
-
if (outputs.length
|
|
913
|
+
if (recipe && outputs.length > 0) throw new UsageError("--recipe cannot be combined with --output or --preset");
|
|
914
|
+
if (!recipe && outputs.length === 0) throw new UsageError("run requires --recipe or at least one --output or --preset");
|
|
688
915
|
const unsupported = outputs.find(
|
|
689
|
-
(
|
|
916
|
+
(output3) => output3.kind === "alias" && !OUTPUT_ALIASES.has(output3.value)
|
|
690
917
|
);
|
|
691
918
|
if (unsupported) throw new UsageError(`Unsupported output alias: ${unsupported.value}`);
|
|
692
919
|
if (force && !download) throw new UsageError("--force requires --download");
|
|
693
920
|
if (timeoutMs !== void 0 && !wait) throw new UsageError("--timeout-ms requires --wait or --download");
|
|
921
|
+
const hasAnimationOptions = Object.keys(animation).length > 0;
|
|
922
|
+
if (hasAnimationOptions) {
|
|
923
|
+
const animationSelections = outputs.filter(
|
|
924
|
+
(output3) => output3.kind === "preset" && (output3.value === "image_animated_webp_v1" || output3.value === "image_animated_apng_v1")
|
|
925
|
+
);
|
|
926
|
+
if (recipe || outputs.length !== 1 || animationSelections.length !== 1) {
|
|
927
|
+
throw new UsageError("animation options require exactly one animated WebP or APNG --preset");
|
|
928
|
+
}
|
|
929
|
+
if (animation.quality !== void 0 && animationSelections[0]?.value === "image_animated_apng_v1") {
|
|
930
|
+
throw new UsageError("--animation-quality applies to animated WebP only; APNG is lossless");
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
const hasPlaceholderOptions = Object.keys(placeholders).length > 0;
|
|
934
|
+
if (hasPlaceholderOptions) {
|
|
935
|
+
const placeholderSelections = outputs.filter(
|
|
936
|
+
(output3) => output3.kind === "preset" && output3.value === "image_placeholders_v1"
|
|
937
|
+
);
|
|
938
|
+
if (recipe || outputs.length !== 1 || placeholderSelections.length !== 1) {
|
|
939
|
+
throw new UsageError("placeholder options require exactly one image_placeholders_v1 --preset");
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
const hasContactSheetOptions = Object.keys(contactSheet).length > 0;
|
|
943
|
+
if (hasContactSheetOptions) {
|
|
944
|
+
const selections = outputs.filter(
|
|
945
|
+
(output3) => output3.kind === "preset" && output3.value === "contact_sheet_v1"
|
|
946
|
+
);
|
|
947
|
+
if (recipe || outputs.length !== 1 || selections.length !== 1) {
|
|
948
|
+
throw new UsageError("contact-sheet options require exactly one contact_sheet_v1 --preset");
|
|
949
|
+
}
|
|
950
|
+
const geometry = {
|
|
951
|
+
columns: 4,
|
|
952
|
+
rows: 4,
|
|
953
|
+
tileWidth: 320,
|
|
954
|
+
tileHeight: 180,
|
|
955
|
+
...contactSheet
|
|
956
|
+
};
|
|
957
|
+
if (geometry.columns * geometry.tileWidth > 4096 || geometry.rows * geometry.tileHeight > 4096) {
|
|
958
|
+
throw new UsageError("contact-sheet width and height must each be at most 4096 pixels");
|
|
959
|
+
}
|
|
960
|
+
if (contactSheet.format === "png" && contactSheet.quality !== void 0) {
|
|
961
|
+
throw new UsageError("--contact-quality applies to JPG and WebP only; PNG is lossless");
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
const hasImageOptions = Object.keys(image).length > 0;
|
|
965
|
+
if (hasImageOptions) {
|
|
966
|
+
const selections = outputs.filter(
|
|
967
|
+
(output3) => output3.kind === "preset" && output3.value === "image_multi_v1"
|
|
968
|
+
);
|
|
969
|
+
if (recipe || outputs.length !== 1 || selections.length !== 1) {
|
|
970
|
+
throw new UsageError("image rendition options require exactly one image_multi_v1 --preset");
|
|
971
|
+
}
|
|
972
|
+
if (image.minQuality !== void 0 && image.maxBytes === void 0) {
|
|
973
|
+
throw new UsageError("--image-min-quality requires --image-max-bytes");
|
|
974
|
+
}
|
|
975
|
+
const quality = image.quality ?? 80;
|
|
976
|
+
if ((image.minQuality ?? 1) > quality) {
|
|
977
|
+
throw new UsageError("--image-min-quality must not exceed --image-quality");
|
|
978
|
+
}
|
|
979
|
+
const format = image.format ?? "webp";
|
|
980
|
+
if (image.maxBytes !== void 0 && format !== "jpg" && format !== "webp") {
|
|
981
|
+
throw new UsageError("--image-max-bytes currently supports --image-format jpg or webp only");
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
const audiogramSelections = outputs.filter(
|
|
985
|
+
(output3) => output3.kind === "preset" && output3.value === "audiogram_v1"
|
|
986
|
+
);
|
|
987
|
+
const hasAudiogramOptions = Object.keys(audiogram).length > 0;
|
|
988
|
+
if (hasAudiogramOptions || audiogramSelections.length > 0) {
|
|
989
|
+
if (recipe || outputs.length !== 1 || audiogramSelections.length !== 1) {
|
|
990
|
+
throw new UsageError("audiogram options require exactly one audiogram_v1 --preset");
|
|
991
|
+
}
|
|
992
|
+
if (!audiogram.artworkSource) {
|
|
993
|
+
throw new UsageError("audiogram_v1 requires --audiogram-artwork <PNG|JPEG|WebP source>");
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
const hasPrivacyOptions = privacyDetectors.length > 0 || Object.keys(privacyRedaction).length > 0;
|
|
997
|
+
if (hasPrivacyOptions) {
|
|
998
|
+
if (recipe) throw new UsageError("privacy-redaction options cannot be combined with --recipe");
|
|
999
|
+
if (privacyDetectors.length === 0) {
|
|
1000
|
+
throw new UsageError("privacy redaction requires at least one --privacy-detector");
|
|
1001
|
+
}
|
|
1002
|
+
if (/\.(?:mp4|mov|mkv|webm|gif|apng)(?:$|[?#])/i.test(source)) {
|
|
1003
|
+
throw new UsageError(
|
|
1004
|
+
"privacy redaction Preview accepts still-image inputs only; video and animated images are not available"
|
|
1005
|
+
);
|
|
1006
|
+
}
|
|
1007
|
+
privacyRedaction.detectors = privacyDetectors;
|
|
1008
|
+
}
|
|
694
1009
|
return {
|
|
695
1010
|
source,
|
|
696
1011
|
outputs,
|
|
1012
|
+
...recipe === void 0 ? {} : { recipe },
|
|
697
1013
|
...metadata === void 0 ? {} : { metadata },
|
|
698
1014
|
...idempotencyKey === void 0 ? {} : { idempotencyKey },
|
|
699
1015
|
wait,
|
|
700
1016
|
...timeoutMs === void 0 ? {} : { timeoutMs },
|
|
701
1017
|
...download === void 0 ? {} : { download },
|
|
702
1018
|
force,
|
|
703
|
-
json
|
|
1019
|
+
json,
|
|
1020
|
+
...hasAnimationOptions ? { animation } : {},
|
|
1021
|
+
...hasPlaceholderOptions ? { placeholders } : {},
|
|
1022
|
+
...hasContactSheetOptions ? { contactSheet } : {},
|
|
1023
|
+
...hasImageOptions ? { image } : {},
|
|
1024
|
+
...hasAudiogramOptions || audiogramSelections.length > 0 ? { audiogram } : {},
|
|
1025
|
+
...hasPrivacyOptions ? { privacyRedaction } : {}
|
|
704
1026
|
};
|
|
705
1027
|
}
|
|
706
|
-
|
|
707
|
-
|
|
1028
|
+
function isHostedAsset(source) {
|
|
1029
|
+
return /^(?:https?|gs):\/\//i.test(source);
|
|
1030
|
+
}
|
|
1031
|
+
async function resolveAudiogramAsset(source, label, uploads) {
|
|
1032
|
+
if (isHostedAsset(source)) return source;
|
|
1033
|
+
if (/^[a-z][a-z0-9+.-]*:/i.test(source) && !/^file:\/\//i.test(source)) {
|
|
1034
|
+
throw new UsageError(`Audiogram ${label} must be an HTTP(S) URL, gs:// URI, or local file path`);
|
|
1035
|
+
}
|
|
1036
|
+
if (!uploads) {
|
|
1037
|
+
throw new UsageError(`This client cannot upload local Audiogram ${label}; use an HTTP(S) URL or gs:// URI`);
|
|
1038
|
+
}
|
|
1039
|
+
return await uploads.resolveSource(source);
|
|
1040
|
+
}
|
|
1041
|
+
async function resolveAudiogram(value, uploads) {
|
|
1042
|
+
const artworkSource = await resolveAudiogramAsset(value.artworkSource, "artwork", uploads);
|
|
1043
|
+
const captionsSource = value.captionsSource === void 0 ? void 0 : await resolveAudiogramAsset(value.captionsSource, "captions", uploads);
|
|
1044
|
+
return {
|
|
1045
|
+
...value,
|
|
1046
|
+
artworkSource,
|
|
1047
|
+
...captionsSource === void 0 ? {} : { captionsSource, burnCaptions: true }
|
|
1048
|
+
};
|
|
1049
|
+
}
|
|
1050
|
+
async function resolveOutputs(selections, capabilitiesClient, animation, placeholders, contactSheet, image, audiogram, privacyRedaction) {
|
|
1051
|
+
if (!privacyRedaction && !selections.some((selection) => selection.kind === "preset")) {
|
|
708
1052
|
return selections.map((selection) => selection.value);
|
|
709
1053
|
}
|
|
710
1054
|
const capabilities = await capabilitiesClient.retrieve();
|
|
711
1055
|
const publicPresets = new Set(capabilities.publicPresets);
|
|
712
1056
|
return selections.map((selection) => {
|
|
713
|
-
if (selection.kind === "alias")
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
1057
|
+
if (selection.kind === "alias" && !privacyRedaction) {
|
|
1058
|
+
return selection.value;
|
|
1059
|
+
}
|
|
1060
|
+
const selectedName = selection.kind === "alias" ? capabilities.outputAliases[selection.value]?.preset : selection.value;
|
|
1061
|
+
const preset = selectedName ? capabilities.presets[selectedName] : void 0;
|
|
1062
|
+
if (!selectedName || !publicPresets.has(selectedName) || !preset) {
|
|
1063
|
+
throw new UsageError(selection.kind === "alias" ? `Gateway did not provide a public expansion for alias: ${selection.value}` : `Unknown or non-public preset: ${selection.value}`);
|
|
1064
|
+
}
|
|
1065
|
+
if (privacyRedaction && preset.outputType !== "image") {
|
|
1066
|
+
throw new UsageError(
|
|
1067
|
+
"privacy redaction Preview can be attached to image outputs only"
|
|
1068
|
+
);
|
|
717
1069
|
}
|
|
718
|
-
|
|
1070
|
+
const output3 = { type: preset.outputType, preset: selectedName };
|
|
1071
|
+
if (animation) output3.animation = animation;
|
|
1072
|
+
if (placeholders) output3.placeholders = placeholders;
|
|
1073
|
+
if (contactSheet) output3.contactSheet = contactSheet;
|
|
1074
|
+
if (image) {
|
|
1075
|
+
output3.images = [{
|
|
1076
|
+
width: image.width ?? 1280,
|
|
1077
|
+
height: image.height ?? 720,
|
|
1078
|
+
mode: image.mode ?? "fit",
|
|
1079
|
+
format: image.format ?? "webp",
|
|
1080
|
+
quality: image.quality ?? 80,
|
|
1081
|
+
...image.maxBytes === void 0 ? {} : { maxBytes: image.maxBytes },
|
|
1082
|
+
...image.maxBytes === void 0 ? {} : { minQuality: image.minQuality ?? 1 }
|
|
1083
|
+
}];
|
|
1084
|
+
}
|
|
1085
|
+
if (audiogram) output3.audiogram = audiogram;
|
|
1086
|
+
if (privacyRedaction) output3.privacyRedaction = privacyRedaction;
|
|
1087
|
+
return output3;
|
|
719
1088
|
});
|
|
720
1089
|
}
|
|
721
1090
|
function receiptProjection(job) {
|
|
@@ -725,6 +1094,7 @@ function receiptProjection(job) {
|
|
|
725
1094
|
tier: job.tier,
|
|
726
1095
|
requiredTier: job.requiredTier,
|
|
727
1096
|
outputs: job.outputs,
|
|
1097
|
+
recipe: job.recipe,
|
|
728
1098
|
message: job.message
|
|
729
1099
|
};
|
|
730
1100
|
}
|
|
@@ -743,6 +1113,7 @@ function detailsProjection2(job) {
|
|
|
743
1113
|
retentionDays: job.bundle.retentionDays
|
|
744
1114
|
},
|
|
745
1115
|
media: job.media,
|
|
1116
|
+
recipe: job.recipe,
|
|
746
1117
|
metadata: job.metadata,
|
|
747
1118
|
error: job.error,
|
|
748
1119
|
createdAt: job.createdAt,
|
|
@@ -769,18 +1140,30 @@ Bundle: ${job.bundle.available ? "available" : "unavailable"}${error}
|
|
|
769
1140
|
}
|
|
770
1141
|
async function runCommand(args, dependencies) {
|
|
771
1142
|
const options = parseRunOptions(args);
|
|
772
|
-
const
|
|
773
|
-
const
|
|
774
|
-
source: options.source,
|
|
775
|
-
outputs,
|
|
776
|
-
...options.metadata === void 0 ? {} : { metadata: options.metadata },
|
|
777
|
-
...options.idempotencyKey === void 0 ? {} : { idempotencyKey: options.idempotencyKey }
|
|
778
|
-
};
|
|
779
|
-
const isLocalSource = !/^(?:https?|gs):\/\//i.test(options.source);
|
|
1143
|
+
const hasLocalAudiogramAsset = options.audiogram !== void 0 && (!isHostedAsset(options.audiogram.artworkSource) || options.audiogram.captionsSource !== void 0 && !isHostedAsset(options.audiogram.captionsSource));
|
|
1144
|
+
const isLocalSource = !isHostedAsset(options.source);
|
|
780
1145
|
dependencies.activity.start(
|
|
781
|
-
isLocalSource ? "Uploading local
|
|
1146
|
+
isLocalSource || hasLocalAudiogramAsset ? "Uploading local media and creating job\u2026" : "Creating job\u2026"
|
|
782
1147
|
);
|
|
783
1148
|
try {
|
|
1149
|
+
const audiogram = options.audiogram === void 0 ? void 0 : await resolveAudiogram(options.audiogram, dependencies.uploads);
|
|
1150
|
+
const outputs = options.recipe ? void 0 : await resolveOutputs(
|
|
1151
|
+
options.outputs,
|
|
1152
|
+
dependencies.capabilities,
|
|
1153
|
+
options.animation,
|
|
1154
|
+
options.placeholders,
|
|
1155
|
+
options.contactSheet,
|
|
1156
|
+
options.image,
|
|
1157
|
+
audiogram,
|
|
1158
|
+
options.privacyRedaction
|
|
1159
|
+
);
|
|
1160
|
+
const params = {
|
|
1161
|
+
source: options.source,
|
|
1162
|
+
...outputs === void 0 ? {} : { outputs },
|
|
1163
|
+
...options.recipe === void 0 ? {} : { recipe: options.recipe },
|
|
1164
|
+
...options.metadata === void 0 ? {} : { metadata: options.metadata },
|
|
1165
|
+
...options.idempotencyKey === void 0 ? {} : { idempotencyKey: options.idempotencyKey }
|
|
1166
|
+
};
|
|
784
1167
|
const submitted = await dependencies.jobs.create(params);
|
|
785
1168
|
if (!options.wait) {
|
|
786
1169
|
dependencies.activity.stop();
|
|
@@ -831,9 +1214,592 @@ async function runCommand(args, dependencies) {
|
|
|
831
1214
|
}
|
|
832
1215
|
}
|
|
833
1216
|
|
|
1217
|
+
// src/commands/recipes.ts
|
|
1218
|
+
import { readFile } from "fs/promises";
|
|
1219
|
+
function optionValue3(args, index, option) {
|
|
1220
|
+
const value = args[index + 1];
|
|
1221
|
+
if (!value || value.startsWith("--")) throw new UsageError(`${option} requires a value`);
|
|
1222
|
+
return value;
|
|
1223
|
+
}
|
|
1224
|
+
function positiveInteger(value, option) {
|
|
1225
|
+
const parsed = Number(value);
|
|
1226
|
+
if (!Number.isSafeInteger(parsed) || parsed < 1) {
|
|
1227
|
+
throw new UsageError(`${option} must be a positive integer`);
|
|
1228
|
+
}
|
|
1229
|
+
return parsed;
|
|
1230
|
+
}
|
|
1231
|
+
async function recipeFile(path) {
|
|
1232
|
+
let raw;
|
|
1233
|
+
try {
|
|
1234
|
+
raw = await readFile(path, "utf8");
|
|
1235
|
+
} catch (error) {
|
|
1236
|
+
throw new UsageError(`Could not read recipe file: ${path}`, { cause: error });
|
|
1237
|
+
}
|
|
1238
|
+
try {
|
|
1239
|
+
const parsed = JSON.parse(raw);
|
|
1240
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error();
|
|
1241
|
+
return parsed;
|
|
1242
|
+
} catch {
|
|
1243
|
+
throw new UsageError("Recipe file must contain one JSON object");
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
function humanRecipe(recipe) {
|
|
1247
|
+
const kind = recipe.builtIn ? "built-in" : "custom";
|
|
1248
|
+
return `${recipe.reference}
|
|
1249
|
+
${recipe.description || "No description"}
|
|
1250
|
+
Type: ${kind}
|
|
1251
|
+
Status: ${recipe.status}
|
|
1252
|
+
`;
|
|
1253
|
+
}
|
|
1254
|
+
function humanList2(recipes) {
|
|
1255
|
+
const headings = ["REFERENCE", "TYPE", "DESCRIPTION"];
|
|
1256
|
+
const rows = recipes.map((recipe) => [
|
|
1257
|
+
recipe.reference,
|
|
1258
|
+
recipe.builtIn ? "built-in" : "custom",
|
|
1259
|
+
recipe.description || "-"
|
|
1260
|
+
]);
|
|
1261
|
+
const widths = headings.map(
|
|
1262
|
+
(heading, index) => Math.max(heading.length, ...rows.map((row) => row[index]?.length ?? 0))
|
|
1263
|
+
);
|
|
1264
|
+
const render = (row) => row.map((value, index) => value.padEnd(widths[index] ?? value.length)).join(" ").trimEnd();
|
|
1265
|
+
return `${[render(headings), render(widths.map((width) => "-".repeat(width))), ...rows.map(render)].join("\n")}
|
|
1266
|
+
`;
|
|
1267
|
+
}
|
|
1268
|
+
function output(recipe, json, write) {
|
|
1269
|
+
write(json ? `${JSON.stringify(recipe)}
|
|
1270
|
+
` : humanRecipe(recipe));
|
|
1271
|
+
}
|
|
1272
|
+
async function runRecipesCommand(args, dependencies) {
|
|
1273
|
+
const subcommand = args[0];
|
|
1274
|
+
const rest = args.slice(1);
|
|
1275
|
+
const json = rest.includes("--json");
|
|
1276
|
+
if (subcommand === "list") {
|
|
1277
|
+
if (rest.some((value) => value !== "--json")) throw new UsageError("Usage: mediaruntime recipes list [--json]");
|
|
1278
|
+
const recipes = await dependencies.recipes.list();
|
|
1279
|
+
dependencies.writeStdout(json ? `${JSON.stringify({ recipes })}
|
|
1280
|
+
` : humanList2(recipes));
|
|
1281
|
+
return 0;
|
|
1282
|
+
}
|
|
1283
|
+
if (subcommand === "get") {
|
|
1284
|
+
let name;
|
|
1285
|
+
let version;
|
|
1286
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
1287
|
+
const value = rest[index];
|
|
1288
|
+
if (value === "--version") {
|
|
1289
|
+
version = positiveInteger(optionValue3(rest, index, value), value);
|
|
1290
|
+
index += 1;
|
|
1291
|
+
} else if (value !== "--json" && !name) name = value;
|
|
1292
|
+
else if (value !== "--json") throw new UsageError("recipes get accepts one name");
|
|
1293
|
+
}
|
|
1294
|
+
if (!name) throw new UsageError("Usage: mediaruntime recipes get <name> [--version <n>]");
|
|
1295
|
+
output(await dependencies.recipes.get(name, version ? { version } : {}), json, dependencies.writeStdout);
|
|
1296
|
+
return 0;
|
|
1297
|
+
}
|
|
1298
|
+
if (subcommand === "create") {
|
|
1299
|
+
let file;
|
|
1300
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
1301
|
+
if (rest[index] === "--file") {
|
|
1302
|
+
file = optionValue3(rest, index, "--file");
|
|
1303
|
+
index += 1;
|
|
1304
|
+
} else if (rest[index] !== "--json") throw new UsageError(`Unknown recipes create option: ${rest[index]}`);
|
|
1305
|
+
}
|
|
1306
|
+
if (!file) throw new UsageError("Usage: mediaruntime recipes create --file <recipe.json>");
|
|
1307
|
+
const value = await recipeFile(file);
|
|
1308
|
+
if (typeof value.name !== "string" || typeof value.template !== "object") {
|
|
1309
|
+
throw new UsageError("Recipe JSON requires name and template");
|
|
1310
|
+
}
|
|
1311
|
+
output(await dependencies.recipes.create(value), json, dependencies.writeStdout);
|
|
1312
|
+
return 0;
|
|
1313
|
+
}
|
|
1314
|
+
if (subcommand === "version") {
|
|
1315
|
+
let name;
|
|
1316
|
+
let file;
|
|
1317
|
+
let expected;
|
|
1318
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
1319
|
+
const value2 = rest[index];
|
|
1320
|
+
if (value2 === "--file") {
|
|
1321
|
+
file = optionValue3(rest, index, value2);
|
|
1322
|
+
index += 1;
|
|
1323
|
+
} else if (value2 === "--expected") {
|
|
1324
|
+
expected = positiveInteger(optionValue3(rest, index, value2), value2);
|
|
1325
|
+
index += 1;
|
|
1326
|
+
} else if (value2 !== "--json" && !name) name = value2;
|
|
1327
|
+
else if (value2 !== "--json") throw new UsageError(`Unknown recipes version option: ${value2}`);
|
|
1328
|
+
}
|
|
1329
|
+
if (!name || !file || !expected) {
|
|
1330
|
+
throw new UsageError("Usage: mediaruntime recipes version <name> --file <recipe.json> --expected <n>");
|
|
1331
|
+
}
|
|
1332
|
+
const value = await recipeFile(file);
|
|
1333
|
+
const template = value.template ?? value;
|
|
1334
|
+
output(await dependencies.recipes.createVersion(name, {
|
|
1335
|
+
// Optimistic concurrency prevents two maintainers from replacing the same latest version.
|
|
1336
|
+
expectedLatestVersion: expected,
|
|
1337
|
+
...typeof value.description === "string" ? { description: value.description } : {},
|
|
1338
|
+
template
|
|
1339
|
+
}), json, dependencies.writeStdout);
|
|
1340
|
+
return 0;
|
|
1341
|
+
}
|
|
1342
|
+
if (subcommand === "archive") {
|
|
1343
|
+
const values = rest.filter((value) => value !== "--json");
|
|
1344
|
+
if (values.length !== 1) throw new UsageError("Usage: mediaruntime recipes archive <name>");
|
|
1345
|
+
await dependencies.recipes.archive(values[0] ?? "");
|
|
1346
|
+
dependencies.writeStdout(json ? `${JSON.stringify({ name: values[0], status: "archived" })}
|
|
1347
|
+
` : `Archived recipe ${values[0]}
|
|
1348
|
+
`);
|
|
1349
|
+
return 0;
|
|
1350
|
+
}
|
|
1351
|
+
throw new UsageError("Usage: mediaruntime recipes <list|get|create|version|archive>");
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
// src/commands/stickers.ts
|
|
1355
|
+
var COLLECTION_PATTERN = /^stc_[0-9a-f]{32}$/;
|
|
1356
|
+
var TOKEN_SCOPES = /* @__PURE__ */ new Set(["packs:read", "stickers:search", "stickers:read", "assets:resolve"]);
|
|
1357
|
+
var STICKER_VARIANTS = /* @__PURE__ */ new Set([
|
|
1358
|
+
"animated",
|
|
1359
|
+
"reduced_motion",
|
|
1360
|
+
"small_80",
|
|
1361
|
+
"small_100",
|
|
1362
|
+
"small_160",
|
|
1363
|
+
"thumbnail"
|
|
1364
|
+
]);
|
|
1365
|
+
var STICKERS_HELP = `MediaRuntime Hosted Sticker Runtime
|
|
1366
|
+
|
|
1367
|
+
Usage:
|
|
1368
|
+
mediaruntime stickers collections list [--include-archived] [--json]
|
|
1369
|
+
mediaruntime stickers collections create --name <name> [--description <text>] [--json]
|
|
1370
|
+
mediaruntime stickers collections get <collection_id> [--json]
|
|
1371
|
+
mediaruntime stickers collections update <collection_id>
|
|
1372
|
+
[--name <name>] [--description <text> | --clear-description]
|
|
1373
|
+
[--status active|archived] [--json]
|
|
1374
|
+
mediaruntime stickers collections archive <collection_id> [--json]
|
|
1375
|
+
mediaruntime stickers collections packs list <collection_id> [--json]
|
|
1376
|
+
mediaruntime stickers collections packs enable <collection_id>
|
|
1377
|
+
(--pack <pack_id> | --activation <activation_id>) [--json]
|
|
1378
|
+
mediaruntime stickers collections packs disable <collection_id> --pack <pack_id> [--json]
|
|
1379
|
+
mediaruntime stickers packs list --collection <collection_id> [--json]
|
|
1380
|
+
mediaruntime stickers search <query> --collection <collection_id>
|
|
1381
|
+
[--pack <pack_id>] [--category <category>] [--animated | --static]
|
|
1382
|
+
[--limit <1..50>] [--json]
|
|
1383
|
+
mediaruntime stickers typeahead <prefix> --collection <collection_id>
|
|
1384
|
+
[--pack <pack_id>] [--locale <locale>] [--limit <1..20>] [--json]
|
|
1385
|
+
mediaruntime stickers get <sticker_id> --collection <collection_id> [--json]
|
|
1386
|
+
mediaruntime stickers resolve <sticker_id> --variant <name>
|
|
1387
|
+
--collection <collection_id> [--json]
|
|
1388
|
+
mediaruntime stickers usage [--json]
|
|
1389
|
+
mediaruntime stickers token create --collection <collection_id>
|
|
1390
|
+
[--expires-in <60..3600>] [--scope <scope>]... [--json]
|
|
1391
|
+
|
|
1392
|
+
Authentication:
|
|
1393
|
+
API key or secure browser login is the default and is required for management,
|
|
1394
|
+
pack binding, usage, and token issuance. MEDIARUNTIME_STICKER_CLIENT_TOKEN is
|
|
1395
|
+
accepted only for packs/search/typeahead/get/resolve runtime reads and must match
|
|
1396
|
+
the explicit --collection. Secrets are never accepted as command-line options.
|
|
1397
|
+
`;
|
|
1398
|
+
function optionValue4(args, index, option) {
|
|
1399
|
+
const value = args[index + 1];
|
|
1400
|
+
if (value === void 0 || value.startsWith("--")) throw new UsageError(`${option} requires a value`);
|
|
1401
|
+
return value;
|
|
1402
|
+
}
|
|
1403
|
+
function collectionId(value) {
|
|
1404
|
+
if (!value || !COLLECTION_PATTERN.test(value)) {
|
|
1405
|
+
throw new UsageError("--collection must be a MediaRuntime sticker collection ID");
|
|
1406
|
+
}
|
|
1407
|
+
return value;
|
|
1408
|
+
}
|
|
1409
|
+
function boundedInteger(raw, option, minimum, maximum) {
|
|
1410
|
+
const value = Number(raw);
|
|
1411
|
+
if (!Number.isSafeInteger(value) || value < minimum || value > maximum) {
|
|
1412
|
+
throw new UsageError(`${option} must be an integer between ${minimum} and ${maximum}`);
|
|
1413
|
+
}
|
|
1414
|
+
return value;
|
|
1415
|
+
}
|
|
1416
|
+
function output2(value, json, writeStdout, human) {
|
|
1417
|
+
writeStdout(json ? `${JSON.stringify(value)}
|
|
1418
|
+
` : human);
|
|
1419
|
+
}
|
|
1420
|
+
function table2(headings, rows) {
|
|
1421
|
+
const widths = headings.map(
|
|
1422
|
+
(heading, index) => Math.max(heading.length, ...rows.map((row) => row[index]?.length ?? 0))
|
|
1423
|
+
);
|
|
1424
|
+
const render = (row) => row.map((cell3, index) => cell3.padEnd(widths[index] ?? cell3.length)).join(" ").trimEnd();
|
|
1425
|
+
return `${[render(headings), render(widths.map((width) => "-".repeat(width))), ...rows.map(render)].join("\n")}
|
|
1426
|
+
`;
|
|
1427
|
+
}
|
|
1428
|
+
function collectionTable(page) {
|
|
1429
|
+
return table2(
|
|
1430
|
+
["ID", "NAME", "STATUS", "PACKS", "UPDATED"],
|
|
1431
|
+
page.items.map((item) => [
|
|
1432
|
+
item.collectionId,
|
|
1433
|
+
item.name,
|
|
1434
|
+
item.status,
|
|
1435
|
+
String(item.packs.length),
|
|
1436
|
+
item.updatedAt
|
|
1437
|
+
])
|
|
1438
|
+
);
|
|
1439
|
+
}
|
|
1440
|
+
function bindingTable(page) {
|
|
1441
|
+
return table2(
|
|
1442
|
+
["PACK", "NAME", "VERSION", "STATUS", "HISTORICAL", "ACTIVATION"],
|
|
1443
|
+
page.items.map((item) => [
|
|
1444
|
+
item.packId,
|
|
1445
|
+
item.packName,
|
|
1446
|
+
item.packVersion,
|
|
1447
|
+
item.status,
|
|
1448
|
+
item.historicalAccess,
|
|
1449
|
+
item.activationId
|
|
1450
|
+
])
|
|
1451
|
+
);
|
|
1452
|
+
}
|
|
1453
|
+
function runtimePackTable(items) {
|
|
1454
|
+
return table2(
|
|
1455
|
+
["PACK", "NAME", "VERSION", "STICKERS", "ANIMATED"],
|
|
1456
|
+
items.map((item) => [
|
|
1457
|
+
item.packId,
|
|
1458
|
+
item.name,
|
|
1459
|
+
item.version,
|
|
1460
|
+
String(item.assetCount),
|
|
1461
|
+
item.animated ? "yes" : "no"
|
|
1462
|
+
])
|
|
1463
|
+
);
|
|
1464
|
+
}
|
|
1465
|
+
function stickerTable(items) {
|
|
1466
|
+
return table2(
|
|
1467
|
+
["STICKER", "LABEL", "EMOJI", "CATEGORY", "ANIMATED", "VARIANTS"],
|
|
1468
|
+
items.map((item) => [
|
|
1469
|
+
item.stickerId,
|
|
1470
|
+
item.label,
|
|
1471
|
+
item.emoji ?? "-",
|
|
1472
|
+
item.category ?? "-",
|
|
1473
|
+
item.animated ? "yes" : "no",
|
|
1474
|
+
item.variants.map((variant) => variant.name).join(",")
|
|
1475
|
+
])
|
|
1476
|
+
);
|
|
1477
|
+
}
|
|
1478
|
+
function hostedClient(dependencies) {
|
|
1479
|
+
if (!dependencies.hosted) {
|
|
1480
|
+
throw new UsageError("This stickers command requires MEDIARUNTIME_API_KEY or mediaruntime login");
|
|
1481
|
+
}
|
|
1482
|
+
return dependencies.hosted;
|
|
1483
|
+
}
|
|
1484
|
+
function runtimeClient(selectedCollectionId, dependencies) {
|
|
1485
|
+
if (dependencies.runtime) return dependencies.runtime(selectedCollectionId);
|
|
1486
|
+
return hostedClient(dependencies).collection(selectedCollectionId);
|
|
1487
|
+
}
|
|
1488
|
+
async function collectionsCommand(args, dependencies) {
|
|
1489
|
+
const subcommand = args[0];
|
|
1490
|
+
const rest = args.slice(1);
|
|
1491
|
+
const client = hostedClient(dependencies);
|
|
1492
|
+
const json = rest.includes("--json");
|
|
1493
|
+
if (subcommand === "list") {
|
|
1494
|
+
let includeArchived = false;
|
|
1495
|
+
for (const argument of rest) {
|
|
1496
|
+
if (argument === "--include-archived") includeArchived = true;
|
|
1497
|
+
else if (argument !== "--json") throw new UsageError(`Unknown stickers collections list option: ${argument}`);
|
|
1498
|
+
}
|
|
1499
|
+
const page = await client.listCollections({ includeArchived });
|
|
1500
|
+
output2(page, json, dependencies.writeStdout, collectionTable(page));
|
|
1501
|
+
return 0;
|
|
1502
|
+
}
|
|
1503
|
+
if (subcommand === "create") {
|
|
1504
|
+
let name;
|
|
1505
|
+
let description;
|
|
1506
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
1507
|
+
const argument = rest[index];
|
|
1508
|
+
if (argument === "--name") {
|
|
1509
|
+
name = optionValue4(rest, index, argument);
|
|
1510
|
+
index += 1;
|
|
1511
|
+
} else if (argument === "--description") {
|
|
1512
|
+
description = optionValue4(rest, index, argument);
|
|
1513
|
+
index += 1;
|
|
1514
|
+
} else if (argument !== "--json") throw new UsageError(`Unknown stickers collections create option: ${argument}`);
|
|
1515
|
+
}
|
|
1516
|
+
if (!name) throw new UsageError("Usage: mediaruntime stickers collections create --name <name> [--description <text>]");
|
|
1517
|
+
const created = await client.createCollection({ name, ...description === void 0 ? {} : { description } });
|
|
1518
|
+
output2(created, json, dependencies.writeStdout, `Created sticker collection ${created.collectionId} (${created.name})
|
|
1519
|
+
`);
|
|
1520
|
+
return 0;
|
|
1521
|
+
}
|
|
1522
|
+
if (subcommand === "get" || subcommand === "archive") {
|
|
1523
|
+
const positional = rest.filter((argument) => argument !== "--json");
|
|
1524
|
+
if (positional.length !== 1) {
|
|
1525
|
+
throw new UsageError(`Usage: mediaruntime stickers collections ${subcommand} <collection_id>`);
|
|
1526
|
+
}
|
|
1527
|
+
const selected = collectionId(positional[0]);
|
|
1528
|
+
const result = subcommand === "get" ? await client.getCollection(selected) : await client.archiveCollection(selected);
|
|
1529
|
+
const human = subcommand === "get" ? `${result.name} (${result.collectionId})
|
|
1530
|
+
Status: ${result.status}
|
|
1531
|
+
Description: ${result.description || "-"}
|
|
1532
|
+
Enabled packs: ${result.packs.length}
|
|
1533
|
+
` : `Archived sticker collection ${result.collectionId}
|
|
1534
|
+
`;
|
|
1535
|
+
output2(result, json, dependencies.writeStdout, human);
|
|
1536
|
+
return 0;
|
|
1537
|
+
}
|
|
1538
|
+
if (subcommand === "update") {
|
|
1539
|
+
let selected;
|
|
1540
|
+
let name;
|
|
1541
|
+
let description;
|
|
1542
|
+
let status;
|
|
1543
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
1544
|
+
const argument = rest[index];
|
|
1545
|
+
if (argument === "--name") {
|
|
1546
|
+
name = optionValue4(rest, index, argument);
|
|
1547
|
+
index += 1;
|
|
1548
|
+
} else if (argument === "--description") {
|
|
1549
|
+
description = optionValue4(rest, index, argument);
|
|
1550
|
+
index += 1;
|
|
1551
|
+
} else if (argument === "--clear-description") {
|
|
1552
|
+
description = null;
|
|
1553
|
+
} else if (argument === "--status") {
|
|
1554
|
+
const value = optionValue4(rest, index, argument);
|
|
1555
|
+
if (value !== "active" && value !== "archived") throw new UsageError("--status must be active or archived");
|
|
1556
|
+
status = value;
|
|
1557
|
+
index += 1;
|
|
1558
|
+
} else if (argument !== "--json" && !selected) selected = argument;
|
|
1559
|
+
else if (argument !== "--json") throw new UsageError(`Unknown stickers collections update option: ${argument}`);
|
|
1560
|
+
}
|
|
1561
|
+
const id = collectionId(selected);
|
|
1562
|
+
if (name === void 0 && description === void 0 && status === void 0) {
|
|
1563
|
+
throw new UsageError("stickers collections update requires --name, --description, --clear-description, or --status");
|
|
1564
|
+
}
|
|
1565
|
+
const updated = await client.updateCollection(id, {
|
|
1566
|
+
...name === void 0 ? {} : { name },
|
|
1567
|
+
...description === void 0 ? {} : { description },
|
|
1568
|
+
...status === void 0 ? {} : { status }
|
|
1569
|
+
});
|
|
1570
|
+
output2(updated, json, dependencies.writeStdout, `Updated sticker collection ${updated.collectionId}
|
|
1571
|
+
`);
|
|
1572
|
+
return 0;
|
|
1573
|
+
}
|
|
1574
|
+
if (subcommand === "packs") return collectionPacksCommand(rest, dependencies);
|
|
1575
|
+
throw new UsageError("Usage: mediaruntime stickers collections <list|create|get|update|archive|packs>");
|
|
1576
|
+
}
|
|
1577
|
+
async function collectionPacksCommand(args, dependencies) {
|
|
1578
|
+
const action = args[0];
|
|
1579
|
+
const rest = args.slice(1);
|
|
1580
|
+
const json = rest.includes("--json");
|
|
1581
|
+
const client = hostedClient(dependencies);
|
|
1582
|
+
let selected;
|
|
1583
|
+
let packId;
|
|
1584
|
+
let activationId;
|
|
1585
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
1586
|
+
const argument = rest[index];
|
|
1587
|
+
if (argument === "--pack") {
|
|
1588
|
+
packId = optionValue4(rest, index, argument);
|
|
1589
|
+
index += 1;
|
|
1590
|
+
} else if (argument === "--activation") {
|
|
1591
|
+
activationId = optionValue4(rest, index, argument);
|
|
1592
|
+
index += 1;
|
|
1593
|
+
} else if (argument !== "--json" && !selected) selected = argument;
|
|
1594
|
+
else if (argument !== "--json") throw new UsageError(`Unknown stickers collections packs option: ${argument}`);
|
|
1595
|
+
}
|
|
1596
|
+
const id = collectionId(selected);
|
|
1597
|
+
if (action === "list") {
|
|
1598
|
+
if (packId || activationId) throw new UsageError("stickers collections packs list does not accept a pack selector");
|
|
1599
|
+
const page = await client.listCollectionPacks(id);
|
|
1600
|
+
output2(page, json, dependencies.writeStdout, bindingTable(page));
|
|
1601
|
+
return 0;
|
|
1602
|
+
}
|
|
1603
|
+
if (action === "enable") {
|
|
1604
|
+
if ((packId ? 1 : 0) + (activationId ? 1 : 0) !== 1) {
|
|
1605
|
+
throw new UsageError("stickers collections packs enable requires exactly one of --pack or --activation");
|
|
1606
|
+
}
|
|
1607
|
+
const binding = activationId ? await client.addCollectionPack(id, { activationId }) : await client.enableCollectionPack(id, packId ?? "");
|
|
1608
|
+
output2(binding, json, dependencies.writeStdout, `Enabled pack ${binding.packId} in ${id}
|
|
1609
|
+
`);
|
|
1610
|
+
return 0;
|
|
1611
|
+
}
|
|
1612
|
+
if (action === "disable") {
|
|
1613
|
+
if (!packId || activationId) throw new UsageError("stickers collections packs disable requires --pack <pack_id>");
|
|
1614
|
+
const binding = await client.disableCollectionPack(id, packId);
|
|
1615
|
+
output2(binding, json, dependencies.writeStdout, `Disabled pack ${binding.packId} in ${id}; historical references remain ${binding.historicalAccess}
|
|
1616
|
+
`);
|
|
1617
|
+
return 0;
|
|
1618
|
+
}
|
|
1619
|
+
throw new UsageError("Usage: mediaruntime stickers collections packs <list|enable|disable> <collection_id>");
|
|
1620
|
+
}
|
|
1621
|
+
function parseRuntimeOptions(args, command) {
|
|
1622
|
+
const options = { json: false, positional: [] };
|
|
1623
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
1624
|
+
const argument = args[index];
|
|
1625
|
+
if (argument === "--collection") {
|
|
1626
|
+
options.collection = optionValue4(args, index, argument);
|
|
1627
|
+
index += 1;
|
|
1628
|
+
} else if (argument === "--pack") {
|
|
1629
|
+
options.packId = optionValue4(args, index, argument);
|
|
1630
|
+
index += 1;
|
|
1631
|
+
} else if (argument === "--category") {
|
|
1632
|
+
options.category = optionValue4(args, index, argument);
|
|
1633
|
+
index += 1;
|
|
1634
|
+
} else if (argument === "--locale") {
|
|
1635
|
+
options.locale = optionValue4(args, index, argument);
|
|
1636
|
+
index += 1;
|
|
1637
|
+
} else if (argument === "--limit") {
|
|
1638
|
+
const maximum = command === "typeahead" ? 20 : 50;
|
|
1639
|
+
options.limit = boundedInteger(optionValue4(args, index, argument), argument, 1, maximum);
|
|
1640
|
+
index += 1;
|
|
1641
|
+
} else if (argument === "--animated" || argument === "--static") {
|
|
1642
|
+
if (options.animated !== void 0) throw new UsageError("Use only one of --animated or --static");
|
|
1643
|
+
options.animated = argument === "--animated";
|
|
1644
|
+
} else if (argument === "--variant") {
|
|
1645
|
+
options.variant = optionValue4(args, index, argument);
|
|
1646
|
+
index += 1;
|
|
1647
|
+
} else if (argument === "--json") options.json = true;
|
|
1648
|
+
else if (argument?.startsWith("--")) throw new UsageError(`Unknown stickers ${command} option: ${argument}`);
|
|
1649
|
+
else options.positional.push(argument ?? "");
|
|
1650
|
+
}
|
|
1651
|
+
return options;
|
|
1652
|
+
}
|
|
1653
|
+
async function runtimeCommand(command, args, dependencies) {
|
|
1654
|
+
const options = parseRuntimeOptions(args, command);
|
|
1655
|
+
const id = collectionId(options.collection);
|
|
1656
|
+
const client = runtimeClient(id, dependencies);
|
|
1657
|
+
if (command === "packs") {
|
|
1658
|
+
if (options.packId || options.category || options.locale || options.limit !== void 0 || options.animated !== void 0 || options.variant) {
|
|
1659
|
+
throw new UsageError("stickers packs list accepts only --collection and --json");
|
|
1660
|
+
}
|
|
1661
|
+
if (options.positional.length !== 1 || options.positional[0] !== "list") {
|
|
1662
|
+
throw new UsageError("Usage: mediaruntime stickers packs list --collection <collection_id>");
|
|
1663
|
+
}
|
|
1664
|
+
const items = await client.listPacks();
|
|
1665
|
+
output2({ items, total: items.length }, options.json, dependencies.writeStdout, runtimePackTable(items));
|
|
1666
|
+
return 0;
|
|
1667
|
+
}
|
|
1668
|
+
if (command === "search") {
|
|
1669
|
+
if (options.locale || options.variant) throw new UsageError("stickers search does not accept typeahead or asset options");
|
|
1670
|
+
if (options.positional.length !== 1) throw new UsageError("Usage: mediaruntime stickers search <query> --collection <collection_id>");
|
|
1671
|
+
const result = await client.search(options.positional[0] ?? "", {
|
|
1672
|
+
...options.packId === void 0 ? {} : { packId: options.packId },
|
|
1673
|
+
...options.category === void 0 ? {} : { category: options.category },
|
|
1674
|
+
...options.animated === void 0 ? {} : { animated: options.animated },
|
|
1675
|
+
...options.limit === void 0 ? {} : { limit: options.limit }
|
|
1676
|
+
});
|
|
1677
|
+
output2(result, options.json, dependencies.writeStdout, stickerTable(result.items));
|
|
1678
|
+
return 0;
|
|
1679
|
+
}
|
|
1680
|
+
if (command === "typeahead") {
|
|
1681
|
+
if (options.category || options.animated !== void 0 || options.variant) {
|
|
1682
|
+
throw new UsageError("stickers typeahead accepts only --pack, --locale, and --limit filters");
|
|
1683
|
+
}
|
|
1684
|
+
if (options.positional.length !== 1) throw new UsageError("Usage: mediaruntime stickers typeahead <prefix> --collection <collection_id>");
|
|
1685
|
+
const result = await client.typeahead(options.positional[0] ?? "", {
|
|
1686
|
+
...options.packId === void 0 ? {} : { packId: options.packId },
|
|
1687
|
+
...options.locale === void 0 ? {} : { locale: options.locale },
|
|
1688
|
+
...options.limit === void 0 ? {} : { limit: options.limit }
|
|
1689
|
+
});
|
|
1690
|
+
const human = table2(
|
|
1691
|
+
["SUGGESTION", "STICKERS"],
|
|
1692
|
+
result.suggestions.map((suggestion) => [suggestion.text, String(suggestion.assetCount)])
|
|
1693
|
+
);
|
|
1694
|
+
output2(result, options.json, dependencies.writeStdout, human);
|
|
1695
|
+
return 0;
|
|
1696
|
+
}
|
|
1697
|
+
if (command === "get") {
|
|
1698
|
+
if (options.packId || options.category || options.locale || options.limit !== void 0 || options.animated !== void 0 || options.variant) {
|
|
1699
|
+
throw new UsageError("stickers get accepts only --collection and --json");
|
|
1700
|
+
}
|
|
1701
|
+
if (options.positional.length !== 1) throw new UsageError("Usage: mediaruntime stickers get <sticker_id> --collection <collection_id>");
|
|
1702
|
+
const sticker = await client.retrieve(options.positional[0] ?? "");
|
|
1703
|
+
const human = `${sticker.label} (${sticker.stickerId})
|
|
1704
|
+
Pack: ${sticker.packId}
|
|
1705
|
+
Category: ${sticker.category ?? "-"}
|
|
1706
|
+
Variants: ${sticker.variants.map((item) => item.name).join(", ")}
|
|
1707
|
+
`;
|
|
1708
|
+
output2(sticker, options.json, dependencies.writeStdout, human);
|
|
1709
|
+
return 0;
|
|
1710
|
+
}
|
|
1711
|
+
if (command === "resolve") {
|
|
1712
|
+
if (options.packId || options.category || options.locale || options.limit !== void 0 || options.animated !== void 0) {
|
|
1713
|
+
throw new UsageError("stickers resolve accepts only --variant, --collection, and --json");
|
|
1714
|
+
}
|
|
1715
|
+
if (options.positional.length !== 1 || !options.variant) {
|
|
1716
|
+
throw new UsageError("Usage: mediaruntime stickers resolve <sticker_id> --variant <name> --collection <collection_id>");
|
|
1717
|
+
}
|
|
1718
|
+
if (!STICKER_VARIANTS.has(options.variant)) {
|
|
1719
|
+
throw new UsageError(`Unsupported sticker variant: ${options.variant}`);
|
|
1720
|
+
}
|
|
1721
|
+
const resolution = await client.resolve(options.positional[0] ?? "", options.variant);
|
|
1722
|
+
const human = `Sticker: ${String(resolution.stickerId ?? "-")}
|
|
1723
|
+
Variant: ${String(resolution.variant ?? options.variant)}
|
|
1724
|
+
Bytes: ${String(resolution.bytes ?? "-")}
|
|
1725
|
+
Expires: ${String(resolution.expiresAt ?? "-")}
|
|
1726
|
+
URL: ${String(resolution.url ?? "-")}
|
|
1727
|
+
`;
|
|
1728
|
+
output2(resolution, options.json, dependencies.writeStdout, human);
|
|
1729
|
+
return 0;
|
|
1730
|
+
}
|
|
1731
|
+
throw new UsageError("Usage: mediaruntime stickers <packs|search|typeahead|get|resolve>");
|
|
1732
|
+
}
|
|
1733
|
+
async function tokenCommand(args, dependencies) {
|
|
1734
|
+
if (args[0] !== "create") throw new UsageError("Usage: mediaruntime stickers token create --collection <collection_id>");
|
|
1735
|
+
let selected;
|
|
1736
|
+
let expiresInSeconds;
|
|
1737
|
+
const scopes = [];
|
|
1738
|
+
let json = false;
|
|
1739
|
+
for (let index = 1; index < args.length; index += 1) {
|
|
1740
|
+
const argument = args[index];
|
|
1741
|
+
if (argument === "--collection") {
|
|
1742
|
+
selected = optionValue4(args, index, argument);
|
|
1743
|
+
index += 1;
|
|
1744
|
+
} else if (argument === "--expires-in") {
|
|
1745
|
+
expiresInSeconds = boundedInteger(optionValue4(args, index, argument), argument, 60, 3600);
|
|
1746
|
+
index += 1;
|
|
1747
|
+
} else if (argument === "--scope") {
|
|
1748
|
+
const scope = optionValue4(args, index, argument);
|
|
1749
|
+
if (!TOKEN_SCOPES.has(scope)) throw new UsageError(`Unsupported Sticker Runtime scope: ${scope}`);
|
|
1750
|
+
scopes.push(scope);
|
|
1751
|
+
index += 1;
|
|
1752
|
+
} else if (argument === "--json") json = true;
|
|
1753
|
+
else throw new UsageError(`Unknown stickers token create option: ${argument}`);
|
|
1754
|
+
}
|
|
1755
|
+
const result = await hostedClient(dependencies).createClientToken({
|
|
1756
|
+
collectionId: collectionId(selected),
|
|
1757
|
+
...expiresInSeconds === void 0 ? {} : { expiresInSeconds },
|
|
1758
|
+
...scopes.length === 0 ? {} : { scopes: [...new Set(scopes)] }
|
|
1759
|
+
});
|
|
1760
|
+
const human = `Access token: ${String(result.accessToken ?? "")}
|
|
1761
|
+
Expires: ${String(result.expiresAt ?? "-")}
|
|
1762
|
+
Collection: ${String(result.collectionId ?? "-")}
|
|
1763
|
+
Scopes: ${Array.isArray(result.scopes) ? result.scopes.join(", ") : "-"}
|
|
1764
|
+
`;
|
|
1765
|
+
output2(result, json, dependencies.writeStdout, human);
|
|
1766
|
+
return 0;
|
|
1767
|
+
}
|
|
1768
|
+
function isScopedStickerRuntimeCommand(args) {
|
|
1769
|
+
return ["packs", "search", "typeahead", "get", "resolve"].includes(args[0] ?? "");
|
|
1770
|
+
}
|
|
1771
|
+
function stickerCollectionOption(args) {
|
|
1772
|
+
const index = args.indexOf("--collection");
|
|
1773
|
+
return collectionId(index < 0 ? void 0 : args[index + 1]);
|
|
1774
|
+
}
|
|
1775
|
+
async function runStickersCommand(args, dependencies) {
|
|
1776
|
+
const command = args[0];
|
|
1777
|
+
if (!command || command === "--help" || command === "-h" || args.includes("--help") || args.includes("-h")) {
|
|
1778
|
+
dependencies.writeStdout(STICKERS_HELP);
|
|
1779
|
+
return 0;
|
|
1780
|
+
}
|
|
1781
|
+
if (command === "collections") return collectionsCommand(args.slice(1), dependencies);
|
|
1782
|
+
if (command === "token") return tokenCommand(args.slice(1), dependencies);
|
|
1783
|
+
if (command === "usage") {
|
|
1784
|
+
const rest = args.slice(1);
|
|
1785
|
+
if (rest.some((argument) => argument !== "--json")) throw new UsageError("Usage: mediaruntime stickers usage [--json]");
|
|
1786
|
+
const result = await hostedClient(dependencies).usage();
|
|
1787
|
+
const human = `Month: ${String(result.month ?? "-")}
|
|
1788
|
+
Status: ${String(result.status ?? "-")}
|
|
1789
|
+
Operations: ${String(result.operations ?? 0)} / ${String(result.includedOperations ?? 0)}
|
|
1790
|
+
Authorized delivery: ${String(result.authorizedDeliveryBytes ?? 0)} / ${String(result.includedDeliveryBytes ?? 0)} bytes
|
|
1791
|
+
Overage: ${String(result.overageChargedCents ?? 0)} ${String(result.currency ?? "USD")} cents
|
|
1792
|
+
`;
|
|
1793
|
+
output2(result, rest.includes("--json"), dependencies.writeStdout, human);
|
|
1794
|
+
return 0;
|
|
1795
|
+
}
|
|
1796
|
+
if (isScopedStickerRuntimeCommand(args)) return runtimeCommand(command ?? "", args.slice(1), dependencies);
|
|
1797
|
+
throw new UsageError("Usage: mediaruntime stickers <collections|packs|search|typeahead|get|resolve|usage|token>");
|
|
1798
|
+
}
|
|
1799
|
+
|
|
834
1800
|
// src/commands/trigger.ts
|
|
835
1801
|
import { randomBytes as randomBytes2, randomUUID } from "crypto";
|
|
836
|
-
import { readFile } from "fs/promises";
|
|
1802
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
837
1803
|
|
|
838
1804
|
// src/trigger/destination.ts
|
|
839
1805
|
function isLoopbackDestination(value) {
|
|
@@ -904,6 +1870,7 @@ function createSyntheticTerminalEvent(type, options) {
|
|
|
904
1870
|
size_bytes: 0,
|
|
905
1871
|
sha256: "0".repeat(64),
|
|
906
1872
|
download: {
|
|
1873
|
+
// This marker URL is descriptive only; trigger never serves a synthetic ZIP.
|
|
907
1874
|
url: `http://127.0.0.1/__mediaruntime_synthetic__/${encodeURIComponent(options.jobId)}.zip`,
|
|
908
1875
|
expiresAt
|
|
909
1876
|
}
|
|
@@ -1061,7 +2028,7 @@ async function runTriggerCommand(args, dependencies = {}) {
|
|
|
1061
2028
|
now: dependencies.now ?? Date.now,
|
|
1062
2029
|
randomUUID: dependencies.randomUUID ?? randomUUID,
|
|
1063
2030
|
randomBytes: dependencies.randomBytes ?? randomBytes2,
|
|
1064
|
-
readFile: dependencies.readFile ??
|
|
2031
|
+
readFile: dependencies.readFile ?? readFile2,
|
|
1065
2032
|
writeStdout: dependencies.writeStdout ?? ((text) => process.stdout.write(text))
|
|
1066
2033
|
};
|
|
1067
2034
|
const secret = await resolveSecret(parsed, deps);
|
|
@@ -1150,15 +2117,50 @@ function createActivityIndicator(write, enabled) {
|
|
|
1150
2117
|
}
|
|
1151
2118
|
|
|
1152
2119
|
// src/cli.ts
|
|
1153
|
-
var VERSION = "1.
|
|
2120
|
+
var VERSION = "1.2.0";
|
|
1154
2121
|
var HELP = `MediaRuntime CLI
|
|
1155
2122
|
|
|
1156
2123
|
Usage:
|
|
1157
|
-
mediaruntime run <source> (--output <alias> | --preset <name>) [...] [--wait]
|
|
2124
|
+
mediaruntime run <source> (--recipe <name[@version]> | --output <alias> | --preset <name>) [...] [--wait]
|
|
2125
|
+
Animation presets: [--animation-width N] [--animation-fps N] [--animation-start SEC]
|
|
2126
|
+
[--animation-duration SEC] [--animation-loop N] [--animation-quality N]
|
|
2127
|
+
Placeholder preset: [--placeholder-max-dimension N] [--placeholder-time SEC]
|
|
2128
|
+
[--lqip-quality N] [--lqip-max-bytes N]
|
|
2129
|
+
Contact-sheet preset: [--contact-columns N] [--contact-rows N]
|
|
2130
|
+
[--contact-tile-width N] [--contact-tile-height N] [--contact-interval SEC]
|
|
2131
|
+
[--contact-start SEC] [--contact-duration SEC] [--contact-max-sheets N]
|
|
2132
|
+
[--contact-format jpg|png|webp] [--contact-quality N]
|
|
2133
|
+
[--image-width PX] [--image-height PX] [--image-mode fit|fill|cover|contain]
|
|
2134
|
+
[--image-format jpg|png|webp|avif] [--image-quality N]
|
|
2135
|
+
[--image-max-bytes N] [--image-min-quality N]
|
|
2136
|
+
Audiogram preset: --audiogram-artwork <source> [--audiogram-captions <source>]
|
|
2137
|
+
[--audiogram-layout square|portrait|landscape]
|
|
2138
|
+
[--audiogram-fit contain|cover|blurred_background]
|
|
2139
|
+
[--audiogram-background #RRGGBB] [--audiogram-waveform #RRGGBB]
|
|
2140
|
+
[--audiogram-waveform-gain 0.5..4]
|
|
2141
|
+
[--audiogram-caption-position top|bottom] [--audiogram-caption-scale 0.75..1.5]
|
|
2142
|
+
[--audiogram-normalize] [--audiogram-loudness-target -24..-12]
|
|
2143
|
+
[--audiogram-start SEC] [--audiogram-duration SEC] [--audiogram-fps 15..30]
|
|
2144
|
+
Privacy redaction Preview (still-image input + image output only):
|
|
2145
|
+
--privacy-detector face|license_plate|text
|
|
2146
|
+
[--privacy-style blur|pixelate|solid] [--privacy-failure-mode fail_closed|report_only]
|
|
2147
|
+
[--privacy-min-confidence 0.30..0.99] [--privacy-sample-interval 0.1..30]
|
|
2148
|
+
[--privacy-max-frames 1..18000] [--privacy-padding 0..0.5]
|
|
2149
|
+
[--privacy-solid-color #RRGGBB] [--privacy-pixel-block-size 4..128]
|
|
2150
|
+
[--privacy-strength standard|strong] [--privacy-debug-observations]
|
|
1158
2151
|
mediaruntime capabilities [--json]
|
|
1159
2152
|
mediaruntime presets list [--json]
|
|
1160
2153
|
mediaruntime jobs list [--status <status>] [--limit <n>] [--cursor <cursor>]
|
|
1161
2154
|
mediaruntime jobs get <job_id> [--download <bundle.zip>] [--force]
|
|
2155
|
+
mediaruntime recipes <list|get|create|version|archive>
|
|
2156
|
+
mediaruntime stickers collections <list|create|get|update|archive|packs>
|
|
2157
|
+
mediaruntime stickers packs list --collection <collection_id>
|
|
2158
|
+
mediaruntime stickers search <query> --collection <collection_id>
|
|
2159
|
+
mediaruntime stickers typeahead <prefix> --collection <collection_id>
|
|
2160
|
+
mediaruntime stickers get <sticker_id> --collection <collection_id>
|
|
2161
|
+
mediaruntime stickers resolve <sticker_id> --variant <name> --collection <collection_id>
|
|
2162
|
+
mediaruntime stickers usage
|
|
2163
|
+
mediaruntime stickers token create --collection <collection_id>
|
|
1162
2164
|
mediaruntime trigger <job.completed|job.failed|job.rejected> --to <local-url>
|
|
1163
2165
|
mediaruntime login [--no-browser]
|
|
1164
2166
|
mediaruntime auth status
|
|
@@ -1173,6 +2175,9 @@ Run/jobs options:
|
|
|
1173
2175
|
--json Machine-readable, URL-redacted output
|
|
1174
2176
|
|
|
1175
2177
|
Authentication uses a secure login or MEDIARUNTIME_API_KEY.
|
|
2178
|
+
Sticker commands use that API key by default. MEDIARUNTIME_STICKER_CLIENT_TOKEN may
|
|
2179
|
+
replace it only for collection-bound packs/search/typeahead/get/resolve reads; it cannot
|
|
2180
|
+
manage collections, inspect workspace usage, or mint another token.
|
|
1176
2181
|
`;
|
|
1177
2182
|
function isFileExistsError(error) {
|
|
1178
2183
|
return error instanceof Error && "code" in error && error.code === "EEXIST";
|
|
@@ -1352,12 +2357,34 @@ async function executeCli(argv, dependencies = {}) {
|
|
|
1352
2357
|
}
|
|
1353
2358
|
return await runPresetsCommand(global.args.slice(1), publicDependencies);
|
|
1354
2359
|
}
|
|
2360
|
+
const stickerArguments = global.args.slice(1);
|
|
2361
|
+
const stickerClientToken = process.env.MEDIARUNTIME_STICKER_CLIENT_TOKEN?.trim();
|
|
2362
|
+
if (command === "stickers" && (!stickerArguments[0] || stickerArguments.includes("--help") || stickerArguments.includes("-h"))) {
|
|
2363
|
+
return await runStickersCommand(stickerArguments, { writeStdout });
|
|
2364
|
+
}
|
|
2365
|
+
if (command === "stickers" && stickerClientToken && isScopedStickerRuntimeCommand(stickerArguments)) {
|
|
2366
|
+
const selectedCollectionId = stickerCollectionOption(stickerArguments);
|
|
2367
|
+
const options = {
|
|
2368
|
+
accessToken: stickerClientToken,
|
|
2369
|
+
collectionId: selectedCollectionId,
|
|
2370
|
+
...baseUrl === void 0 ? {} : { baseUrl }
|
|
2371
|
+
};
|
|
2372
|
+
const constructor = MediaRuntimeSdk.StickerRuntime;
|
|
2373
|
+
const runtime = dependencies.createStickerRuntime?.(options) ?? (constructor ? new constructor(options) : void 0);
|
|
2374
|
+
if (!runtime) {
|
|
2375
|
+
throw new UsageError("The installed @mediaruntime/node SDK does not support scoped Sticker Runtime tokens");
|
|
2376
|
+
}
|
|
2377
|
+
return await runStickersCommand(stickerArguments, {
|
|
2378
|
+
runtime: () => runtime,
|
|
2379
|
+
writeStdout
|
|
2380
|
+
});
|
|
2381
|
+
}
|
|
1355
2382
|
let apiKey = process.env.MEDIARUNTIME_API_KEY?.trim();
|
|
1356
2383
|
if (!apiKey && (!dependencies.createClient || dependencies.credentialStore)) {
|
|
1357
2384
|
apiKey = (await resolveCredential(authBaseUrl, authDependencies))?.apiKey;
|
|
1358
2385
|
}
|
|
1359
2386
|
if (!dependencies.createClient && !apiKey) {
|
|
1360
|
-
throw new UsageError("Run mediaruntime login or set MEDIARUNTIME_API_KEY before using
|
|
2387
|
+
throw new UsageError("Run mediaruntime login or set MEDIARUNTIME_API_KEY before using this command");
|
|
1361
2388
|
}
|
|
1362
2389
|
const client = dependencies.createClient?.({
|
|
1363
2390
|
...baseUrl === void 0 ? {} : { baseUrl },
|
|
@@ -1370,6 +2397,7 @@ async function executeCli(argv, dependencies = {}) {
|
|
|
1370
2397
|
const commandDependencies = {
|
|
1371
2398
|
jobs: client.jobs,
|
|
1372
2399
|
capabilities: client.capabilities,
|
|
2400
|
+
...client.uploads ? { uploads: client.uploads } : {},
|
|
1373
2401
|
writeStdout,
|
|
1374
2402
|
downloadBundle,
|
|
1375
2403
|
activity: createActivityIndicator(writeStderr, isStderrTTY && !json)
|
|
@@ -1378,6 +2406,21 @@ async function executeCli(argv, dependencies = {}) {
|
|
|
1378
2406
|
if (command === "jobs") {
|
|
1379
2407
|
return await runJobsCommand(global.args.slice(1), commandDependencies);
|
|
1380
2408
|
}
|
|
2409
|
+
if (command === "recipes") {
|
|
2410
|
+
if (!client.recipes) throw new UsageError("This MediaRuntime client does not support hosted recipes");
|
|
2411
|
+
return await runRecipesCommand(global.args.slice(1), {
|
|
2412
|
+
recipes: client.recipes,
|
|
2413
|
+
writeStdout
|
|
2414
|
+
});
|
|
2415
|
+
}
|
|
2416
|
+
if (command === "stickers") {
|
|
2417
|
+
const stickers = client.stickers;
|
|
2418
|
+
if (!stickers) throw new UsageError("This MediaRuntime client does not support Hosted Sticker Runtime");
|
|
2419
|
+
return await runStickersCommand(stickerArguments, {
|
|
2420
|
+
hosted: stickers,
|
|
2421
|
+
writeStdout
|
|
2422
|
+
});
|
|
2423
|
+
}
|
|
1381
2424
|
throw new UsageError(`Unknown command: ${command}`);
|
|
1382
2425
|
} catch (error) {
|
|
1383
2426
|
const exitCode = exitCodeFor(error);
|