@mediaruntime/cli 1.3.0 → 1.5.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 +16 -0
- package/README.md +14 -1
- package/dist/cli.js +184 -8
- package/dist/cli.js.map +1 -1
- package/docs/CLI_SDD.md +13 -0
- package/docs/clipping.md +113 -0
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.5.0 — 2026-09-21
|
|
4
|
+
|
|
5
|
+
- Add `run --no-webhook` to suppress callbacks for a billable job without changing account settings.
|
|
6
|
+
- Preserve normal delivery when omitted; waiting and downloading work with either policy.
|
|
7
|
+
- Require Node SDK 1.5.0 and expose the returned policy in job detail JSON.
|
|
8
|
+
|
|
9
|
+
## 1.4.0 — 2026-09-13
|
|
10
|
+
|
|
11
|
+
- Add capability-discovered Clip Candidates and Video Clip commands, bounded ranges,
|
|
12
|
+
original or vertical blurred framing, keyword controls, and opt-in burned captions.
|
|
13
|
+
- Attach local JSON plans/segment arrays, SRT, or VTT with source timestamps; reuse
|
|
14
|
+
transcripts for analysis without another Whisper run. Validate bounded UTF-8 attachments
|
|
15
|
+
before submission and preserve the normal wait and verified ZIP download flow.
|
|
16
|
+
- Require `@mediaruntime/node` 1.4.0 for clipping serialization and report parity.
|
|
17
|
+
- Correct the executable version and regression-test it against the package manifest.
|
|
18
|
+
|
|
3
19
|
## 1.3.0 — 2026-08-31
|
|
4
20
|
|
|
5
21
|
- Add complete Hosted Sticker Runtime commands for collection lifecycle, pack binding,
|
package/README.md
CHANGED
|
@@ -4,7 +4,20 @@ Official command-line client for MediaRuntime. It submits media, waits for jobs,
|
|
|
4
4
|
the canonical ZIP output bundle, inspects account jobs, and sends correctly signed
|
|
5
5
|
synthetic webhooks to a local receiver.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Version `1.5.0` adds `run --no-webhook` for local development against hosted MediaRuntime. Use `--wait` or `--download` to retrieve results without a callback. Normal account billing applies; omitting the switch preserves normal webhook delivery.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
mediaruntime run ./product-video.mp4 --output video.web --no-webhook --download ./result.zip
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
This suppresses callbacks even when your account has a live endpoint. The account configuration is unchanged. Webhook retries cannot override the job's opt-out. A polling timeout does not cancel processing: keep the printed job ID and use `mediaruntime jobs get <job-id>` to check it instead of creating another billable job.
|
|
14
|
+
|
|
15
|
+
Version `1.4.0` adds [clipping commands](docs/clipping.md) for analysis,
|
|
16
|
+
manual ranges, vertical blur-fill, and JSON/SRT/VTT transcript attachments. It requires
|
|
17
|
+
Node SDK 1.4.0 or newer and a gateway/engine deployment supporting the clipping
|
|
18
|
+
presets. The live capability catalog determines which presets are available.
|
|
19
|
+
|
|
20
|
+
The documented `1.x` command names, flags, JSON envelopes, exit
|
|
8
21
|
codes, and credential precedence follow semantic versioning. Breaking changes require a
|
|
9
22
|
new major version; additive commands and fields may ship in minor releases.
|
|
10
23
|
|
package/dist/cli.js
CHANGED
|
@@ -459,6 +459,7 @@ function detailsProjection(job) {
|
|
|
459
459
|
id: job.id,
|
|
460
460
|
status: job.status,
|
|
461
461
|
tier: job.tier,
|
|
462
|
+
...job.deliverWebhook === void 0 ? {} : { deliverWebhook: job.deliverWebhook },
|
|
462
463
|
usage: job.usage,
|
|
463
464
|
billing: job.billing,
|
|
464
465
|
bundle: {
|
|
@@ -608,6 +609,93 @@ async function runJobsCommand(args, dependencies) {
|
|
|
608
609
|
throw new UsageError("Usage: mediaruntime jobs <list|get>");
|
|
609
610
|
}
|
|
610
611
|
|
|
612
|
+
// src/clipTranscript.ts
|
|
613
|
+
import { open } from "fs/promises";
|
|
614
|
+
var MAX_FILE_BYTES = 1024 * 1024;
|
|
615
|
+
function timestamp(value) {
|
|
616
|
+
const match = /^(?:(\d{2,}):)?(\d{2}):(\d{2})[.,](\d{3})$/.exec(value);
|
|
617
|
+
if (!match || Number(match[2]) > 59 || Number(match[3]) > 59) {
|
|
618
|
+
throw new UsageError("Transcript cue times must use HH:MM:SS,mmm (SRT) or MM:SS.mmm / HH:MM:SS.mmm (VTT)");
|
|
619
|
+
}
|
|
620
|
+
return Number(match[1] ?? 0) * 3600 + Number(match[2]) * 60 + Number(match[3]) + Number(match[4]) / 1e3;
|
|
621
|
+
}
|
|
622
|
+
function subtitleSegments(text, vtt) {
|
|
623
|
+
if (vtt && !/^WEBVTT(?:\s|$)/.test(text)) throw new UsageError("VTT transcripts must begin with WEBVTT");
|
|
624
|
+
const segments = [];
|
|
625
|
+
for (const block of text.split(/\n[ \t]*\n/)) {
|
|
626
|
+
if (vtt && /^(WEBVTT(?:\s|$)|NOTE(?:\s|$)|STYLE(?:\s|$)|REGION(?:\s|$))/.test(block)) continue;
|
|
627
|
+
const lines = block.split("\n");
|
|
628
|
+
const timing = lines[0]?.includes("-->") ? 0 : 1;
|
|
629
|
+
const match = /^(\S+)\s+-->\s+(\S+)(?:\s+.*)?$/.exec(lines[timing] ?? "");
|
|
630
|
+
if (!match) throw new UsageError("Transcript contains an invalid SRT/VTT cue");
|
|
631
|
+
const entities = { amp: "&", lt: "<", gt: ">", quot: '"', apos: "'", nbsp: " " };
|
|
632
|
+
const cue = lines.slice(timing + 1).join("\n").replace(/<[^>]*>/g, "").replace(/&(amp|lt|gt|quot|apos|nbsp);/g, (_, name) => entities[name]);
|
|
633
|
+
segments.push({ startTimeSec: timestamp(match[1]), endTimeSec: timestamp(match[2]), text: cue });
|
|
634
|
+
}
|
|
635
|
+
return segments;
|
|
636
|
+
}
|
|
637
|
+
async function readClipTranscript(path) {
|
|
638
|
+
if (!/\.(json|srt|vtt)$/i.test(path)) throw new UsageError("--clip-transcript accepts a local .json, .srt, or .vtt file");
|
|
639
|
+
let data;
|
|
640
|
+
try {
|
|
641
|
+
const file = await open(path, "r");
|
|
642
|
+
try {
|
|
643
|
+
if (!(await file.stat()).isFile()) throw new UsageError("Clip transcript must be a regular file");
|
|
644
|
+
const buffer = Buffer.alloc(MAX_FILE_BYTES + 1);
|
|
645
|
+
let size = 0;
|
|
646
|
+
while (size < buffer.length) {
|
|
647
|
+
const read = await file.read(buffer, size, buffer.length - size, null);
|
|
648
|
+
if (!read.bytesRead) break;
|
|
649
|
+
size += read.bytesRead;
|
|
650
|
+
}
|
|
651
|
+
if (size > MAX_FILE_BYTES) throw new UsageError("Clip transcript file must be at most 1 MiB");
|
|
652
|
+
data = buffer.subarray(0, size);
|
|
653
|
+
} finally {
|
|
654
|
+
await file.close();
|
|
655
|
+
}
|
|
656
|
+
} catch (error) {
|
|
657
|
+
if (error instanceof UsageError) throw error;
|
|
658
|
+
throw new UsageError("Cannot read clip transcript file; check its path and permissions");
|
|
659
|
+
}
|
|
660
|
+
let text;
|
|
661
|
+
try {
|
|
662
|
+
text = new TextDecoder("utf-8", { fatal: true }).decode(data).replace(/^\uFEFF/, "").replace(/\r\n?/g, "\n").trim();
|
|
663
|
+
} catch {
|
|
664
|
+
throw new UsageError("Clip transcript must be valid UTF-8 text");
|
|
665
|
+
}
|
|
666
|
+
let segments;
|
|
667
|
+
if (/\.json$/i.test(path)) {
|
|
668
|
+
let value;
|
|
669
|
+
try {
|
|
670
|
+
value = JSON.parse(text);
|
|
671
|
+
} catch {
|
|
672
|
+
throw new UsageError("Clip transcript must be valid JSON");
|
|
673
|
+
}
|
|
674
|
+
segments = Array.isArray(value) ? value : value && typeof value === "object" && "transcript" in value ? value.transcript : void 0;
|
|
675
|
+
} else segments = subtitleSegments(text, /\.vtt$/i.test(path));
|
|
676
|
+
if (!Array.isArray(segments) || !segments.length || segments.length > 2e3) {
|
|
677
|
+
throw new UsageError("Clip transcript must contain 1 to 2000 segments");
|
|
678
|
+
}
|
|
679
|
+
let previous = -1;
|
|
680
|
+
let totalBytes = 0;
|
|
681
|
+
return segments.map((value) => {
|
|
682
|
+
const segment = value && typeof value === "object" ? value : {};
|
|
683
|
+
const start = segment.start_time_sec ?? segment.startTimeSec;
|
|
684
|
+
const end = segment.end_time_sec ?? segment.endTimeSec;
|
|
685
|
+
const cue = segment.text;
|
|
686
|
+
if (typeof start !== "number" || !Number.isFinite(start) || typeof end !== "number" || !Number.isFinite(end) || start < 0 || end <= start || end > 604800 || start < previous) {
|
|
687
|
+
throw new UsageError("Transcript segments require ordered finite source times with end after start (within 604800 seconds)");
|
|
688
|
+
}
|
|
689
|
+
if (typeof cue !== "string" || !cue.trim() || Buffer.byteLength(cue, "utf8") > 2e3) {
|
|
690
|
+
throw new UsageError("Transcript segments require nonblank text of at most 2000 UTF-8 bytes");
|
|
691
|
+
}
|
|
692
|
+
previous = start;
|
|
693
|
+
totalBytes += Buffer.byteLength(cue, "utf8");
|
|
694
|
+
if (totalBytes > 256 * 1024) throw new UsageError("Transcript text exceeds 256 KiB");
|
|
695
|
+
return { startTimeSec: start, endTimeSec: end, text: cue.trim() };
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
|
|
611
699
|
// src/commands/run.ts
|
|
612
700
|
var UNSUCCESSFUL_TERMINAL_STATUSES2 = /* @__PURE__ */ new Set(["FAILED", "REJECTED", "PARTIAL"]);
|
|
613
701
|
var OUTPUT_ALIASES = /* @__PURE__ */ new Set([
|
|
@@ -655,6 +743,7 @@ function parseRunOptions(args) {
|
|
|
655
743
|
let metadata;
|
|
656
744
|
let recipe;
|
|
657
745
|
let idempotencyKey;
|
|
746
|
+
let deliverWebhook;
|
|
658
747
|
let wait = false;
|
|
659
748
|
let timeoutMs;
|
|
660
749
|
let download;
|
|
@@ -665,6 +754,9 @@ function parseRunOptions(args) {
|
|
|
665
754
|
const contactSheet = {};
|
|
666
755
|
const image = {};
|
|
667
756
|
const audiogram = {};
|
|
757
|
+
const clip = {};
|
|
758
|
+
const clipAnalysis = {};
|
|
759
|
+
let clipTranscriptFile;
|
|
668
760
|
const privacyRedaction = {};
|
|
669
761
|
const privacyDetectors = [];
|
|
670
762
|
for (let index = 0; index < args.length; index += 1) {
|
|
@@ -684,6 +776,8 @@ function parseRunOptions(args) {
|
|
|
684
776
|
} else if (argument === "--idempotency-key") {
|
|
685
777
|
idempotencyKey = optionValue2(args, index, argument);
|
|
686
778
|
index += 1;
|
|
779
|
+
} else if (argument === "--no-webhook") {
|
|
780
|
+
deliverWebhook = false;
|
|
687
781
|
} else if (argument === "--wait") {
|
|
688
782
|
wait = true;
|
|
689
783
|
} else if (argument === "--timeout-ms") {
|
|
@@ -794,6 +888,36 @@ function parseRunOptions(args) {
|
|
|
794
888
|
} else if (argument === "--image-min-quality") {
|
|
795
889
|
image.minQuality = boundedNumber(args, index, argument, 1, 100, true);
|
|
796
890
|
index += 1;
|
|
891
|
+
} else if (argument === "--clip-start") {
|
|
892
|
+
clip.startTimeSec = boundedNumber(args, index, argument, 0, 604800);
|
|
893
|
+
index += 1;
|
|
894
|
+
} else if (argument === "--clip-duration") {
|
|
895
|
+
clip.durationSec = boundedNumber(args, index, argument, 0.1, 300);
|
|
896
|
+
index += 1;
|
|
897
|
+
} else if (argument === "--clip-layout") {
|
|
898
|
+
const value = optionValue2(args, index, argument);
|
|
899
|
+
if (value !== "original" && value !== "vertical_blur") {
|
|
900
|
+
throw new UsageError("--clip-layout must be original or vertical_blur");
|
|
901
|
+
}
|
|
902
|
+
clip.layout = value;
|
|
903
|
+
index += 1;
|
|
904
|
+
} else if (argument === "--clip-captions") {
|
|
905
|
+
clip.burnCaptions = true;
|
|
906
|
+
} else if (argument === "--clip-transcript") {
|
|
907
|
+
clipTranscriptFile = optionValue2(args, index, argument);
|
|
908
|
+
index += 1;
|
|
909
|
+
} else if (argument === "--clip-min-duration") {
|
|
910
|
+
clipAnalysis.minDurationSec = boundedNumber(args, index, argument, 1, 300);
|
|
911
|
+
index += 1;
|
|
912
|
+
} else if (argument === "--clip-max-duration") {
|
|
913
|
+
clipAnalysis.maxDurationSec = boundedNumber(args, index, argument, 1, 300);
|
|
914
|
+
index += 1;
|
|
915
|
+
} else if (argument === "--clip-count") {
|
|
916
|
+
clipAnalysis.maxCandidates = boundedNumber(args, index, argument, 1, 20, true);
|
|
917
|
+
index += 1;
|
|
918
|
+
} else if (argument === "--clip-keyword") {
|
|
919
|
+
(clipAnalysis.keywords ??= []).push(optionValue2(args, index, argument));
|
|
920
|
+
index += 1;
|
|
797
921
|
} else if (argument === "--audiogram-artwork") {
|
|
798
922
|
audiogram.artworkSource = optionValue2(args, index, argument);
|
|
799
923
|
index += 1;
|
|
@@ -994,6 +1118,33 @@ function parseRunOptions(args) {
|
|
|
994
1118
|
}
|
|
995
1119
|
}
|
|
996
1120
|
const hasPrivacyOptions = privacyDetectors.length > 0 || Object.keys(privacyRedaction).length > 0;
|
|
1121
|
+
const clipPreset = outputs.length === 1 && outputs[0]?.kind === "preset" ? outputs[0].value : void 0;
|
|
1122
|
+
const hasClip = Object.keys(clip).length > 0 || clipPreset === "video_clip_v1";
|
|
1123
|
+
const hasClipAnalysis = Object.keys(clipAnalysis).length > 0 || clipPreset === "clip_candidates_v1";
|
|
1124
|
+
if (hasClip && (recipe || clipPreset !== "video_clip_v1" || hasClipAnalysis)) {
|
|
1125
|
+
throw new UsageError("clip render options require exactly one video_clip_v1 --preset");
|
|
1126
|
+
}
|
|
1127
|
+
if (hasClip && (clip.startTimeSec === void 0 || clip.durationSec === void 0)) {
|
|
1128
|
+
throw new UsageError("video_clip_v1 requires --clip-start and --clip-duration");
|
|
1129
|
+
}
|
|
1130
|
+
if (hasClipAnalysis && (recipe || clipPreset !== "clip_candidates_v1")) {
|
|
1131
|
+
throw new UsageError("clip analysis options require exactly one clip_candidates_v1 --preset");
|
|
1132
|
+
}
|
|
1133
|
+
if ((clipAnalysis.minDurationSec ?? 15) > (clipAnalysis.maxDurationSec ?? 60)) {
|
|
1134
|
+
throw new UsageError("--clip-min-duration must not exceed --clip-max-duration");
|
|
1135
|
+
}
|
|
1136
|
+
if ((clipAnalysis.keywords?.length ?? 0) > 20) {
|
|
1137
|
+
throw new UsageError("At most 20 --clip-keyword values are supported");
|
|
1138
|
+
}
|
|
1139
|
+
if (clipAnalysis.keywords?.some((keyword) => !keyword.trim() || Buffer.byteLength(keyword, "utf8") > 100)) {
|
|
1140
|
+
throw new UsageError("Each --clip-keyword must contain 1 to 100 UTF-8 bytes of nonblank text");
|
|
1141
|
+
}
|
|
1142
|
+
if (clipTranscriptFile && !hasClip && !hasClipAnalysis) {
|
|
1143
|
+
throw new UsageError("--clip-transcript requires a clipping preset");
|
|
1144
|
+
}
|
|
1145
|
+
if (clip.burnCaptions && !clipTranscriptFile) {
|
|
1146
|
+
throw new UsageError("--clip-captions requires --clip-transcript with source timestamps");
|
|
1147
|
+
}
|
|
997
1148
|
if (hasPrivacyOptions) {
|
|
998
1149
|
if (recipe) throw new UsageError("privacy-redaction options cannot be combined with --recipe");
|
|
999
1150
|
if (privacyDetectors.length === 0) {
|
|
@@ -1012,6 +1163,7 @@ function parseRunOptions(args) {
|
|
|
1012
1163
|
...recipe === void 0 ? {} : { recipe },
|
|
1013
1164
|
...metadata === void 0 ? {} : { metadata },
|
|
1014
1165
|
...idempotencyKey === void 0 ? {} : { idempotencyKey },
|
|
1166
|
+
...deliverWebhook === void 0 ? {} : { deliverWebhook },
|
|
1015
1167
|
wait,
|
|
1016
1168
|
...timeoutMs === void 0 ? {} : { timeoutMs },
|
|
1017
1169
|
...download === void 0 ? {} : { download },
|
|
@@ -1021,6 +1173,9 @@ function parseRunOptions(args) {
|
|
|
1021
1173
|
...hasPlaceholderOptions ? { placeholders } : {},
|
|
1022
1174
|
...hasContactSheetOptions ? { contactSheet } : {},
|
|
1023
1175
|
...hasImageOptions ? { image } : {},
|
|
1176
|
+
...hasClip ? { clip } : {},
|
|
1177
|
+
...hasClipAnalysis ? { clipAnalysis } : {},
|
|
1178
|
+
...clipTranscriptFile ? { clipTranscriptFile } : {},
|
|
1024
1179
|
...hasAudiogramOptions || audiogramSelections.length > 0 ? { audiogram } : {},
|
|
1025
1180
|
...hasPrivacyOptions ? { privacyRedaction } : {}
|
|
1026
1181
|
};
|
|
@@ -1047,7 +1202,7 @@ async function resolveAudiogram(value, uploads) {
|
|
|
1047
1202
|
...captionsSource === void 0 ? {} : { captionsSource, burnCaptions: true }
|
|
1048
1203
|
};
|
|
1049
1204
|
}
|
|
1050
|
-
async function resolveOutputs(selections, capabilitiesClient, animation, placeholders, contactSheet, image, audiogram, privacyRedaction) {
|
|
1205
|
+
async function resolveOutputs(selections, capabilitiesClient, animation, placeholders, contactSheet, image, audiogram, privacyRedaction, clip, clipAnalysis) {
|
|
1051
1206
|
if (!privacyRedaction && !selections.some((selection) => selection.kind === "preset")) {
|
|
1052
1207
|
return selections.map((selection) => selection.value);
|
|
1053
1208
|
}
|
|
@@ -1084,6 +1239,8 @@ async function resolveOutputs(selections, capabilitiesClient, animation, placeho
|
|
|
1084
1239
|
}
|
|
1085
1240
|
if (audiogram) output3.audiogram = audiogram;
|
|
1086
1241
|
if (privacyRedaction) output3.privacyRedaction = privacyRedaction;
|
|
1242
|
+
if (clip) output3.clip = clip;
|
|
1243
|
+
if (clipAnalysis) output3.clipAnalysis = clipAnalysis;
|
|
1087
1244
|
return output3;
|
|
1088
1245
|
});
|
|
1089
1246
|
}
|
|
@@ -1103,6 +1260,7 @@ function detailsProjection2(job) {
|
|
|
1103
1260
|
id: job.id,
|
|
1104
1261
|
status: job.status,
|
|
1105
1262
|
tier: job.tier,
|
|
1263
|
+
...job.deliverWebhook === void 0 ? {} : { deliverWebhook: job.deliverWebhook },
|
|
1106
1264
|
usage: job.usage,
|
|
1107
1265
|
billing: job.billing,
|
|
1108
1266
|
bundle: {
|
|
@@ -1146,6 +1304,14 @@ async function runCommand(args, dependencies) {
|
|
|
1146
1304
|
isLocalSource || hasLocalAudiogramAsset ? "Uploading local media and creating job\u2026" : "Creating job\u2026"
|
|
1147
1305
|
);
|
|
1148
1306
|
try {
|
|
1307
|
+
if ((options.clip || options.clipAnalysis) && !("getClipCandidates" in dependencies.jobs)) {
|
|
1308
|
+
throw new UsageError("Clipping requires the MediaRuntime Node SDK release with getClipCandidates support");
|
|
1309
|
+
}
|
|
1310
|
+
if (options.clipTranscriptFile) {
|
|
1311
|
+
const transcript = await readClipTranscript(options.clipTranscriptFile);
|
|
1312
|
+
if (options.clip) options.clip.transcript = transcript;
|
|
1313
|
+
if (options.clipAnalysis) options.clipAnalysis.transcript = transcript;
|
|
1314
|
+
}
|
|
1149
1315
|
const audiogram = options.audiogram === void 0 ? void 0 : await resolveAudiogram(options.audiogram, dependencies.uploads);
|
|
1150
1316
|
const outputs = options.recipe ? void 0 : await resolveOutputs(
|
|
1151
1317
|
options.outputs,
|
|
@@ -1155,14 +1321,18 @@ async function runCommand(args, dependencies) {
|
|
|
1155
1321
|
options.contactSheet,
|
|
1156
1322
|
options.image,
|
|
1157
1323
|
audiogram,
|
|
1158
|
-
options.privacyRedaction
|
|
1324
|
+
options.privacyRedaction,
|
|
1325
|
+
options.clip,
|
|
1326
|
+
options.clipAnalysis
|
|
1159
1327
|
);
|
|
1160
1328
|
const params = {
|
|
1161
1329
|
source: options.source,
|
|
1162
1330
|
...outputs === void 0 ? {} : { outputs },
|
|
1163
1331
|
...options.recipe === void 0 ? {} : { recipe: options.recipe },
|
|
1164
1332
|
...options.metadata === void 0 ? {} : { metadata: options.metadata },
|
|
1165
|
-
...options.idempotencyKey === void 0 ? {} : { idempotencyKey: options.idempotencyKey }
|
|
1333
|
+
...options.idempotencyKey === void 0 ? {} : { idempotencyKey: options.idempotencyKey },
|
|
1334
|
+
// Omit by default so normal account webhook delivery stays unchanged.
|
|
1335
|
+
...options.deliverWebhook === void 0 ? {} : { deliverWebhook: options.deliverWebhook }
|
|
1166
1336
|
};
|
|
1167
1337
|
const submitted = await dependencies.jobs.create(params);
|
|
1168
1338
|
if (!options.wait) {
|
|
@@ -2032,7 +2202,7 @@ async function runTriggerCommand(args, dependencies = {}) {
|
|
|
2032
2202
|
writeStdout: dependencies.writeStdout ?? ((text) => process.stdout.write(text))
|
|
2033
2203
|
};
|
|
2034
2204
|
const secret = await resolveSecret(parsed, deps);
|
|
2035
|
-
const
|
|
2205
|
+
const timestamp2 = Math.floor(deps.now() / 1e3);
|
|
2036
2206
|
const unique = deps.randomUUID();
|
|
2037
2207
|
const eventId = `evt_cli_${unique}`;
|
|
2038
2208
|
const jobId = parsed.jobId ?? `job_cli_${unique}`;
|
|
@@ -2041,9 +2211,9 @@ async function runTriggerCommand(args, dependencies = {}) {
|
|
|
2041
2211
|
eventId,
|
|
2042
2212
|
jobId,
|
|
2043
2213
|
accountId,
|
|
2044
|
-
timestamp
|
|
2214
|
+
timestamp: timestamp2
|
|
2045
2215
|
});
|
|
2046
|
-
const signed = signSyntheticWebhook(payload, { eventId, timestamp, secret });
|
|
2216
|
+
const signed = signSyntheticWebhook(payload, { eventId, timestamp: timestamp2, secret });
|
|
2047
2217
|
const response = await deps.fetch(parsed.destination, {
|
|
2048
2218
|
method: "POST",
|
|
2049
2219
|
headers: signed.headers,
|
|
@@ -2117,11 +2287,12 @@ function createActivityIndicator(write, enabled) {
|
|
|
2117
2287
|
}
|
|
2118
2288
|
|
|
2119
2289
|
// src/cli.ts
|
|
2120
|
-
var VERSION = "1.
|
|
2290
|
+
var VERSION = "1.5.0";
|
|
2121
2291
|
var HELP = `MediaRuntime CLI
|
|
2122
2292
|
|
|
2123
2293
|
Usage:
|
|
2124
|
-
mediaruntime run <source> (--recipe <name[@version]> | --output <alias> | --preset <name>) [...] [--wait]
|
|
2294
|
+
mediaruntime run <source> (--recipe <name[@version]> | --output <alias> | --preset <name>) [...] [--no-webhook] [--wait]
|
|
2295
|
+
--no-webhook disables callbacks for this job; normal billing applies.
|
|
2125
2296
|
Animation presets: [--animation-width N] [--animation-fps N] [--animation-start SEC]
|
|
2126
2297
|
[--animation-duration SEC] [--animation-loop N] [--animation-quality N]
|
|
2127
2298
|
Placeholder preset: [--placeholder-max-dimension N] [--placeholder-time SEC]
|
|
@@ -2133,6 +2304,11 @@ Usage:
|
|
|
2133
2304
|
[--image-width PX] [--image-height PX] [--image-mode fit|fill|cover|contain]
|
|
2134
2305
|
[--image-format jpg|png|webp|avif] [--image-quality N]
|
|
2135
2306
|
[--image-max-bytes N] [--image-min-quality N]
|
|
2307
|
+
Clip render: --preset video_clip_v1 --clip-start SEC --clip-duration SEC
|
|
2308
|
+
[--clip-layout original|vertical_blur] [--clip-transcript <plan.json|captions.srt|captions.vtt>] [--clip-captions]
|
|
2309
|
+
Clip suggestions: --preset clip_candidates_v1 [--clip-min-duration SEC]
|
|
2310
|
+
[--clip-max-duration SEC] [--clip-count 1..20] [--clip-keyword TEXT]
|
|
2311
|
+
[--clip-transcript <plan.json|captions.srt|captions.vtt>]
|
|
2136
2312
|
Audiogram preset: --audiogram-artwork <source> [--audiogram-captions <source>]
|
|
2137
2313
|
[--audiogram-layout square|portrait|landscape]
|
|
2138
2314
|
[--audiogram-fit contain|cover|blurred_background]
|