ottoport 1.13.1 → 1.13.2
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/.claude-plugin/plugin.json +1 -1
- package/cli/ottoport.mjs +20 -4
- package/commands/video.md +5 -0
- package/mcp/server.mjs +5 -4
- package/package.json +1 -1
- package/skills/ottoport/SKILL.md +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ottoport",
|
|
3
3
|
"description": "One API for every model. Call chat, image, video, speech and music models through the OttoPort gateway — as MCP tools, slash commands, or the bundled CLI.",
|
|
4
|
-
"version": "1.13.
|
|
4
|
+
"version": "1.13.2",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "LITBOX LLC",
|
|
7
7
|
"email": "support@ottoport.ai"
|
package/cli/ottoport.mjs
CHANGED
|
@@ -24,6 +24,9 @@ import { readFile, stat, writeFile } from "node:fs/promises";
|
|
|
24
24
|
const BASE = "https://ottoport.ai";
|
|
25
25
|
|
|
26
26
|
// ── arg parsing ──────────────────────────────────────────────────────
|
|
27
|
+
// Switches that never take a value, so `--draft "a boat"` keeps its prompt.
|
|
28
|
+
const BOOLEAN_FLAGS = new Set(["draft"]);
|
|
29
|
+
|
|
27
30
|
function parseArgs(argv) {
|
|
28
31
|
const positional = [];
|
|
29
32
|
const flags = {};
|
|
@@ -33,7 +36,7 @@ function parseArgs(argv) {
|
|
|
33
36
|
const key = a.slice(2);
|
|
34
37
|
if (key.startsWith("no-")) {
|
|
35
38
|
flags[key.slice(3)] = false;
|
|
36
|
-
} else if (i + 1 < argv.length && !argv[i + 1].startsWith("--")) {
|
|
39
|
+
} else if (!BOOLEAN_FLAGS.has(key) && i + 1 < argv.length && !argv[i + 1].startsWith("--")) {
|
|
37
40
|
const value = argv[++i];
|
|
38
41
|
// Repeats accumulate rather than overwrite, so `--ref a --ref b` keeps
|
|
39
42
|
// both. Everything downstream reads the last value of a scalar flag,
|
|
@@ -297,10 +300,14 @@ async function cmdImage(prompt, flags) {
|
|
|
297
300
|
|
|
298
301
|
async function cmdVideo(prompt, flags) {
|
|
299
302
|
// A depth model needs only --video; the gateway refuses a prompt there.
|
|
300
|
-
|
|
303
|
+
// A draft's 1080p render takes everything from the draft, the prompt included.
|
|
304
|
+
const draftId = last(flags["draft-id"]);
|
|
305
|
+
if (!prompt && !last(flags.video) && !draftId) die("video requires a prompt: ottoport video \"a drone shot\" (or --video <url> on a depth model)");
|
|
301
306
|
const { baseUrl, apiKey } = config(flags);
|
|
307
|
+
// Drafts are Seedance 2.5's, so a draft with no model named is one of its.
|
|
308
|
+
const model = last(flags.model) || (flags.draft === true || draftId ? "seedance-2.5" : "kling-3.0");
|
|
302
309
|
const body = {
|
|
303
|
-
model
|
|
310
|
+
model,
|
|
304
311
|
...(prompt ? { prompt } : {}),
|
|
305
312
|
...(last(flags.video) ? { video_url: last(flags.video) } : {}),
|
|
306
313
|
...(last(flags.duration) ? { duration: Number(last(flags.duration)) } : {}),
|
|
@@ -312,6 +319,8 @@ async function cmdVideo(prompt, flags) {
|
|
|
312
319
|
...(references(flags) ? { reference_image_urls: references(flags) } : {}),
|
|
313
320
|
...(references(flags, "ref-video") ? { reference_video_urls: references(flags, "ref-video") } : {}),
|
|
314
321
|
...(references(flags, "ref-audio") ? { reference_audio_urls: references(flags, "ref-audio") } : {}),
|
|
322
|
+
...(flags.draft === true ? { draft: true } : {}),
|
|
323
|
+
...(draftId ? { draft_id: draftId } : {}),
|
|
315
324
|
...webhook(flags),
|
|
316
325
|
...collection(flags),
|
|
317
326
|
};
|
|
@@ -338,6 +347,10 @@ async function cmdVideo(prompt, flags) {
|
|
|
338
347
|
const url = job.data?.[0]?.url;
|
|
339
348
|
if (url) {
|
|
340
349
|
console.log(outputLine(job.data[0]));
|
|
350
|
+
// The draft's job id is what renders it in 1080p, so say it.
|
|
351
|
+
if (!draftId && (body.draft || model.endsWith("-draft"))) {
|
|
352
|
+
process.stderr.write(`draft ${job.id} — render it in 1080p within seven days: ottoport video --model ${model} --draft-id ${job.id}\n`);
|
|
353
|
+
}
|
|
341
354
|
if (flags.out) await download(url, last(flags.out));
|
|
342
355
|
} else {
|
|
343
356
|
console.log(JSON.stringify(job, null, 2));
|
|
@@ -475,8 +488,11 @@ Usage:
|
|
|
475
488
|
[--aspect-ratio 16:9] [--seed 7] [--image <url>]
|
|
476
489
|
[--last-frame <url>] [--ref <url>[,<url>…]]
|
|
477
490
|
[--ref-video <url>[,<url>…]] [--ref-audio <url>[,<url>…]]
|
|
478
|
-
[--webhook-url https://…] [--webhook-secret <s>]
|
|
491
|
+
[--draft] [--webhook-url https://…] [--webhook-secret <s>]
|
|
479
492
|
[--no-wait] [--out clip.mp4]
|
|
493
|
+
ottoport video --draft-id <job-id> [--model seedance-2.5] [--out clip.mp4]
|
|
494
|
+
render a Seedance 2.5 draft (--draft, or the
|
|
495
|
+
seedance-2.5-draft model) in 1080p, within 7 days
|
|
480
496
|
ottoport video --model video-depth-anything --video <url> [--out depth.mp4]
|
|
481
497
|
turn a video (≤60s) into a grayscale depth video
|
|
482
498
|
ottoport webhook status <job-id> [--json] delivery attempts and responses
|
package/commands/video.md
CHANGED
|
@@ -22,6 +22,11 @@ Request: $ARGUMENTS
|
|
|
22
22
|
discovering it from a failed call.
|
|
23
23
|
- `resolution` is billed: a 1080p or 4k clip costs more per second than the
|
|
24
24
|
model's base tier. Send one only when the request asks for it.
|
|
25
|
+
- When the user wants to preview before paying for the full clip, draft it:
|
|
26
|
+
`model: "seedance-2.5-draft"` (or `seedance-2.5` with `draft: true`) renders
|
|
27
|
+
a cheap 480p take. To keep it, call again with the same model and
|
|
28
|
+
`draft_id` set to the draft's job id — nothing else; it renders that shot in
|
|
29
|
+
1080p, within seven days.
|
|
25
30
|
- Video generation takes a minute or more and the call blocks until the job is
|
|
26
31
|
terminal. Say so before you start, and do not retry a call that is merely slow
|
|
27
32
|
— a retry is a second billable generation.
|
package/mcp/server.mjs
CHANGED
|
@@ -358,7 +358,8 @@ async function generateVideo({ prompt, model, webhook_url, webhook_secret, colle
|
|
|
358
358
|
const output = job.data?.[0];
|
|
359
359
|
const url = output?.url ? outputLine(output) : undefined;
|
|
360
360
|
// The draft's job id is what completes it, so it is worth saying.
|
|
361
|
-
|
|
361
|
+
// `seedance-2.5-draft` drafts without being told to.
|
|
362
|
+
if (url && !rest.draft_id && (rest.draft || /-draft$/.test(model ?? ""))) return `${url}\nDraft ${job.id} — complete it at 1080p with draft_id "${job.id}" within seven days.`;
|
|
362
363
|
return url || JSON.stringify(job);
|
|
363
364
|
}
|
|
364
365
|
|
|
@@ -458,7 +459,7 @@ const TOOLS = [
|
|
|
458
459
|
},
|
|
459
460
|
{
|
|
460
461
|
name: "ottoport_generate_video",
|
|
461
|
-
description: "Generate a video through an OttoPort video model (Veo, Kling, Seedance, …). Modes: text-to-video, image-to-video (`image_url`), first-and-last-frame (plus `last_frame_url`), reference-to-video (`reference_image_urls`, plus `reference_video_urls` / `reference_audio_urls` on models whose capabilities list `referenceMedia`), and on some models video edit/extend (`video_url`). Seedance 2.5 also drafts: `draft: true` renders a cheap 480p preview, and `draft_id` set to that job's id renders the same shot at 1080p. video-depth-anything instead turns `video_url` into a grayscale depth video and takes no prompt. Kling motion control (`kling-2.6-motion-control`, `kling-3.0-motion-control` and their `-pro` variants) makes the character in `image_url` perform the movement in `video_url`; the prompt is optional and the output runs as long as the clip. Which a model accepts is in its capabilities — call ottoport_list_models. Blocks on the queue and returns a video URL, unless `webhook_url` is given.",
|
|
462
|
+
description: "Generate a video through an OttoPort video model (Veo, Kling, Seedance, …). Modes: text-to-video, image-to-video (`image_url`), first-and-last-frame (plus `last_frame_url`), reference-to-video (`reference_image_urls`, plus `reference_video_urls` / `reference_audio_urls` on models whose capabilities list `referenceMedia`), and on some models video edit/extend (`video_url`). Seedance 2.5 also drafts: `draft: true` (or the model `seedance-2.5-draft`, where every request is a draft) renders a cheap 480p preview, and `draft_id` set to that job's id renders the same shot at 1080p. video-depth-anything instead turns `video_url` into a grayscale depth video and takes no prompt. Kling motion control (`kling-2.6-motion-control`, `kling-3.0-motion-control` and their `-pro` variants) makes the character in `image_url` perform the movement in `video_url`; the prompt is optional and the output runs as long as the clip. Which a model accepts is in its capabilities — call ottoport_list_models. Blocks on the queue and returns a video URL, unless `webhook_url` is given.",
|
|
462
463
|
inputSchema: {
|
|
463
464
|
type: "object",
|
|
464
465
|
properties: {
|
|
@@ -480,8 +481,8 @@ const TOOLS = [
|
|
|
480
481
|
character_orientation: { type: "string", enum: ["video", "image"], description: "Motion-control models only: whether the character faces the way the person in `video_url` does (\"video\", the default) or keeps the pose of `image_url` (\"image\")." },
|
|
481
482
|
keep_original_sound: { type: "boolean", description: "Motion-control models only: keep the audio of `video_url` in the result." },
|
|
482
483
|
video_task: { type: "string", enum: ["edit", "extend"], description: "edit (default) restyles video_url; extend continues it for `duration` more seconds." },
|
|
483
|
-
draft: { type: "boolean", description: "
|
|
484
|
-
draft_id: { type: "string", description: "
|
|
484
|
+
draft: { type: "boolean", description: "seedance-2.5 only (implied on seedance-2.5-draft): render a 480p preview at about a quarter of the 1080p price, to check the shot before paying for it. Text, image and reference-image requests; no `resolution`." },
|
|
485
|
+
draft_id: { type: "string", description: "seedance-2.5 or seedance-2.5-draft: the job id of a finished draft, to render that same shot at 1080p — either id completes a draft made with the other. Send `model` and this alone — prompt, inputs and duration come from the draft. Within seven days of the draft." },
|
|
485
486
|
webhook_url: { type: "string", description: "Public HTTPS endpoint to POST the finished job to (events video.completed / video.failed, signed with HMAC-SHA256). With it the tool returns the job id at once instead of waiting." },
|
|
486
487
|
webhook_secret: { type: "string", description: "Signing secret for webhook_url. Omit to use the server's OTTOPORT_WEBHOOK_SECRET." },
|
|
487
488
|
collection: { type: "string", description: "Save the result for good into this collection — its id or exact name (see ottoport_list_collections). Without it the file expires after a few days. Needs collections unlocked on the account. Defaults to OTTOPORT_COLLECTION when set." },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ottoport",
|
|
3
|
-
"version": "1.13.
|
|
3
|
+
"version": "1.13.2",
|
|
4
4
|
"description": "Claude Code plugin, CLI and MCP server for OttoPort — one OpenAI-compatible API for every LLM, image, video, and speech model.",
|
|
5
5
|
"homepage": "https://ottoport.ai",
|
|
6
6
|
"repository": {
|
package/skills/ottoport/SKILL.md
CHANGED
|
@@ -17,7 +17,7 @@ and do not call the HTTP API, when a tool covers the job:
|
|
|
17
17
|
| `ottoport_list_models` | The catalog: what each model is for, and its rate in credits and USD. Filter with `modality`, or pass `model` for ONE model in full — its modes, its resolution tiers with prices, and every parameter it reads. |
|
|
18
18
|
| `ottoport_chat` | `prompt`, plus optional `model`, `system`, `temperature`, `max_tokens`. |
|
|
19
19
|
| `ottoport_generate_image` | `prompt`, plus optional `model`, `size` or `resolution`, `quality` (GPT Image and Ideogram), `aspect_ratio`, `n`, `seed`, `image_url` (one reference), `reference_image_urls` (several). Layer decomposition too: `model` `qwen-image-layered` or `seedream-5.0-layers` with `image_url` alone (prompt optional, `num_layers` on Qwen) returns one URL per RGBA layer. |
|
|
20
|
-
| `ottoport_generate_video` | `prompt`, plus optional `model`, `duration`, `resolution`, `aspect_ratio`, `seed`, `image_url` (first frame), `last_frame_url`, `reference_image_urls`, `reference_video_urls` / `reference_audio_urls` (models listing `referenceMedia`), `video_url` (edit / extend, or the movement for motion control), `character_orientation` / `keep_original_sound` (motion control), `draft` / `draft_id` (Seedance 2.5: a 480p preview, then the same shot at 1080p from the preview's job id), `webhook_url` (returns the job id at once and POSTs the result there; the secret comes from `webhook_secret` or the server's `OTTOPORT_WEBHOOK_SECRET`). |
|
|
20
|
+
| `ottoport_generate_video` | `prompt`, plus optional `model`, `duration`, `resolution`, `aspect_ratio`, `seed`, `image_url` (first frame), `last_frame_url`, `reference_image_urls`, `reference_video_urls` / `reference_audio_urls` (models listing `referenceMedia`), `video_url` (edit / extend, or the movement for motion control), `character_orientation` / `keep_original_sound` (motion control), `draft` / `draft_id` (Seedance 2.5: a 480p preview, then the same shot at 1080p from the preview's job id; the model `seedance-2.5-draft` drafts on every request without `draft`), `webhook_url` (returns the job id at once and POSTs the result there; the secret comes from `webhook_secret` or the server's `OTTOPORT_WEBHOOK_SECRET`). |
|
|
21
21
|
| `ottoport_video_webhook` | `job_id`, plus `action` `status` (every delivery attempt and the endpoint's response) or `retry` (redeliver a finished job's webhook now). |
|
|
22
22
|
| `ottoport_generate_speech` | `prompt` (the text), plus optional `model`, `voice`, `format`. |
|
|
23
23
|
| `ottoport_generate_music` | `prompt`, plus optional `model`, `duration`, `format`. |
|