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.
@@ -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.1",
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
- if (!prompt && !last(flags.video)) die("video requires a prompt: ottoport video \"a drone shot\" (or --video <url> on a depth model)");
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: last(flags.model) || "kling-3.0",
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
- if (url && rest.draft) return `${url}\nDraft ${job.id} — complete it at 1080p with draft_id "${job.id}" within seven days.`;
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: "Seedance 2.5 only: 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`." },
484
- draft_id: { type: "string", description: "Seedance 2.5 only: the job id of a finished draft, to render that same shot at 1080p. Send `model` and this alone — prompt, inputs and duration come from the draft. Within seven days of the draft." },
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.1",
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": {
@@ -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`. |