vidjutsu 1.2.0 → 1.4.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/README.md CHANGED
@@ -1,8 +1,14 @@
1
1
  # vidjutsu
2
2
 
3
- TypeScript SDK for the [VidJutsu](https://vidjutsu.ai) video intelligence API.
3
+ Clone viral TikTok and Instagram videos onto your own characters. Typed SDK, CLI, and MCP server for the [VidJutsu](https://vidjutsu.ai) API, auto-generated from the [OpenAPI spec](https://raw.githubusercontent.com/tfcbot/vidjutsu-openapi/main/openapi/spec.json).
4
4
 
5
- Watch, extract, transcribe, and overlay fully typed, auto-generated from the [OpenAPI spec](https://raw.githubusercontent.com/tfcbot/vidjutsu-openapi/main/openapi/spec.json).
5
+ Point it at a source video and it does three things:
6
+
7
+ 1. **Score it.** A clone check tells you whether the video will clone well before you spend anything on rendering.
8
+ 2. **Swap in your character.** Create a reusable character once, then reuse it across every clone.
9
+ 3. **Get a rendered clone.** Kling 3.0 Motion Control maps the source motion onto your character while preserving the source soundtrack.
10
+
11
+ Supporting endpoints cover discovery (scrape TikTok/Instagram), single-video download, and media processing (watch, extract, transcribe, overlay, compliance).
6
12
 
7
13
  ## Install
8
14
 
@@ -10,128 +16,209 @@ Watch, extract, transcribe, and overlay — fully typed, auto-generated from the
10
16
  npm install vidjutsu
11
17
  ```
12
18
 
13
- ## Get your API key
19
+ Or install the standalone CLI (no Node required):
14
20
 
15
21
  ```bash
16
22
  curl -fsSL https://vidjutsu.ai/install.sh | bash
23
+ ```
24
+
25
+ ## Get an API key
26
+
27
+ ```bash
17
28
  vidjutsu subscribe --email you@example.com
18
29
  ```
19
30
 
20
- Your key saves to `~/.vidjutsu/config.json` automatically. The SDK reads it from there no extra setup needed.
31
+ The key saves to `~/.vidjutsu/config.json`. The SDK and CLI read it from there, so nothing else needs configuring.
32
+
33
+ ## Three ways to use it
34
+
35
+ - **MCP** — connect an agent (Claude and others) and let it drive the clone workflow.
36
+ - **CLI** — `vidjutsu clone ...` from a terminal.
37
+ - **SDK** — typed methods in TypeScript.
38
+
39
+ ### MCP
40
+
41
+ For **Claude Code**:
42
+
43
+ ```bash
44
+ claude mcp add --transport http vidjutsu https://api.vidjutsu.ai/mcp
45
+ ```
46
+
47
+ For the **Claude desktop and web apps**, add a custom connector pointing at `https://api.vidjutsu.ai/mcp` and complete the OAuth prompt. There is no command to run.
48
+
49
+ ### CLI
50
+
51
+ Characters are reusable. Create one, then clone any number of videos with it.
52
+
53
+ ```bash
54
+ # 1. Create a character once. This prints a char_... id.
55
+ vidjutsu clone character --prompt "A neutral, camera-ready presenter"
56
+
57
+ # 2. Check whether a source video will clone well (optional but recommended).
58
+ vidjutsu clone check "https://www.tiktok.com/@creator/video/123456789"
59
+
60
+ # 3. Clone the video onto your character.
61
+ vidjutsu clone run "https://www.tiktok.com/@creator/video/123456789" \
62
+ --character char_abc123
63
+
64
+ # List your characters any time.
65
+ vidjutsu clone character list
66
+ ```
21
67
 
22
- ## Quick start
68
+ `clone run` downloads the source video, runs the clone check, extracts frame 0, builds a character-swapped starting frame, submits a Kling 3.0 Motion Control generation, and waits for the final URL. It stops if the check verdict is weak unless you pass `--force`. Source clips are limited to 15 seconds.
69
+
70
+ ### SDK
23
71
 
24
72
  ```ts
25
73
  import { createClient } from "vidjutsu";
26
74
 
27
- const vj = createClient();
75
+ const vj = createClient(); // reads ~/.vidjutsu/config.json or env vars
28
76
 
29
- // Watch AI analyzes a video and answers your prompt (10 credits)
30
- const { data: watch } = await vj.watchMedia({
31
- mediaUrl: "https://cdn.example.com/video.mp4",
32
- prompt: "Is the hook strong enough for fitness TikTok?",
77
+ // 1. Create a reusable character (returns { id: "char_...", imageUrl, model }).
78
+ const { data: character } = await vj.createCharacter({
79
+ prompt: "A neutral, camera-ready presenter",
33
80
  });
34
- console.log(watch.response);
35
81
 
36
- // Extract pull frames, audio, and metadata (5 credits)
37
- const { data: extract } = await vj.extractMedia({
38
- mediaUrl: "https://cdn.example.com/video.mp4",
39
- frames: "auto",
40
- audio: true,
82
+ // 2. Stage the source video's file, then check it.
83
+ const { data: source } = await vj.downloadTikTokVideo({
84
+ url: "https://www.tiktok.com/@creator/video/123456789",
85
+ });
86
+ const { data: check } = await vj.cloneCheck({ videoUrl: source.url });
87
+ console.log(check.verdict, check.score);
88
+
89
+ // 3. Extract frame 0 and build a character-swapped starting frame.
90
+ const { data: extracted } = await vj.extractMedia({
91
+ mediaUrl: source.url,
92
+ frames: [0],
93
+ audio: false,
94
+ metadata: false,
41
95
  });
42
- console.log(extract.frames, extract.metadata);
96
+ const firstFrame = extracted.frames?.[0]?.url;
97
+ if (!firstFrame) throw new Error("No first frame returned");
43
98
 
44
- // Transcribe speech-to-text with word-level timing (10 credits)
45
- const { data: transcript } = await vj.transcribeMedia({
46
- mediaUrl: "https://cdn.example.com/video.mp4",
99
+ const { data: starting } = await vj.cloneStartingImage({
100
+ firstFrame,
101
+ characterId: character.id,
102
+ prompt: "Match the framing and lighting of the source",
47
103
  });
48
- console.log(transcript.transcript, transcript.words);
49
104
 
50
- // Overlay burn text onto video (5 credits)
51
- const { data: overlay } = await vj.createOverlay({
52
- videoUrl: "https://cdn.example.com/video.mp4",
53
- text: "Follow for more tips",
54
- position: "bottom",
105
+ // 4. Generate the clone video, then poll for the final URL.
106
+ const { data: task } = await vj.cloneVideo({
107
+ startingImageUrl: starting.imageUrl,
108
+ sourceVideoUrl: source.url,
109
+ model: "kling", // the only supported clone-video model
55
110
  });
56
- console.log(overlay.resultUrl);
111
+ let result;
112
+ do {
113
+ await new Promise((resolve) => setTimeout(resolve, 3000));
114
+ ({ data: result } = await vj.getCloneVideo(task.id));
115
+ } while (result.status === "processing");
116
+ if (result.status === "failed") throw new Error(result.error);
117
+ console.log(result.videoUrl);
57
118
  ```
58
119
 
120
+ Every method returns the `openapi-fetch` shape: `{ data, error, response }`.
121
+
59
122
  ## Authentication
60
123
 
61
124
  The client resolves your API key in this order:
62
125
 
63
126
  1. **Explicit** — `createClient({ apiKey: "vj_live_..." })`
64
127
  2. **Environment variable** — `VIDJUTSU_API_KEY`
65
- 3. **Config file** — `~/.vidjutsu/config.json` (written by `vidjutsu auth`)
128
+ 3. **Config file** — `~/.vidjutsu/config.json` (written by `vidjutsu subscribe` / `vidjutsu auth`)
66
129
 
67
130
  ```ts
68
- // Reads from env or config file
69
- const vj = createClient();
131
+ const vj = createClient(); // env or config file
132
+ const vj = createClient({ apiKey: "vj_live_..." }); // explicit
133
+ const vj = createClient({ baseUrl: "https://staging.api.vidjutsu.ai" }); // custom host
134
+ ```
70
135
 
71
- // Or pass explicitly
72
- const vj = createClient({ apiKey: "vj_live_..." });
136
+ The default base URL is `https://api.vidjutsu.ai`.
73
137
 
74
- // Custom base URL
75
- const vj = createClient({ baseUrl: "https://staging.api.vidjutsu.ai" });
76
- ```
138
+ ## Methods
77
139
 
78
- ## All methods
140
+ ### Clone
79
141
 
80
- ### Video intelligence (paid)
142
+ | Method | Description |
143
+ |--------|-------------|
144
+ | `cloneCheck(body)` | Score whether a source video will clone well |
145
+ | `createCharacter(body)` / `getCharacter(id)` / `listCharacters()` | Create and reuse persisted characters |
146
+ | `cloneStartingImage(body)` | Build a character-swapped starting frame (no overlays) |
147
+ | `cloneVideo(body)` / `getCloneVideo(id)` | Generate a clone video and poll the task |
148
+
149
+ ### Discovery (scrape)
150
+
151
+ Metadata and discovery only. Scrape returns the provider's raw source URLs and never stages media.
81
152
 
82
- | Method | Credits | Description |
83
- |--------|---------|-------------|
84
- | `watchMedia(body)` | 10 | AI watches a video/image and answers your prompt |
85
- | `extractMedia(body)` | 5 | Extract frames, audio, and metadata from video |
86
- | `transcribeMedia(body)` | 10 | Speech-to-text with word-level timing |
87
- | `checkSpec(body)` | 5 | Validate a VidLang spec against rules |
88
- | `createOverlay(body)` | 5 | Burn text overlay onto video |
153
+ | Method | Description |
154
+ |--------|-------------|
155
+ | `scrapeTikTokTrending(body)` / `scrapeTikTokProfile(body)` / `scrapeTikTokProfileVideos(body)` | Find TikTok videos |
156
+ | `scrapeTikTokVideo(body)` / `scrapeTikTokVideoTranscript(body)` / `scrapeTikTokVideoComments(body)` | Single TikTok video, transcript, comments |
157
+ | `scrapeTikTokSearchUsers(body)` | Search TikTok users |
158
+ | `scrapeInstagramProfile(body)` / `scrapeInstagramUserPosts(body)` / `scrapeInstagramUserReels(body)` | Find Instagram content |
159
+ | `scrapeInstagramPost(body)` / `scrapeInstagramPostComments(body)` | Single Instagram post, comments |
160
+
161
+ ### Download
162
+
163
+ Stage one video's file to the CDN, one call per video.
164
+
165
+ | Method | Description |
166
+ |--------|-------------|
167
+ | `downloadTikTokVideo(body)` / `downloadInstagramVideo(body)` | Download and stage a single video |
168
+
169
+ ### Media processing (metered)
170
+
171
+ Each endpoint has its own daily rate limit (fixed window, resets 00:00 UTC). Hitting a limit returns HTTP 429 with a `retryAfter` hint.
172
+
173
+ | Method | Description |
174
+ |--------|-------------|
175
+ | `watchMedia(body)` | AI watches a video/image and answers your prompt |
176
+ | `extractMedia(body)` | Extract frames, audio, and metadata |
177
+ | `transcribeMedia(body)` | Speech-to-text with word-level timing |
178
+ | `createOverlay(body)` / `createDisclaimer(body)` | Burn text or a disclaimer onto video |
179
+ | `checkComplianceVideo(body)` / `checkCompliancePrompt(body)` | Ad-compliance checks |
89
180
 
90
- ### Resources (free)
181
+ ### Resources
91
182
 
92
183
  | Method | Description |
93
184
  |--------|-------------|
94
- | `createAccount(body)` / `updateAccount(body, query)` / `listOrGetAccounts(query)` / `deleteAccount(query)` | Manage accounts |
95
- | `createPost(body)` / `updatePost(body, query)` / `listOrGetPosts(query)` / `deletePost(query)` | Manage posts |
96
- | `createAsset(body)` / `updateAsset(body, query)` / `listOrGetAssets(query)` / `deleteAsset(query)` | Manage assets |
97
- | `createReference(body)` / `updateReference(body, query)` / `listOrGetReferences(query)` / `deleteReference(query)` | Manage references |
98
- | `uploadFile(body)` / `uploadFromUrl(body)` | Upload media to CDN |
99
- | `getBalance()` | Check credit balance |
185
+ | `createAccount` / `updateAccount` / `listOrGetAccounts` / `deleteAccount` | Accounts |
186
+ | `createPost` / `updatePost` / `listOrGetPosts` / `deletePost` | Posts |
187
+ | `createAsset` / `updateAsset` / `listOrGetAssets` / `deleteAsset` | Assets |
188
+ | `createReference` / `updateReference` / `listOrGetReferences` / `deleteReference` | References |
189
+ | `createEditorProject` / `updateEditorProject` / `listOrGetEditorProjects` / `deleteEditorProject` | Editor projects |
190
+ | `uploadFile(body)` / `uploadFromUrl(body)` | Upload media to the CDN |
100
191
 
101
- ### Utilities (free)
192
+ ### Account and billing
102
193
 
103
194
  | Method | Description |
104
195
  |--------|-------------|
105
- | `getInfo()` | API info and endpoint discovery |
106
- | `getPricing()` | Current pricing |
107
- | `createCheckout(body)` | Create Stripe checkout session |
108
- | `getCheckoutStatus(query)` | Check checkout status |
109
- | `createSubscription(body)` | Subscribe ($99/mo) |
110
- | `rotateApiKey()` | Rotate API key (invalidates current key) |
111
- | `recoverApiKey(body)` | Recover key via email |
196
+ | `getInfo()` / `getPricing()` / `getUsage()` | API info, pricing, remaining daily capacity |
197
+ | `createSubscription(body)` / `getCheckoutStatus(query)` | Subscribe and check checkout status |
198
+ | `rotateApiKey()` / `recoverApiKey(body)` | Rotate or recover your key |
112
199
 
113
200
  ## Raw client
114
201
 
115
- Every method returns the `openapi-fetch` response shape: `{ data, error, response }`. For endpoints not covered by convenience methods, use the raw client:
202
+ For any endpoint without a convenience method, use the raw `openapi-fetch` client:
116
203
 
117
204
  ```ts
118
- const { data, error } = await vj.api.GET("/v1/balance");
119
- const { data, error } = await vj.api.POST("/v1/watch", {
120
- body: { mediaUrl: "...", prompt: "..." },
205
+ const { data } = await vj.api.GET("/v1/usage");
206
+ const { data } = await vj.api.POST("/v1/clones/check", {
207
+ body: { videoUrl: "https://..." },
121
208
  });
122
209
  ```
123
210
 
124
- ## Credits & billing
211
+ ## Pricing and billing
125
212
 
126
- - **$99/month** 1,000 credits included
127
- - **Extra credits** $0.10 each
128
- - No overage charges API returns 402 when credits run out
213
+ - **$99/month flat** for full API access, via Stripe, billed monthly, cancel anytime.
214
+ - No credits and no top-ups. Usage is governed by per-endpoint daily rate limits that reset at 00:00 UTC.
215
+ - Hitting a daily limit returns **HTTP 429** with a `retryAfter` hint. Check remaining capacity with `getUsage()` or `GET /v1/usage`.
216
+ - `GET /v1/pricing` is the source of truth for current price and limits.
129
217
 
130
218
  ## Links
131
219
 
132
220
  - [Documentation](https://docs.vidjutsu.ai)
133
221
  - [API Reference](https://docs.vidjutsu.ai/api-reference/introduction)
134
- - [CLI](https://github.com/tfcbot/vidjutsu-sdk) (included in this package)
135
222
  - [OpenAPI Spec](https://raw.githubusercontent.com/tfcbot/vidjutsu-openapi/main/openapi/spec.json)
136
223
 
137
224
  ## License
@@ -2190,21 +2190,26 @@ var init_dist2 = __esm(() => {
2190
2190
  });
2191
2191
 
2192
2192
  // src/cli/version.ts
2193
- var VERSION = "1.0.0";
2193
+ var VERSION = "1.4.0";
2194
2194
 
2195
2195
  // src/cli/client.ts
2196
2196
  import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
2197
2197
  import { join } from "path";
2198
2198
  import { homedir } from "os";
2199
2199
  function loadConfig() {
2200
+ const envApiUrl = process.env.VIDJUTSU_API_URL;
2201
+ const envApiKey = process.env.VIDJUTSU_API_KEY;
2200
2202
  if (existsSync(CONFIG_FILE)) {
2201
2203
  const raw = JSON.parse(readFileSync(CONFIG_FILE, "utf-8"));
2202
2204
  if (!raw.apiUrl || raw.apiUrl.includes(".convex.site")) {
2203
2205
  raw.apiUrl = DEFAULT_API_URL;
2204
2206
  }
2205
- return raw;
2207
+ return {
2208
+ apiUrl: envApiUrl ?? raw.apiUrl,
2209
+ apiKey: envApiKey ?? raw.apiKey
2210
+ };
2206
2211
  }
2207
- return { apiUrl: DEFAULT_API_URL };
2212
+ return { apiUrl: envApiUrl ?? DEFAULT_API_URL, apiKey: envApiKey };
2208
2213
  }
2209
2214
  function saveConfig(config) {
2210
2215
  if (!existsSync(CONFIG_DIR))
@@ -2237,7 +2242,7 @@ async function publicRequest(method, path, body) {
2237
2242
  }
2238
2243
  return json;
2239
2244
  }
2240
- async function apiRequest(method, path, body) {
2245
+ async function apiRequest(method, path, body, options = {}) {
2241
2246
  const config = loadConfig();
2242
2247
  if (!config.apiKey) {
2243
2248
  throw new Error('Not authenticated. Run "vidjutsu auth --key <your_api_key>" first.');
@@ -2245,7 +2250,8 @@ async function apiRequest(method, path, body) {
2245
2250
  const url = `${config.apiUrl}${path}`;
2246
2251
  const headers = {
2247
2252
  Authorization: `Bearer ${config.apiKey}`,
2248
- "Content-Type": "application/json"
2253
+ "Content-Type": "application/json",
2254
+ ...options.idempotencyKey ? { "Idempotency-Key": options.idempotencyKey } : {}
2249
2255
  };
2250
2256
  const res = await fetch(url, {
2251
2257
  method,
@@ -2716,6 +2722,259 @@ var init_update = __esm(() => {
2716
2722
  });
2717
2723
  });
2718
2724
 
2725
+ // src/cli/commands/clone.ts
2726
+ var exports_clone = {};
2727
+ __export(exports_clone, {
2728
+ default: () => clone_default
2729
+ });
2730
+ function validateModel(model) {
2731
+ if (model === undefined)
2732
+ return;
2733
+ if (!VALID_MODELS.includes(model)) {
2734
+ throw new Error(`--model must be one of: ${VALID_MODELS.join(", ")}`);
2735
+ }
2736
+ return model;
2737
+ }
2738
+ function isCharacterId(value) {
2739
+ return value.startsWith("char_");
2740
+ }
2741
+ function requireCharacterId(value) {
2742
+ if (!isCharacterId(value)) {
2743
+ throw new Error("--character must be a stored character id beginning with char_");
2744
+ }
2745
+ return value;
2746
+ }
2747
+ async function pollCloneVideo(id, { intervalMs = 3000, timeoutMs = 600000 } = {}) {
2748
+ const deadline = Date.now() + timeoutMs;
2749
+ while (true) {
2750
+ const status = await apiRequest("GET", `/v1/clones/video/${encodeURIComponent(id)}`);
2751
+ if (status.status === "completed")
2752
+ return status;
2753
+ if (status.status === "failed")
2754
+ throw new Error(status.error ?? `Clone video ${id} failed`);
2755
+ if (Date.now() >= deadline)
2756
+ throw new Error(`Timed out waiting for clone video ${id}`);
2757
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
2758
+ }
2759
+ }
2760
+ var VALID_MODELS, clone_default;
2761
+ var init_clone = __esm(() => {
2762
+ init_dist2();
2763
+ init_client();
2764
+ VALID_MODELS = ["kling"];
2765
+ clone_default = defineCommand({
2766
+ meta: {
2767
+ name: "clone",
2768
+ description: "Clone a source video's motion onto a new character. About 5 minutes end to end for the full chain. " + "Kling 3.0 Motion Control is the supported model. Source clips are capped at 15 seconds. " + "Generation steps share the subscription's daily clone limit."
2769
+ },
2770
+ subCommands: {
2771
+ check: defineCommand({
2772
+ meta: {
2773
+ name: "check",
2774
+ description: "Evaluate whether a source video can be cloned reliably. Uses the daily clone limit. " + "Exits nonzero when the verdict is weak."
2775
+ },
2776
+ args: {
2777
+ videoUrl: { type: "positional", description: "Public HTTPS URL of the source video", required: true },
2778
+ context: { type: "string", description: "Optional context about the intended clone" }
2779
+ },
2780
+ async run({ args }) {
2781
+ const result = await apiRequest("POST", "/v1/clones/check", {
2782
+ videoUrl: args.videoUrl,
2783
+ context: args.context
2784
+ });
2785
+ console.log(`Verdict: ${result.verdict}`);
2786
+ console.log(`Score: ${result.score}/100`);
2787
+ console.log("Evidence:");
2788
+ for (const item of result.evidence ?? []) {
2789
+ console.log(` - ${item}`);
2790
+ }
2791
+ if (result.verdict === "weak") {
2792
+ process.exit(1);
2793
+ }
2794
+ }
2795
+ }),
2796
+ character: defineCommand({
2797
+ meta: {
2798
+ name: "character",
2799
+ description: "Create a reusable, persisted character. Create it once, then reuse the printed id for every " + "clone (see `vidjutsu clone starting-image --character <id>` and `clone run --character <id>`). " + "Uses the daily clone limit."
2800
+ },
2801
+ subCommands: {
2802
+ list: defineCommand({
2803
+ meta: {
2804
+ name: "list",
2805
+ description: "List your persisted characters. Reads are not billed."
2806
+ },
2807
+ async run() {
2808
+ const result = await apiRequest("GET", "/v1/characters");
2809
+ const characters = Array.isArray(result) ? result : result.characters ?? [];
2810
+ if (characters.length === 0) {
2811
+ console.log("No characters yet. Create one with: vidjutsu clone character --prompt ...");
2812
+ return;
2813
+ }
2814
+ for (const c3 of characters) {
2815
+ console.log(`${c3.id} ${c3.model} ${c3.createdAt}`);
2816
+ }
2817
+ }
2818
+ })
2819
+ },
2820
+ args: {
2821
+ prompt: { type: "string", description: "Text description of the character to generate" },
2822
+ reference: { type: "string", description: "Optional public HTTPS reference image URL to guide identity" }
2823
+ },
2824
+ async run({ args, rawArgs }) {
2825
+ if (rawArgs[0] === "list")
2826
+ return;
2827
+ if (!args.prompt) {
2828
+ throw new Error("--prompt is required to create a character (or run `vidjutsu clone character list`).");
2829
+ }
2830
+ const result = await apiRequest("POST", "/v1/characters", {
2831
+ prompt: args.prompt,
2832
+ referenceImageUrl: args.reference
2833
+ });
2834
+ console.log(`Character created: ${result.id}`);
2835
+ console.log(`Image: ${result.imageUrl}`);
2836
+ console.log(`Model: ${result.model}`);
2837
+ console.log("");
2838
+ console.log(`Reuse this character with: --character ${result.id}`);
2839
+ }
2840
+ }),
2841
+ "starting-image": defineCommand({
2842
+ meta: {
2843
+ name: "starting-image",
2844
+ description: "Create a character-swapped starting frame from an extracted first frame and a stored character id. Uses the daily clone limit."
2845
+ },
2846
+ args: {
2847
+ character: {
2848
+ type: "string",
2849
+ description: "Stored character id (char_...) from `clone character`",
2850
+ required: true
2851
+ },
2852
+ "first-frame": {
2853
+ type: "string",
2854
+ description: "Public HTTPS URL of the source video's extracted first-frame image",
2855
+ required: true
2856
+ },
2857
+ prompt: { type: "string", description: "Instructions for composing the starting frame", required: true }
2858
+ },
2859
+ async run({ args }) {
2860
+ const result = await apiRequest("POST", "/v1/clones/starting-image", {
2861
+ firstFrame: args["first-frame"],
2862
+ characterId: requireCharacterId(args.character),
2863
+ prompt: args.prompt
2864
+ });
2865
+ console.log(result.imageUrl);
2866
+ }
2867
+ }),
2868
+ video: defineCommand({
2869
+ meta: {
2870
+ name: "video",
2871
+ description: "Clone source motion onto a starting image with Kling 3.0 Motion Control. Uses the daily clone limit. " + "Returns a task id (HTTP 202); use --wait to poll to completion."
2872
+ },
2873
+ args: {
2874
+ "starting-image": { type: "string", description: "Public HTTPS URL of the starting frame", required: true },
2875
+ source: { type: "string", description: "Public HTTPS URL of the source video whose motion is cloned", required: true },
2876
+ model: { type: "string", description: "kling (the only supported model)" },
2877
+ prompt: { type: "string", description: "Optional override for the default motion-control prompt" },
2878
+ wait: { type: "boolean", description: "Poll GET status until completed or failed, then print videoUrl" }
2879
+ },
2880
+ async run({ args }) {
2881
+ const model = validateModel(args.model);
2882
+ const accepted = await apiRequest("POST", "/v1/clones/video", {
2883
+ startingImageUrl: args["starting-image"],
2884
+ sourceVideoUrl: args.source,
2885
+ model,
2886
+ prompt: args.prompt
2887
+ });
2888
+ console.log(accepted.id);
2889
+ if (args.wait) {
2890
+ const final = await pollCloneVideo(accepted.id);
2891
+ console.log(final.videoUrl);
2892
+ }
2893
+ }
2894
+ }),
2895
+ status: defineCommand({
2896
+ meta: {
2897
+ name: "status",
2898
+ description: "Poll a clone video task by id. Status reads are not billed."
2899
+ },
2900
+ args: {
2901
+ id: { type: "positional", description: "Clone video task id", required: true }
2902
+ },
2903
+ async run({ args }) {
2904
+ const result = await apiRequest("GET", `/v1/clones/video/${encodeURIComponent(args.id)}`);
2905
+ console.log(JSON.stringify(result, null, 2));
2906
+ }
2907
+ }),
2908
+ run: defineCommand({
2909
+ meta: {
2910
+ name: "run",
2911
+ description: "Run the full clone chain: download the TikTok video, check it, generate a starting image from a " + "reusable stored character, generate the clone video, and wait for it to finish. About 5 minutes end " + "to end. Characters are reusable: create one once with `vidjutsu clone character --prompt ...`, then " + "pass its id here with --character on every run — this command never generates a new random character. " + "--character is required. Kling 3.0 Motion Control is the only model; source clips " + "are capped at 15 seconds. Generation steps share the daily clone limit. Stops before generation when the " + "check verdict is weak, unless --force is given. The command extracts frame 0 and passes it directly " + "to the character edit step."
2912
+ },
2913
+ args: {
2914
+ tiktokUrl: { type: "positional", description: "TikTok video URL to clone", required: true },
2915
+ character: {
2916
+ type: "string",
2917
+ description: "Required. Stored character id (char_...) from `vidjutsu clone character --prompt ...`. " + "Characters are reusable across runs — create one once, then pass its id here every time."
2918
+ },
2919
+ "starting-prompt": { type: "string", description: "Prompt for the starting frame composition" },
2920
+ model: { type: "string", description: "kling (the only supported model)" },
2921
+ force: { type: "boolean", description: "Proceed even if the clone check verdict is weak" }
2922
+ },
2923
+ async run({ args }) {
2924
+ const model = validateModel(args.model) ?? "kling";
2925
+ if (!args.character) {
2926
+ console.log("Missing --character. Create a reusable character first with: " + 'vidjutsu clone character --prompt "A neutral, camera-ready presenter"');
2927
+ console.log("Then pass its id here: vidjutsu clone run <url> --character char_...");
2928
+ process.exit(1);
2929
+ }
2930
+ const characterId = requireCharacterId(args.character);
2931
+ console.log(`Downloading ${args.tiktokUrl}...`);
2932
+ const download = await apiRequest("POST", "/v1/videos/download/tiktok", {
2933
+ url: args.tiktokUrl
2934
+ });
2935
+ console.log("Checking cloneability...");
2936
+ const check = await apiRequest("POST", "/v1/clones/check", {
2937
+ videoUrl: download.url
2938
+ });
2939
+ console.log(`Verdict: ${check.verdict} (score ${check.score}/100)`);
2940
+ for (const item of check.evidence ?? []) {
2941
+ console.log(` - ${item}`);
2942
+ }
2943
+ if (check.verdict === "weak" && !args.force) {
2944
+ console.log("Stopping: the source clip is unlikely to clone well. Pass --force to continue anyway.");
2945
+ process.exit(1);
2946
+ }
2947
+ console.log("Extracting first frame...");
2948
+ const extracted = await apiRequest("POST", "/v1/extract", {
2949
+ mediaUrl: download.url,
2950
+ frames: [0],
2951
+ audio: false,
2952
+ metadata: false
2953
+ });
2954
+ const firstFrame = extracted.frames?.find((frame) => frame.index === 0)?.url ?? extracted.frames?.[0]?.url;
2955
+ if (!firstFrame)
2956
+ throw new Error("The source video did not return an extracted first frame");
2957
+ console.log("Generating starting image...");
2958
+ const startingImage = await apiRequest("POST", "/v1/clones/starting-image", {
2959
+ firstFrame,
2960
+ characterId,
2961
+ prompt: args["starting-prompt"] ?? "Match the framing and pose of the source video's opening frame"
2962
+ });
2963
+ console.log("Generating clone video...");
2964
+ const accepted = await apiRequest("POST", "/v1/clones/video", {
2965
+ startingImageUrl: startingImage.imageUrl,
2966
+ sourceVideoUrl: download.url,
2967
+ model
2968
+ });
2969
+ console.log(`Task ${accepted.id} accepted, polling...`);
2970
+ const final = await pollCloneVideo(accepted.id);
2971
+ console.log(final.videoUrl);
2972
+ }
2973
+ })
2974
+ }
2975
+ });
2976
+ });
2977
+
2719
2978
  // src/cli/commands/generated/watch.ts
2720
2979
  var exports_watch = {};
2721
2980
  __export(exports_watch, {
@@ -3377,13 +3636,117 @@ var init_info = __esm(() => {
3377
3636
  });
3378
3637
  });
3379
3638
 
3639
+ // src/cli/commands/generated/project.ts
3640
+ var exports_project = {};
3641
+ __export(exports_project, {
3642
+ default: () => project_default
3643
+ });
3644
+ function parseTags5(raw) {
3645
+ if (!raw)
3646
+ return;
3647
+ return raw.split(",").map((pair) => {
3648
+ const [key, ...rest] = pair.split("=");
3649
+ return { key: key.trim(), value: rest.join("=").trim() };
3650
+ });
3651
+ }
3652
+ var project_default;
3653
+ var init_project = __esm(() => {
3654
+ init_dist2();
3655
+ init_client();
3656
+ project_default = defineCommand({
3657
+ meta: { name: "project", description: "Manage projects" },
3658
+ subCommands: {
3659
+ create: defineCommand({
3660
+ meta: { name: "create", description: "Create an editor project" },
3661
+ args: {
3662
+ name: { type: "string", description: "name", required: true },
3663
+ project: { type: "string", description: "project" },
3664
+ tags: { type: "string", description: "Tags as key=value pairs, comma-separated" },
3665
+ metadata: { type: "string", description: "metadata" }
3666
+ },
3667
+ async run({ args }) {
3668
+ const body = {};
3669
+ if (args["name"] !== undefined)
3670
+ body["name"] = args["name"];
3671
+ if (args["project"] !== undefined)
3672
+ body["project"] = args["project"];
3673
+ const tags = parseTags5(args.tags);
3674
+ if (tags)
3675
+ body.tags = tags;
3676
+ if (args["metadata"] !== undefined)
3677
+ body["metadata"] = args["metadata"];
3678
+ const result = await apiRequest("POST", "/v1/projects", body);
3679
+ console.log(JSON.stringify(result, null, 2));
3680
+ }
3681
+ }),
3682
+ edit: defineCommand({
3683
+ meta: { name: "edit", description: "Edit an editor project" },
3684
+ args: {
3685
+ id: { type: "positional", description: "Project ID", required: true },
3686
+ name: { type: "string", description: "name" },
3687
+ status: { type: "string", description: "status" },
3688
+ project: { type: "string", description: "project" },
3689
+ tags: { type: "string", description: "Tags as key=value pairs, comma-separated" },
3690
+ metadata: { type: "string", description: "metadata" }
3691
+ },
3692
+ async run({ args }) {
3693
+ const body = {};
3694
+ if (args["name"] !== undefined)
3695
+ body["name"] = args["name"];
3696
+ if (args["status"] !== undefined)
3697
+ body["status"] = args["status"];
3698
+ if (args["project"] !== undefined)
3699
+ body["project"] = args["project"];
3700
+ const tags = parseTags5(args.tags);
3701
+ if (tags)
3702
+ body.tags = tags;
3703
+ if (args["metadata"] !== undefined)
3704
+ body["metadata"] = args["metadata"];
3705
+ const result = await apiRequest("PUT", "/v1/projects?id=" + args.id, body);
3706
+ console.log(JSON.stringify(result, null, 2));
3707
+ }
3708
+ }),
3709
+ list: defineCommand({
3710
+ meta: { name: "list", description: "List editor projects" },
3711
+ args: {
3712
+ id: { type: "string", description: "Project ID (omit to list all)" },
3713
+ status: { type: "string", description: "status" }
3714
+ },
3715
+ async run({ args }) {
3716
+ let path = "/v1/projects";
3717
+ const params = new URLSearchParams;
3718
+ if (args["id"])
3719
+ params.set("id", args["id"]);
3720
+ if (args["status"])
3721
+ params.set("status", args["status"]);
3722
+ const qs = params.toString();
3723
+ if (qs)
3724
+ path += "?" + qs;
3725
+ const result = await apiRequest("GET", path);
3726
+ console.log(JSON.stringify(result, null, 2));
3727
+ }
3728
+ }),
3729
+ delete: defineCommand({
3730
+ meta: { name: "delete", description: "Archive an editor project" },
3731
+ args: {
3732
+ id: { type: "positional", description: "Project ID", required: true }
3733
+ },
3734
+ async run({ args }) {
3735
+ const result = await apiRequest("DELETE", "/v1/projects?id=" + args.id);
3736
+ console.log(JSON.stringify(result, null, 2));
3737
+ }
3738
+ })
3739
+ }
3740
+ });
3741
+ });
3742
+
3380
3743
  // src/cli/index.ts
3381
3744
  init_dist2();
3382
3745
  var main = defineCommand({
3383
3746
  meta: {
3384
3747
  name: "vidjutsu",
3385
3748
  version: VERSION,
3386
- description: "Video intelligence API watch, extract, transcribe, check."
3749
+ description: "Clone short-form videos and process media with the VidJutsu API."
3387
3750
  },
3388
3751
  subCommands: {
3389
3752
  auth: () => Promise.resolve().then(() => (init_auth(), exports_auth)).then((m2) => m2.default),
@@ -3393,6 +3756,7 @@ var main = defineCommand({
3393
3756
  status: () => Promise.resolve().then(() => (init_status(), exports_status)).then((m2) => m2.default),
3394
3757
  version: () => Promise.resolve().then(() => (init_version(), exports_version)).then((m2) => m2.default),
3395
3758
  update: () => Promise.resolve().then(() => (init_update(), exports_update)).then((m2) => m2.default),
3759
+ clone: () => Promise.resolve().then(() => (init_clone(), exports_clone)).then((m2) => m2.default),
3396
3760
  watch: () => Promise.resolve().then(() => (init_watch(), exports_watch)).then((m2) => m2.default),
3397
3761
  extract: () => Promise.resolve().then(() => (init_extract(), exports_extract)).then((m2) => m2.default),
3398
3762
  transcribe: () => Promise.resolve().then(() => (init_transcribe(), exports_transcribe)).then((m2) => m2.default),
@@ -3403,7 +3767,8 @@ var main = defineCommand({
3403
3767
  asset: () => Promise.resolve().then(() => (init_asset(), exports_asset)).then((m2) => m2.default),
3404
3768
  reference: () => Promise.resolve().then(() => (init_reference(), exports_reference)).then((m2) => m2.default),
3405
3769
  usage: () => Promise.resolve().then(() => (init_usage(), exports_usage)).then((m2) => m2.default),
3406
- info: () => Promise.resolve().then(() => (init_info(), exports_info)).then((m2) => m2.default)
3770
+ info: () => Promise.resolve().then(() => (init_info(), exports_info)).then((m2) => m2.default),
3771
+ project: () => Promise.resolve().then(() => (init_project(), exports_project)).then((m2) => m2.default)
3407
3772
  }
3408
3773
  });
3409
3774
  runMain(main);
package/dist/methods.d.ts CHANGED
@@ -14,79 +14,137 @@ type QueryParams<P extends keyof paths, M extends keyof paths[P]> = paths[P][M]
14
14
  };
15
15
  } ? Q : never;
16
16
  export interface VidJutsuMethods {
17
- /** Create account Auth required. */
17
+ /** Create account Bearer API key required. */
18
18
  createAccount(body: ReqBody<"/v1/accounts", "post">): ReturnType<Client["POST"]>;
19
- /** Update account Auth required. */
19
+ /** Update account Bearer API key required. */
20
20
  updateAccount(body: ReqBody<"/v1/accounts", "put">, query?: QueryParams<"/v1/accounts", "put">): ReturnType<Client["PUT"]>;
21
- /** List accounts or get by ID Auth required. */
21
+ /** List accounts or get by ID Bearer API key required. */
22
22
  listOrGetAccounts(query?: QueryParams<"/v1/accounts", "get">): ReturnType<Client["GET"]>;
23
- /** Delete account Auth required. */
23
+ /** Delete account Bearer API key required. */
24
24
  deleteAccount(query?: QueryParams<"/v1/accounts", "delete">): ReturnType<Client["DELETE"]>;
25
25
  /** Recover API key Public endpoint. */
26
26
  recoverApiKey(body: ReqBody<"/v1/api_keys/recover", "post">): ReturnType<Client["POST"]>;
27
- /** Rotate API key Auth required. */
27
+ /** Rotate API key Bearer API key required. */
28
28
  rotateApiKey(): ReturnType<Client["POST"]>;
29
- /** Create asset from existing URL Auth required. */
29
+ /** Create asset from existing URL Bearer API key required. */
30
30
  createAsset(body: ReqBody<"/v1/assets", "post">): ReturnType<Client["POST"]>;
31
- /** Update asset metadata Auth required. */
31
+ /** Update asset metadata Bearer API key required. */
32
32
  updateAsset(body: ReqBody<"/v1/assets", "put">, query?: QueryParams<"/v1/assets", "put">): ReturnType<Client["PUT"]>;
33
- /** List assets or get by ID Auth required. */
33
+ /** List assets or get by ID Bearer API key required. */
34
34
  listOrGetAssets(query?: QueryParams<"/v1/assets", "get">): ReturnType<Client["GET"]>;
35
- /** Delete asset (soft) Auth required. */
35
+ /** Delete asset (soft) Bearer API key required. */
36
36
  deleteAsset(query?: QueryParams<"/v1/assets", "delete">): ReturnType<Client["DELETE"]>;
37
+ /** Agent registration (auth.md) Public endpoint. */
38
+ signupAgent(body: ReqBody<"/v1/auth/agent", "post">): ReturnType<Client["POST"]>;
39
+ /** Initiate claim flow Public endpoint. */
40
+ agentClaim(body: ReqBody<"/v1/auth/agent/claim", "post">): ReturnType<Client["POST"]>;
41
+ /** Complete claim flow Public endpoint. */
42
+ agentClaimComplete(body: ReqBody<"/v1/auth/agent/claim/complete", "post">): ReturnType<Client["POST"]>;
43
+ /** Revoke credential Public endpoint. */
44
+ agentRevoke(): ReturnType<Client["POST"]>;
37
45
  /** Confirm email verification code Public endpoint. */
38
46
  confirmVerification(body: ReqBody<"/v1/auth/verify/confirm", "post">): ReturnType<Client["POST"]>;
39
47
  /** Request email verification code Public endpoint. */
40
48
  requestVerification(body: ReqBody<"/v1/auth/verify/request", "post">): ReturnType<Client["POST"]>;
41
- /** Check spec Auth required. 5 credits. */
49
+ /** Create a reusable, persisted character image Active subscription and daily quota required. */
50
+ createCharacter(body: ReqBody<"/v1/characters", "post">): ReturnType<Client["POST"]>;
51
+ /** List your persisted characters Bearer API key required. */
52
+ listCharacters(): ReturnType<Client["GET"]>;
53
+ /** Fetch a persisted character by id Bearer API key required. */
54
+ getCharacter(id: string): ReturnType<Client["GET"]>;
55
+ /** Check spec Active subscription and daily quota required. */
42
56
  checkSpec(body: ReqBody<"/v1/check", "post">): ReturnType<Client["POST"]>;
43
- /** Get check rules Auth required. */
57
+ /** Get check rules Bearer API key required. */
44
58
  getCheckRules(): ReturnType<Client["GET"]>;
45
- /** Update check rules Auth required. */
59
+ /** Update check rules Bearer API key required. */
46
60
  updateCheckRules(body: ReqBody<"/v1/check/rules", "put">): ReturnType<Client["PUT"]>;
47
- /** Check prompt compliance */
61
+ /** Evaluate whether a source video can be cloned reliably Active subscription and daily quota required. */
62
+ cloneCheck(body: ReqBody<"/v1/clones/check", "post">): ReturnType<Client["POST"]>;
63
+ /** Create a character-swapped starting frame Active subscription and daily quota required. */
64
+ cloneStartingImage(body: ReqBody<"/v1/clones/starting-image", "post">): ReturnType<Client["POST"]>;
65
+ /** Clone source motion with Kling 3.0 Motion Control Active subscription and daily quota required. */
66
+ cloneVideo(body: ReqBody<"/v1/clones/video", "post">): ReturnType<Client["POST"]>;
67
+ /** Poll a clone video task Bearer API key required. */
68
+ getCloneVideo(id: string): ReturnType<Client["GET"]>;
69
+ /** Check prompt compliance Active subscription and daily quota required. */
48
70
  checkCompliancePrompt(body: ReqBody<"/v1/compliance/prompt", "post">): ReturnType<Client["POST"]>;
49
- /** Check video compliance */
71
+ /** Check video compliance Active subscription and daily quota required. */
50
72
  checkComplianceVideo(body: ReqBody<"/v1/compliance/video", "post">): ReturnType<Client["POST"]>;
51
73
  /** Check checkout status Public endpoint. */
52
74
  getCheckoutStatus(query?: QueryParams<"/v1/credits/status", "get">): ReturnType<Client["GET"]>;
53
- /** Burn fine-print disclaimer onto video */
75
+ /** Burn fine-print disclaimer onto video Active subscription and daily quota required. */
54
76
  createDisclaimer(body: ReqBody<"/v1/disclaimer", "post">): ReturnType<Client["POST"]>;
55
- /** Extract from media Auth required. 5 credits. */
77
+ /** Extract from media Active subscription and daily quota required. */
56
78
  extractMedia(body: ReqBody<"/v1/extract", "post">): ReturnType<Client["POST"]>;
57
79
  /** API info Public endpoint. */
58
80
  getInfo(): ReturnType<Client["GET"]>;
59
- /** Burn text overlay onto video Auth required. 5 credits. */
81
+ /** Burn text overlay onto video Active subscription and daily quota required. */
60
82
  createOverlay(body: ReqBody<"/v1/overlay", "post">): ReturnType<Client["POST"]>;
61
- /** Create post Auth required. */
83
+ /** Create post Bearer API key required. */
62
84
  createPost(body: ReqBody<"/v1/posts", "post">): ReturnType<Client["POST"]>;
63
- /** Update post Auth required. */
85
+ /** Update post Bearer API key required. */
64
86
  updatePost(body: ReqBody<"/v1/posts", "put">, query?: QueryParams<"/v1/posts", "put">): ReturnType<Client["PUT"]>;
65
- /** List posts or get by ID Auth required. */
87
+ /** List posts or get by ID Bearer API key required. */
66
88
  listOrGetPosts(query?: QueryParams<"/v1/posts", "get">): ReturnType<Client["GET"]>;
67
- /** Delete post Auth required. */
89
+ /** Delete post Bearer API key required. */
68
90
  deletePost(query?: QueryParams<"/v1/posts", "delete">): ReturnType<Client["DELETE"]>;
69
91
  /** Get pricing Public endpoint. */
70
92
  getPricing(): ReturnType<Client["GET"]>;
71
- /** Create reference Auth required. */
93
+ /** Create editor project Bearer API key required. */
94
+ createEditorProject(body: ReqBody<"/v1/projects", "post">): ReturnType<Client["POST"]>;
95
+ /** Update editor project Bearer API key required. */
96
+ updateEditorProject(body: ReqBody<"/v1/projects", "put">, query?: QueryParams<"/v1/projects", "put">): ReturnType<Client["PUT"]>;
97
+ /** List projects or get by ID Bearer API key required. */
98
+ listOrGetEditorProjects(query?: QueryParams<"/v1/projects", "get">): ReturnType<Client["GET"]>;
99
+ /** Archive editor project (soft) Bearer API key required. */
100
+ deleteEditorProject(query?: QueryParams<"/v1/projects", "delete">): ReturnType<Client["DELETE"]>;
101
+ /** Create reference Bearer API key required. */
72
102
  createReference(body: ReqBody<"/v1/references", "post">): ReturnType<Client["POST"]>;
73
- /** Update reference Auth required. */
103
+ /** Update reference Bearer API key required. */
74
104
  updateReference(body: ReqBody<"/v1/references", "put">, query?: QueryParams<"/v1/references", "put">): ReturnType<Client["PUT"]>;
75
- /** List references or get by ID Auth required. */
105
+ /** List references or get by ID Bearer API key required. */
76
106
  listOrGetReferences(query?: QueryParams<"/v1/references", "get">): ReturnType<Client["GET"]>;
77
- /** Delete reference Auth required. */
107
+ /** Delete reference Bearer API key required. */
78
108
  deleteReference(query?: QueryParams<"/v1/references", "delete">): ReturnType<Client["DELETE"]>;
109
+ /** Scrape Instagram post or reel Active subscription and daily quota required. */
110
+ scrapeInstagramPost(body: ReqBody<"/v1/scrape/instagram/post", "post">): ReturnType<Client["POST"]>;
111
+ /** Scrape Instagram post comments Active subscription and daily quota required. */
112
+ scrapeInstagramPostComments(body: ReqBody<"/v1/scrape/instagram/post/comments", "post">): ReturnType<Client["POST"]>;
113
+ /** Scrape Instagram profile Active subscription and daily quota required. */
114
+ scrapeInstagramProfile(body: ReqBody<"/v1/scrape/instagram/profile", "post">): ReturnType<Client["POST"]>;
115
+ /** Scrape Instagram user posts Active subscription and daily quota required. */
116
+ scrapeInstagramUserPosts(body: ReqBody<"/v1/scrape/instagram/user/posts", "post">): ReturnType<Client["POST"]>;
117
+ /** Scrape Instagram user reels Active subscription and daily quota required. */
118
+ scrapeInstagramUserReels(body: ReqBody<"/v1/scrape/instagram/user/reels", "post">): ReturnType<Client["POST"]>;
119
+ /** Scrape TikTok profile Active subscription and daily quota required. */
120
+ scrapeTikTokProfile(body: ReqBody<"/v1/scrape/tiktok/profile", "post">): ReturnType<Client["POST"]>;
121
+ /** Scrape TikTok profile videos Active subscription and daily quota required. */
122
+ scrapeTikTokProfileVideos(body: ReqBody<"/v1/scrape/tiktok/profile/videos", "post">): ReturnType<Client["POST"]>;
123
+ /** Scrape TikTok user search Active subscription and daily quota required. */
124
+ scrapeTikTokSearchUsers(body: ReqBody<"/v1/scrape/tiktok/search/users", "post">): ReturnType<Client["POST"]>;
125
+ /** Scrape TikTok trending feed Active subscription and daily quota required. */
126
+ scrapeTikTokTrending(body: ReqBody<"/v1/scrape/tiktok/trending", "post">): ReturnType<Client["POST"]>;
127
+ /** Scrape TikTok video Active subscription and daily quota required. */
128
+ scrapeTikTokVideo(body: ReqBody<"/v1/scrape/tiktok/video", "post">): ReturnType<Client["POST"]>;
129
+ /** Scrape TikTok video comments Active subscription and daily quota required. */
130
+ scrapeTikTokVideoComments(body: ReqBody<"/v1/scrape/tiktok/video/comments", "post">): ReturnType<Client["POST"]>;
131
+ /** Scrape TikTok video transcript Active subscription and daily quota required. */
132
+ scrapeTikTokVideoTranscript(body: ReqBody<"/v1/scrape/tiktok/video/transcript", "post">): ReturnType<Client["POST"]>;
79
133
  /** Create subscription Public endpoint. */
80
134
  createSubscription(body: ReqBody<"/v1/subscribe", "post">): ReturnType<Client["POST"]>;
81
- /** Transcribe media Auth required. 10 credits. */
135
+ /** Transcribe media Active subscription and daily quota required. */
82
136
  transcribeMedia(body: ReqBody<"/v1/transcribe", "post">): ReturnType<Client["POST"]>;
83
- /** Upload file Auth required. */
84
- uploadFile(): ReturnType<Client["POST"]>;
85
- /** Upload from URL Auth required. */
137
+ /** Upload file Bearer API key required. */
138
+ uploadFile(body: Blob | ArrayBuffer | ReadableStream): ReturnType<Client["POST"]>;
139
+ /** Upload from URL Bearer API key required. */
86
140
  uploadFromUrl(body: ReqBody<"/v1/upload/url", "post">): ReturnType<Client["POST"]>;
87
- /** Get daily usage */
141
+ /** Get daily usage Bearer API key required. */
88
142
  getUsage(): ReturnType<Client["GET"]>;
89
- /** Watch media Auth required. 10 credits. */
143
+ /** Import an Instagram video into VidJutsu Active subscription and daily quota required. */
144
+ downloadInstagramVideo(body: ReqBody<"/v1/videos/download/instagram", "post">): ReturnType<Client["POST"]>;
145
+ /** Import a TikTok video into VidJutsu Active subscription and daily quota required. */
146
+ downloadTikTokVideo(body: ReqBody<"/v1/videos/download/tiktok", "post">): ReturnType<Client["POST"]>;
147
+ /** Watch media Active subscription and daily quota required. */
90
148
  watchMedia(body: ReqBody<"/v1/watch", "post">): ReturnType<Client["POST"]>;
91
149
  }
92
150
  export declare function bindMethods(client: Client): VidJutsuMethods;
package/dist/methods.js CHANGED
@@ -30,12 +30,33 @@ export function bindMethods(client) {
30
30
  deleteAsset(query) {
31
31
  return client.DELETE("/v1/assets", { params: { query } });
32
32
  },
33
+ signupAgent(body) {
34
+ return client.POST("/v1/auth/agent", { body });
35
+ },
36
+ agentClaim(body) {
37
+ return client.POST("/v1/auth/agent/claim", { body });
38
+ },
39
+ agentClaimComplete(body) {
40
+ return client.POST("/v1/auth/agent/claim/complete", { body });
41
+ },
42
+ agentRevoke() {
43
+ return client.POST("/v1/auth/agent/revoke", {});
44
+ },
33
45
  confirmVerification(body) {
34
46
  return client.POST("/v1/auth/verify/confirm", { body });
35
47
  },
36
48
  requestVerification(body) {
37
49
  return client.POST("/v1/auth/verify/request", { body });
38
50
  },
51
+ createCharacter(body) {
52
+ return client.POST("/v1/characters", { body });
53
+ },
54
+ listCharacters() {
55
+ return client.GET("/v1/characters", {});
56
+ },
57
+ getCharacter(id) {
58
+ return client.GET("/v1/characters/{id}", { params: { path: { id } } });
59
+ },
39
60
  checkSpec(body) {
40
61
  return client.POST("/v1/check", { body });
41
62
  },
@@ -45,6 +66,18 @@ export function bindMethods(client) {
45
66
  updateCheckRules(body) {
46
67
  return client.PUT("/v1/check/rules", { body });
47
68
  },
69
+ cloneCheck(body) {
70
+ return client.POST("/v1/clones/check", { body });
71
+ },
72
+ cloneStartingImage(body) {
73
+ return client.POST("/v1/clones/starting-image", { body });
74
+ },
75
+ cloneVideo(body) {
76
+ return client.POST("/v1/clones/video", { body });
77
+ },
78
+ getCloneVideo(id) {
79
+ return client.GET("/v1/clones/video/{id}", { params: { path: { id } } });
80
+ },
48
81
  checkCompliancePrompt(body) {
49
82
  return client.POST("/v1/compliance/prompt", { body });
50
83
  },
@@ -81,6 +114,18 @@ export function bindMethods(client) {
81
114
  getPricing() {
82
115
  return client.GET("/v1/pricing", {});
83
116
  },
117
+ createEditorProject(body) {
118
+ return client.POST("/v1/projects", { body });
119
+ },
120
+ updateEditorProject(body, query) {
121
+ return client.PUT("/v1/projects", { body, params: { query } });
122
+ },
123
+ listOrGetEditorProjects(query) {
124
+ return client.GET("/v1/projects", { params: { query } });
125
+ },
126
+ deleteEditorProject(query) {
127
+ return client.DELETE("/v1/projects", { params: { query } });
128
+ },
84
129
  createReference(body) {
85
130
  return client.POST("/v1/references", { body });
86
131
  },
@@ -93,14 +138,54 @@ export function bindMethods(client) {
93
138
  deleteReference(query) {
94
139
  return client.DELETE("/v1/references", { params: { query } });
95
140
  },
141
+ scrapeInstagramPost(body) {
142
+ return client.POST("/v1/scrape/instagram/post", { body });
143
+ },
144
+ scrapeInstagramPostComments(body) {
145
+ return client.POST("/v1/scrape/instagram/post/comments", { body });
146
+ },
147
+ scrapeInstagramProfile(body) {
148
+ return client.POST("/v1/scrape/instagram/profile", { body });
149
+ },
150
+ scrapeInstagramUserPosts(body) {
151
+ return client.POST("/v1/scrape/instagram/user/posts", { body });
152
+ },
153
+ scrapeInstagramUserReels(body) {
154
+ return client.POST("/v1/scrape/instagram/user/reels", { body });
155
+ },
156
+ scrapeTikTokProfile(body) {
157
+ return client.POST("/v1/scrape/tiktok/profile", { body });
158
+ },
159
+ scrapeTikTokProfileVideos(body) {
160
+ return client.POST("/v1/scrape/tiktok/profile/videos", { body });
161
+ },
162
+ scrapeTikTokSearchUsers(body) {
163
+ return client.POST("/v1/scrape/tiktok/search/users", { body });
164
+ },
165
+ scrapeTikTokTrending(body) {
166
+ return client.POST("/v1/scrape/tiktok/trending", { body });
167
+ },
168
+ scrapeTikTokVideo(body) {
169
+ return client.POST("/v1/scrape/tiktok/video", { body });
170
+ },
171
+ scrapeTikTokVideoComments(body) {
172
+ return client.POST("/v1/scrape/tiktok/video/comments", { body });
173
+ },
174
+ scrapeTikTokVideoTranscript(body) {
175
+ return client.POST("/v1/scrape/tiktok/video/transcript", { body });
176
+ },
96
177
  createSubscription(body) {
97
178
  return client.POST("/v1/subscribe", { body });
98
179
  },
99
180
  transcribeMedia(body) {
100
181
  return client.POST("/v1/transcribe", { body });
101
182
  },
102
- uploadFile() {
103
- return client.POST("/v1/upload", {});
183
+ uploadFile(body) {
184
+ return client.POST("/v1/upload", {
185
+ body: body,
186
+ bodySerializer: (b) => b,
187
+ headers: { "Content-Type": "application/octet-stream" },
188
+ });
104
189
  },
105
190
  uploadFromUrl(body) {
106
191
  return client.POST("/v1/upload/url", { body });
@@ -108,6 +193,12 @@ export function bindMethods(client) {
108
193
  getUsage() {
109
194
  return client.GET("/v1/usage", {});
110
195
  },
196
+ downloadInstagramVideo(body) {
197
+ return client.POST("/v1/videos/download/instagram", { body });
198
+ },
199
+ downloadTikTokVideo(body) {
200
+ return client.POST("/v1/videos/download/tiktok", { body });
201
+ },
111
202
  watchMedia(body) {
112
203
  return client.POST("/v1/watch", { body });
113
204
  },
package/install.sh CHANGED
@@ -43,45 +43,6 @@ detect_platform() {
43
43
  info "Detected platform: ${BOLD}${PLATFORM}${RESET}"
44
44
  }
45
45
 
46
- # --- Prerequisites ---
47
-
48
- ensure_node() {
49
- if command -v node >/dev/null 2>&1; then
50
- success "Node.js $(node --version) found"
51
- return
52
- fi
53
-
54
- error "Node.js is required. Install it from https://nodejs.org"
55
- }
56
-
57
- ensure_bun() {
58
- if command -v bun >/dev/null 2>&1; then
59
- success "Bun $(bun --version) found"
60
- return
61
- fi
62
-
63
- info "Installing Bun..."
64
- curl -fsSL https://bun.sh/install | bash
65
- export BUN_INSTALL="$HOME/.bun"
66
- export PATH="$BUN_INSTALL/bin:$PATH"
67
- success "Bun installed: $(bun --version)"
68
- }
69
-
70
- ensure_typescript() {
71
- if command -v tsc >/dev/null 2>&1; then
72
- success "TypeScript $(tsc --version) found"
73
- return
74
- fi
75
-
76
- info "Installing TypeScript globally..."
77
- if command -v bun >/dev/null 2>&1; then
78
- bun add -g typescript
79
- elif command -v npm >/dev/null 2>&1; then
80
- npm install -g typescript
81
- fi
82
- success "TypeScript installed"
83
- }
84
-
85
46
  # --- Version Detection ---
86
47
 
87
48
  get_version() {
@@ -180,9 +141,6 @@ main() {
180
141
  printf "\n${BOLD} VidJutsu CLI Installer${RESET}\n\n"
181
142
 
182
143
  detect_platform
183
- ensure_node
184
- ensure_bun
185
- ensure_typescript
186
144
  get_version
187
145
  install_binary
188
146
  verify
@@ -190,7 +148,7 @@ main() {
190
148
  printf "\n${GREEN}${BOLD} ✓ VidJutsu CLI v${VERSION} installed successfully${RESET}\n"
191
149
  printf "\n Get started:\n"
192
150
  printf " ${CYAN}vidjutsu auth${RESET} — authenticate your account\n"
193
- printf " ${CYAN}vidjutsu generate${RESET} generate AI video\n"
151
+ printf " ${CYAN}vidjutsu clone --help${RESET} clone a staged short video\n"
194
152
  printf " ${CYAN}vidjutsu --help${RESET} — see all commands\n\n"
195
153
  }
196
154
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vidjutsu",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "description": "VidJutsu — video intelligence API. SDK + CLI.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -20,6 +20,8 @@
20
20
  ],
21
21
  "scripts": {
22
22
  "generate": "bun scripts/generate-schema.ts && bun scripts/generate-methods.ts",
23
+ "test": "bun test",
24
+ "check:release": "bun scripts/check-release.ts",
23
25
  "build": "bun run build:sdk && bun run build:cli",
24
26
  "build:sdk": "tsc",
25
27
  "build:cli": "bun build src/cli/index.ts --outfile dist/cli/index.mjs --target node",
@@ -29,7 +31,7 @@
29
31
  "build:binary:linux-x64": "bun build src/cli/index.ts --compile --target=bun-linux-x64 --outfile dist/vidjutsu-linux-x64",
30
32
  "build:binary:linux-arm64": "bun build src/cli/index.ts --compile --target=bun-linux-arm64 --outfile dist/vidjutsu-linux-arm64",
31
33
  "dev": "bun run src/cli/index.ts",
32
- "prepublishOnly": "bun run generate && bun run build"
34
+ "prepublishOnly": "bun run generate && bun run check:release && bun test && bun run build"
33
35
  },
34
36
  "dependencies": {
35
37
  "citty": "^0.1",