videodraft 0.3.0 → 0.3.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/dist/client.d.ts +1 -1
- package/dist/client.js +20 -7
- package/dist/index.js +23 -10
- package/package.json +1 -1
- package/skills/videodraft/references/models.md +3 -3
package/dist/client.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ interface TokenProvider {
|
|
|
12
12
|
* Called once after a 401. Return a fresh token to retry with, or null to
|
|
13
13
|
* give up (the 401 then surfaces as AuthError).
|
|
14
14
|
*/
|
|
15
|
-
onUnauthorized?(): Promise<string | null>;
|
|
15
|
+
onUnauthorized?(failedToken?: string): Promise<string | null>;
|
|
16
16
|
}
|
|
17
17
|
interface VideoDraftClientOptions {
|
|
18
18
|
tokenProvider: TokenProvider;
|
package/dist/client.js
CHANGED
|
@@ -221,7 +221,7 @@ var VideoDraftClient = class {
|
|
|
221
221
|
const token = await this.tokenProvider.getAccessToken();
|
|
222
222
|
let response = await this.post(method, params, token);
|
|
223
223
|
if (response.status === 401 && this.tokenProvider.onUnauthorized) {
|
|
224
|
-
const fresh = await this.tokenProvider.onUnauthorized();
|
|
224
|
+
const fresh = await this.tokenProvider.onUnauthorized(token);
|
|
225
225
|
if (fresh) response = await this.post(method, params, fresh);
|
|
226
226
|
}
|
|
227
227
|
if (response.status === 401) {
|
|
@@ -284,7 +284,7 @@ var VideoDraftClient = class {
|
|
|
284
284
|
const token = await this.tokenProvider.getAccessToken();
|
|
285
285
|
let response = await doFetch(token);
|
|
286
286
|
if (response.status === 401 && this.tokenProvider.onUnauthorized) {
|
|
287
|
-
const fresh = await this.tokenProvider.onUnauthorized();
|
|
287
|
+
const fresh = await this.tokenProvider.onUnauthorized(token);
|
|
288
288
|
if (fresh) response = await doFetch(fresh);
|
|
289
289
|
}
|
|
290
290
|
if (response.status === 401) {
|
|
@@ -330,7 +330,7 @@ var VideoDraftClient = class {
|
|
|
330
330
|
});
|
|
331
331
|
let response = await post(token);
|
|
332
332
|
if (response.status === 401 && this.tokenProvider.onUnauthorized) {
|
|
333
|
-
const fresh = await this.tokenProvider.onUnauthorized();
|
|
333
|
+
const fresh = await this.tokenProvider.onUnauthorized(token);
|
|
334
334
|
if (fresh) response = await post(fresh);
|
|
335
335
|
}
|
|
336
336
|
if (response.status === 401) throw new AuthError("Token is invalid, expired, or revoked.");
|
|
@@ -622,6 +622,7 @@ async function refreshUnderLock(profileName, env) {
|
|
|
622
622
|
);
|
|
623
623
|
}
|
|
624
624
|
function profileTokenProvider(profileName, env = process.env) {
|
|
625
|
+
let lastAccessToken = null;
|
|
625
626
|
return {
|
|
626
627
|
async getAccessToken() {
|
|
627
628
|
const { profile } = getProfile(profileName, env);
|
|
@@ -629,18 +630,30 @@ function profileTokenProvider(profileName, env = process.env) {
|
|
|
629
630
|
if (profile.auth_kind === "oauth" && isExpiring(profile)) {
|
|
630
631
|
const fresh = await refreshUnderLock(profileName, env);
|
|
631
632
|
if (!fresh) throw new AuthError("Your session expired and could not be refreshed.");
|
|
633
|
+
lastAccessToken = fresh;
|
|
632
634
|
return fresh;
|
|
633
635
|
}
|
|
636
|
+
lastAccessToken = profile.access_token;
|
|
634
637
|
return profile.access_token;
|
|
635
638
|
},
|
|
636
|
-
async onUnauthorized() {
|
|
639
|
+
async onUnauthorized(failedToken) {
|
|
637
640
|
const { profile } = getProfile(profileName, env);
|
|
638
|
-
if (profile?.
|
|
641
|
+
if (!profile?.access_token) return null;
|
|
642
|
+
if (profile.auth_kind !== "oauth") {
|
|
643
|
+
const staleToken = failedToken ?? lastAccessToken;
|
|
644
|
+
if (profile.access_token !== staleToken) {
|
|
645
|
+
lastAccessToken = profile.access_token;
|
|
646
|
+
return profile.access_token;
|
|
647
|
+
}
|
|
648
|
+
return null;
|
|
649
|
+
}
|
|
639
650
|
updateConfig((config) => {
|
|
640
651
|
const p = config.profiles[profileName];
|
|
641
|
-
if (p) p.expires_at = (/* @__PURE__ */ new Date(0)).toISOString();
|
|
652
|
+
if (p?.auth_kind === "oauth") p.expires_at = (/* @__PURE__ */ new Date(0)).toISOString();
|
|
642
653
|
}, env);
|
|
643
|
-
|
|
654
|
+
const fresh = await refreshUnderLock(profileName, env);
|
|
655
|
+
if (fresh) lastAccessToken = fresh;
|
|
656
|
+
return fresh;
|
|
644
657
|
}
|
|
645
658
|
};
|
|
646
659
|
}
|
package/dist/index.js
CHANGED
|
@@ -24,8 +24,8 @@ function readVersionFromDisk() {
|
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
function resolveVersion() {
|
|
27
|
-
if ("0.3.
|
|
28
|
-
return "0.3.
|
|
27
|
+
if ("0.3.2") {
|
|
28
|
+
return "0.3.2";
|
|
29
29
|
}
|
|
30
30
|
return readVersionFromDisk();
|
|
31
31
|
}
|
|
@@ -648,7 +648,7 @@ var VideoDraftClient = class {
|
|
|
648
648
|
const token = await this.tokenProvider.getAccessToken();
|
|
649
649
|
let response = await this.post(method, params, token);
|
|
650
650
|
if (response.status === 401 && this.tokenProvider.onUnauthorized) {
|
|
651
|
-
const fresh = await this.tokenProvider.onUnauthorized();
|
|
651
|
+
const fresh = await this.tokenProvider.onUnauthorized(token);
|
|
652
652
|
if (fresh) response = await this.post(method, params, fresh);
|
|
653
653
|
}
|
|
654
654
|
if (response.status === 401) {
|
|
@@ -711,7 +711,7 @@ var VideoDraftClient = class {
|
|
|
711
711
|
const token = await this.tokenProvider.getAccessToken();
|
|
712
712
|
let response = await doFetch(token);
|
|
713
713
|
if (response.status === 401 && this.tokenProvider.onUnauthorized) {
|
|
714
|
-
const fresh = await this.tokenProvider.onUnauthorized();
|
|
714
|
+
const fresh = await this.tokenProvider.onUnauthorized(token);
|
|
715
715
|
if (fresh) response = await doFetch(fresh);
|
|
716
716
|
}
|
|
717
717
|
if (response.status === 401) {
|
|
@@ -757,7 +757,7 @@ var VideoDraftClient = class {
|
|
|
757
757
|
});
|
|
758
758
|
let response = await post(token);
|
|
759
759
|
if (response.status === 401 && this.tokenProvider.onUnauthorized) {
|
|
760
|
-
const fresh = await this.tokenProvider.onUnauthorized();
|
|
760
|
+
const fresh = await this.tokenProvider.onUnauthorized(token);
|
|
761
761
|
if (fresh) response = await post(fresh);
|
|
762
762
|
}
|
|
763
763
|
if (response.status === 401) throw new AuthError("Token is invalid, expired, or revoked.");
|
|
@@ -869,6 +869,7 @@ async function refreshUnderLock(profileName, env) {
|
|
|
869
869
|
);
|
|
870
870
|
}
|
|
871
871
|
function profileTokenProvider(profileName, env = process.env) {
|
|
872
|
+
let lastAccessToken = null;
|
|
872
873
|
return {
|
|
873
874
|
async getAccessToken() {
|
|
874
875
|
const { profile } = getProfile(profileName, env);
|
|
@@ -876,18 +877,30 @@ function profileTokenProvider(profileName, env = process.env) {
|
|
|
876
877
|
if (profile.auth_kind === "oauth" && isExpiring(profile)) {
|
|
877
878
|
const fresh = await refreshUnderLock(profileName, env);
|
|
878
879
|
if (!fresh) throw new AuthError("Your session expired and could not be refreshed.");
|
|
880
|
+
lastAccessToken = fresh;
|
|
879
881
|
return fresh;
|
|
880
882
|
}
|
|
883
|
+
lastAccessToken = profile.access_token;
|
|
881
884
|
return profile.access_token;
|
|
882
885
|
},
|
|
883
|
-
async onUnauthorized() {
|
|
886
|
+
async onUnauthorized(failedToken) {
|
|
884
887
|
const { profile } = getProfile(profileName, env);
|
|
885
|
-
if (profile?.
|
|
888
|
+
if (!profile?.access_token) return null;
|
|
889
|
+
if (profile.auth_kind !== "oauth") {
|
|
890
|
+
const staleToken = failedToken ?? lastAccessToken;
|
|
891
|
+
if (profile.access_token !== staleToken) {
|
|
892
|
+
lastAccessToken = profile.access_token;
|
|
893
|
+
return profile.access_token;
|
|
894
|
+
}
|
|
895
|
+
return null;
|
|
896
|
+
}
|
|
886
897
|
updateConfig((config) => {
|
|
887
898
|
const p = config.profiles[profileName];
|
|
888
|
-
if (p) p.expires_at = (/* @__PURE__ */ new Date(0)).toISOString();
|
|
899
|
+
if (p?.auth_kind === "oauth") p.expires_at = (/* @__PURE__ */ new Date(0)).toISOString();
|
|
889
900
|
}, env);
|
|
890
|
-
|
|
901
|
+
const fresh = await refreshUnderLock(profileName, env);
|
|
902
|
+
if (fresh) lastAccessToken = fresh;
|
|
903
|
+
return fresh;
|
|
891
904
|
}
|
|
892
905
|
};
|
|
893
906
|
}
|
|
@@ -1907,7 +1920,7 @@ function registerGenerateCommands(program) {
|
|
|
1907
1920
|
generate.command("video [prompt...]").description("Generate a video clip (async; per-second pricing \u2014 see --estimate)").option("--model <id>", "video model id (default google-veo3.1 fast)").option("--ar <ratio>", 'aspect ratio, e.g. "16:9", "9:16"').option("--duration <seconds>", "clip duration in seconds").option("--resolution <res>", 'e.g. "480p", "720p", "1080p", "4k"').option(
|
|
1908
1921
|
"--quality <tier>",
|
|
1909
1922
|
'e.g. "mini", "fast", "standard", "quality", "pro"'
|
|
1910
|
-
).option("--audio", "generate native model audio").option("--no-audio", "disable native model audio").option("--start-image <url|file>", "start frame (image-to-video)").option("--end-image <url|file>", "end frame (supported models only)").option("--ref <url|file>", "reference image (repeatable)", collect, []).option("--ref-video <url|file>", "reference video (repeatable; Seedance 2, Wan 2.7; local files uploaded)", collect, []).option("--ref-audio <url|file>", "reference audio (repeatable; Seedance 2; local files uploaded)", collect, []).option("--segment <prompt:seconds>", "multi-prompt segment (repeatable; Kling 3.0 / 3.0 Turbo / O3)", collect, []).option("--negative <text>", "negative prompt (Kling/Wan/Luma)").option("--seed <n>", "seed").option("--project <id>", "attach to a project").option("--session <id>", "AI Studio session id").option("--scene <n>", "0-based scene index").option("--shot <n>", "0-based shot index").option("--download <path>", "download outputs (template: {job_id} {index} {ext})").option("--no-wait", "submit and return the job id immediately").option("--estimate", "print the cost estimate and exit (spends nothing)").action(async function(promptWords = []) {
|
|
1923
|
+
).option("--audio", "generate native model audio").option("--no-audio", "disable native model audio").option("--start-image <url|file>", "start frame (image-to-video)").option("--end-image <url|file>", "end frame (supported models only)").option("--ref <url|file>", "reference image (repeatable)", collect, []).option("--ref-video <url|file>", "reference video (repeatable; Gemini Omni Flash, Seedance 2, Kling O3, Wan 2.7; local files uploaded)", collect, []).option("--ref-audio <url|file>", "reference audio (repeatable; Seedance 2; local files uploaded)", collect, []).option("--segment <prompt:seconds>", "multi-prompt segment (repeatable; Kling 3.0 / 3.0 Turbo / O3)", collect, []).option("--negative <text>", "negative prompt (Kling/Wan/Luma)").option("--seed <n>", "seed").option("--project <id>", "attach to a project").option("--session <id>", "AI Studio session id").option("--scene <n>", "0-based scene index").option("--shot <n>", "0-based shot index").option("--download <path>", "download outputs (template: {job_id} {index} {ext})").option("--no-wait", "submit and return the job id immediately").option("--estimate", "print the cost estimate and exit (spends nothing)").action(async function(promptWords = []) {
|
|
1911
1924
|
const ctx = buildContext(this);
|
|
1912
1925
|
const opts = this.opts();
|
|
1913
1926
|
const prompt = promptWords.join(" ").trim();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "videodraft",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "Official VideoDraft CLI — create AI videos, images and audio from your terminal. Agent-friendly: --json everywhere, stable exit codes, async job polling.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -12,8 +12,8 @@ videodraft models styles --json # visual style presets
|
|
|
12
12
|
|
|
13
13
|
## Defaults (safe starting points)
|
|
14
14
|
|
|
15
|
-
- **Image**: `nano-banana-2` (the platform default, 1K). Use `--num 1..4` for variations of one prompt in a single call — never loop for variations.
|
|
16
|
-
- **Video**: `google-veo3.1` at fast quality (6s / 720p) — the platform default.
|
|
15
|
+
- **Image**: `nano-banana-2` (the platform default, 1K, up to 14 reference images). Use `--num 1..4` for variations of one prompt in a single call — never loop for variations. `nano-banana-2-lite` is the fastest/cheapest Google direct image model (1K only, up to 14 reference images).
|
|
16
|
+
- **Video**: `google-veo3.1` at fast quality (6s / 720p) — the platform default. `gemini-omni-flash` is Google's any-to-any multimodal video model (text/image/video → video, auto or 3-10s, 720p, audio always on).
|
|
17
17
|
- **Voiceover**: ElevenLabs Brittney (default voice).
|
|
18
18
|
- **Music**: `lyria-3-clip-preview` (30s, cheap); `lyria-3-pro-preview` for 180s/quality; `elevenlabs-music` for music that can include vocals/lyrics.
|
|
19
19
|
- **ElevenLabs audio**: `generate sound-effect`, `generate dialogue`, `generate voice-changer`, and `generate dub` are synchronous audio/media calls. Voice changer and dubbing require the source media duration in seconds for billing and currently accept source media up to 300s.
|
|
@@ -24,7 +24,7 @@ videodraft models styles --json # visual style presets
|
|
|
24
24
|
- Most video models support only 16:9 / 9:16 / 1:1. A 3:4 request hard-fails on most.
|
|
25
25
|
- `--seed` reproduces a specific output on models that support it (e.g. Flux, Ideogram V4); everything else ignores it. You do not need a seed for variation — `--num` already varies.
|
|
26
26
|
- `--rendering-speed` applies to Ideogram (V3: `Default`/`Turbo`/`Quality`; V4: `Turbo`/`Balanced`/`Quality`) and affects image cost — pass it to `videodraft costs ... --rendering-speed <tier>` for an accurate estimate. Always trust `videodraft models image --json` over this list; new models and tiers appear there the moment the platform ships them, with no CLI update.
|
|
27
|
-
- Reference inputs: `--ref <img>` (images), `--ref-video <v>` (Seedance 2, Wan 2.7), `--ref-audio <a>` (Seedance 2). The CLI uploads local files for all of these, so you can pass a path or a URL. `--segment "<prompt>:<seconds>"` (repeatable) drives multi-prompt models (Kling 3.0 / 3.0 Turbo / O3); total 3-15s. `generate image --video-ref` is the nano-banana-2 video reference.
|
|
27
|
+
- Reference inputs: `--ref <img>` (images), `--ref-video <v>` (Gemini Omni Flash, Seedance 2, Kling O3, Wan 2.7), `--ref-audio <a>` (Seedance 2). The CLI uploads local files for all of these, so you can pass a path or a URL. `--segment "<prompt>:<seconds>"` (repeatable) drives multi-prompt models (Kling 3.0 / 3.0 Turbo / O3); total 3-15s. `generate image --video-ref` is the nano-banana-2 video reference.
|
|
28
28
|
- The top-level prompt is OPTIONAL for `generate video` with multi-prompt models and for Kling 3.0 Turbo (`--model kling-v3-turbo`) image-to-video — a `--segment`-only or `--start-image`-only call is valid. Every other model still needs a prompt; the server enforces per-model rules.
|
|
29
29
|
- AI Production: `videodraft produce <project> --mode full_video` generates one Seedance 2 video per scene; poll with `videodraft generations`, then `videodraft finalize <project>` swaps them into the timeline before `export`.
|
|
30
30
|
|