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 +155 -68
- package/dist/cli/index.mjs +372 -7
- package/dist/methods.d.ts +89 -31
- package/dist/methods.js +93 -2
- package/install.sh +1 -43
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
# vidjutsu
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
30
|
-
const { data:
|
|
31
|
-
|
|
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
|
-
//
|
|
37
|
-
const { data:
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
96
|
+
const firstFrame = extracted.frames?.[0]?.url;
|
|
97
|
+
if (!firstFrame) throw new Error("No first frame returned");
|
|
43
98
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
-
//
|
|
51
|
-
const { data:
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
72
|
-
const vj = createClient({ apiKey: "vj_live_..." });
|
|
136
|
+
The default base URL is `https://api.vidjutsu.ai`.
|
|
73
137
|
|
|
74
|
-
|
|
75
|
-
const vj = createClient({ baseUrl: "https://staging.api.vidjutsu.ai" });
|
|
76
|
-
```
|
|
138
|
+
## Methods
|
|
77
139
|
|
|
78
|
-
|
|
140
|
+
### Clone
|
|
79
141
|
|
|
80
|
-
|
|
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 |
|
|
83
|
-
|
|
84
|
-
| `
|
|
85
|
-
| `
|
|
86
|
-
| `
|
|
87
|
-
| `
|
|
88
|
-
| `
|
|
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
|
|
181
|
+
### Resources
|
|
91
182
|
|
|
92
183
|
| Method | Description |
|
|
93
184
|
|--------|-------------|
|
|
94
|
-
| `createAccount
|
|
95
|
-
| `createPost
|
|
96
|
-
| `createAsset
|
|
97
|
-
| `createReference
|
|
98
|
-
| `
|
|
99
|
-
| `
|
|
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
|
-
###
|
|
192
|
+
### Account and billing
|
|
102
193
|
|
|
103
194
|
| Method | Description |
|
|
104
195
|
|--------|-------------|
|
|
105
|
-
| `getInfo()` | API info
|
|
106
|
-
| `
|
|
107
|
-
| `
|
|
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
|
-
|
|
202
|
+
For any endpoint without a convenience method, use the raw `openapi-fetch` client:
|
|
116
203
|
|
|
117
204
|
```ts
|
|
118
|
-
const { data
|
|
119
|
-
const { data
|
|
120
|
-
body: {
|
|
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
|
-
##
|
|
211
|
+
## Pricing and billing
|
|
125
212
|
|
|
126
|
-
- **$99/month**
|
|
127
|
-
-
|
|
128
|
-
-
|
|
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
|
package/dist/cli/index.mjs
CHANGED
|
@@ -2190,21 +2190,26 @@ var init_dist2 = __esm(() => {
|
|
|
2190
2190
|
});
|
|
2191
2191
|
|
|
2192
2192
|
// src/cli/version.ts
|
|
2193
|
-
var VERSION = "1.
|
|
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
|
|
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: "
|
|
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
|
|
17
|
+
/** Create account Bearer API key required. */
|
|
18
18
|
createAccount(body: ReqBody<"/v1/accounts", "post">): ReturnType<Client["POST"]>;
|
|
19
|
-
/** Update account
|
|
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
|
|
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
|
|
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
|
|
27
|
+
/** Rotate API key Bearer API key required. */
|
|
28
28
|
rotateApiKey(): ReturnType<Client["POST"]>;
|
|
29
|
-
/** Create asset from existing URL
|
|
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
|
|
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
|
|
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)
|
|
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
|
-
/**
|
|
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
|
|
57
|
+
/** Get check rules Bearer API key required. */
|
|
44
58
|
getCheckRules(): ReturnType<Client["GET"]>;
|
|
45
|
-
/** Update check rules
|
|
59
|
+
/** Update check rules Bearer API key required. */
|
|
46
60
|
updateCheckRules(body: ReqBody<"/v1/check/rules", "put">): ReturnType<Client["PUT"]>;
|
|
47
|
-
/**
|
|
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
|
|
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
|
|
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
|
|
83
|
+
/** Create post Bearer API key required. */
|
|
62
84
|
createPost(body: ReqBody<"/v1/posts", "post">): ReturnType<Client["POST"]>;
|
|
63
|
-
/** Update post
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
135
|
+
/** Transcribe media Active subscription and daily quota required. */
|
|
82
136
|
transcribeMedia(body: ReqBody<"/v1/transcribe", "post">): ReturnType<Client["POST"]>;
|
|
83
|
-
/** Upload file
|
|
84
|
-
uploadFile(): ReturnType<Client["POST"]>;
|
|
85
|
-
/** Upload from URL
|
|
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
|
-
/**
|
|
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
|
|
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.
|
|
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",
|