vidjutsu 1.2.0 → 1.3.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
@@ -26,14 +26,14 @@ import { createClient } from "vidjutsu";
26
26
 
27
27
  const vj = createClient();
28
28
 
29
- // Watch — AI analyzes a video and answers your prompt (10 credits)
29
+ // Watch — AI analyzes a video and answers your prompt (50/day)
30
30
  const { data: watch } = await vj.watchMedia({
31
31
  mediaUrl: "https://cdn.example.com/video.mp4",
32
32
  prompt: "Is the hook strong enough for fitness TikTok?",
33
33
  });
34
34
  console.log(watch.response);
35
35
 
36
- // Extract — pull frames, audio, and metadata (5 credits)
36
+ // Extract — pull frames, audio, and metadata (100/day)
37
37
  const { data: extract } = await vj.extractMedia({
38
38
  mediaUrl: "https://cdn.example.com/video.mp4",
39
39
  frames: "auto",
@@ -41,13 +41,13 @@ const { data: extract } = await vj.extractMedia({
41
41
  });
42
42
  console.log(extract.frames, extract.metadata);
43
43
 
44
- // Transcribe — speech-to-text with word-level timing (10 credits)
44
+ // Transcribe — speech-to-text with word-level timing (30/day)
45
45
  const { data: transcript } = await vj.transcribeMedia({
46
46
  mediaUrl: "https://cdn.example.com/video.mp4",
47
47
  });
48
48
  console.log(transcript.transcript, transcript.words);
49
49
 
50
- // Overlay — burn text onto video (5 credits)
50
+ // Overlay — burn text onto video (50/day)
51
51
  const { data: overlay } = await vj.createOverlay({
52
52
  videoUrl: "https://cdn.example.com/video.mp4",
53
53
  text: "Follow for more tips",
@@ -77,17 +77,19 @@ const vj = createClient({ baseUrl: "https://staging.api.vidjutsu.ai" });
77
77
 
78
78
  ## All methods
79
79
 
80
- ### Video intelligence (paid)
80
+ ### Video intelligence (metered)
81
81
 
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 |
82
+ Requires an active subscription. Each endpoint has its own daily rate limit (fixed window, resets 00:00 UTC). Hitting a limit returns HTTP 429 with a `retryAfter` hint.
89
83
 
90
- ### Resources (free)
84
+ | Method | Daily limit | Description |
85
+ |--------|-------------|-------------|
86
+ | `watchMedia(body)` | 50 / day | AI watches a video/image and answers your prompt |
87
+ | `extractMedia(body)` | 100 / day | Extract frames, audio, and metadata from video |
88
+ | `transcribeMedia(body)` | 30 / day | Speech-to-text with word-level timing |
89
+ | `checkSpec(body)` | 100 / day | Validate a VidLang spec against rules |
90
+ | `createOverlay(body)` | 50 / day | Burn text overlay onto video |
91
+
92
+ ### Resources (unmetered)
91
93
 
92
94
  | Method | Description |
93
95
  |--------|-------------|
@@ -96,7 +98,6 @@ const vj = createClient({ baseUrl: "https://staging.api.vidjutsu.ai" });
96
98
  | `createAsset(body)` / `updateAsset(body, query)` / `listOrGetAssets(query)` / `deleteAsset(query)` | Manage assets |
97
99
  | `createReference(body)` / `updateReference(body, query)` / `listOrGetReferences(query)` / `deleteReference(query)` | Manage references |
98
100
  | `uploadFile(body)` / `uploadFromUrl(body)` | Upload media to CDN |
99
- | `getBalance()` | Check credit balance |
100
101
 
101
102
  ### Utilities (free)
102
103
 
@@ -115,17 +116,18 @@ const vj = createClient({ baseUrl: "https://staging.api.vidjutsu.ai" });
115
116
  Every method returns the `openapi-fetch` response shape: `{ data, error, response }`. For endpoints not covered by convenience methods, use the raw client:
116
117
 
117
118
  ```ts
118
- const { data, error } = await vj.api.GET("/v1/balance");
119
+ const { data, error } = await vj.api.GET("/v1/usage");
119
120
  const { data, error } = await vj.api.POST("/v1/watch", {
120
121
  body: { mediaUrl: "...", prompt: "..." },
121
122
  });
122
123
  ```
123
124
 
124
- ## Credits & billing
125
+ ## Pricing & billing
125
126
 
126
- - **$99/month** — 1,000 credits included
127
- - **Extra credits** — $0.10 each
128
- - No overage charges API returns 402 when credits run out
127
+ - **$99/month flat** — full API access via Stripe, billed monthly, cancel anytime.
128
+ - **No credits, no metered billing, no top-ups** — usage is governed by per-endpoint daily rate limits (reset 00:00 UTC), not a credit balance.
129
+ - Hitting a daily limit returns **HTTP 429** with a `retryAfter` hint. Check remaining capacity with `getUsage()` or `GET /v1/usage`.
130
+ - `GET /v1/pricing` is the single source of truth for current price and limits.
129
131
 
130
132
  ## Links
131
133
 
@@ -2197,14 +2197,19 @@ 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,
@@ -2601,19 +2607,21 @@ var init_status = __esm(() => {
2601
2607
  status_default = defineCommand({
2602
2608
  meta: { name: "status", description: "Check resource status by ID" },
2603
2609
  args: {
2604
- id: { type: "positional", description: "Resource ID (acc_xxx, post_xxx, ref_xxx)", required: true }
2610
+ id: { type: "positional", description: "Resource ID (job_xxx, acc_xxx, post_xxx, ref_xxx)", required: true }
2605
2611
  },
2606
2612
  async run({ args }) {
2607
2613
  const id = args.id;
2608
2614
  let path;
2609
- if (id.startsWith("acc_"))
2615
+ if (id.startsWith("job_"))
2616
+ path = `/v1/distribution/jobs?id=${id}`;
2617
+ else if (id.startsWith("acc_"))
2610
2618
  path = `/v1/accounts?id=${id}`;
2611
2619
  else if (id.startsWith("post_"))
2612
2620
  path = `/v1/posts?id=${id}`;
2613
2621
  else if (id.startsWith("ref_"))
2614
2622
  path = `/v1/references?id=${id}`;
2615
2623
  else {
2616
- console.error("Unknown ID prefix. Expected acc_, post_, or ref_");
2624
+ console.error("Unknown ID prefix. Expected job_, acc_, post_, or ref_");
2617
2625
  process.exit(1);
2618
2626
  }
2619
2627
  const result = await apiRequest("GET", path);
@@ -2622,6 +2630,148 @@ var init_status = __esm(() => {
2622
2630
  });
2623
2631
  });
2624
2632
 
2633
+ // src/cli/commands/balance.ts
2634
+ var exports_balance = {};
2635
+ __export(exports_balance, {
2636
+ default: () => balance_default
2637
+ });
2638
+ function fmtTs(ms) {
2639
+ const d2 = new Date(ms);
2640
+ const pad = (n2) => String(n2).padStart(2, "0");
2641
+ return `${d2.getUTCFullYear()}-${pad(d2.getUTCMonth() + 1)}-${pad(d2.getUTCDate())} ${pad(d2.getUTCHours())}:${pad(d2.getUTCMinutes())} UTC`;
2642
+ }
2643
+ function fmtDelta(n2) {
2644
+ const s2 = n2 >= 0 ? `+${n2}` : String(n2);
2645
+ return s2.padStart(6);
2646
+ }
2647
+ var balance_default;
2648
+ var init_balance = __esm(() => {
2649
+ init_dist2();
2650
+ init_client();
2651
+ balance_default = defineCommand({
2652
+ meta: {
2653
+ name: "balance",
2654
+ description: "Show credit balance and recent ledger entries"
2655
+ },
2656
+ args: {
2657
+ json: {
2658
+ type: "boolean",
2659
+ description: "Emit raw JSON instead of a table",
2660
+ default: false
2661
+ }
2662
+ },
2663
+ async run({ args }) {
2664
+ const res = await apiRequest("GET", "/v1/balance");
2665
+ if (args.json) {
2666
+ console.log(JSON.stringify(res, null, 2));
2667
+ return;
2668
+ }
2669
+ console.log(`Balance: ${res.balance} credits`);
2670
+ if (!res.ledger || res.ledger.length === 0) {
2671
+ console.log(`
2672
+ No ledger entries yet.`);
2673
+ return;
2674
+ }
2675
+ console.log(`
2676
+ Recent ledger (newest first):`);
2677
+ for (const e2 of res.ledger) {
2678
+ const detail = e2.operationId ?? e2.note ?? "";
2679
+ console.log(` ${fmtDelta(e2.delta)} ${e2.reason.padEnd(7)} ${fmtTs(e2.createdAt)} ${detail}`);
2680
+ }
2681
+ }
2682
+ });
2683
+ });
2684
+
2685
+ // src/cli/commands/admin.ts
2686
+ var exports_admin = {};
2687
+ __export(exports_admin, {
2688
+ default: () => admin_default
2689
+ });
2690
+ import { spawnSync } from "node:child_process";
2691
+ var grant, admin_default;
2692
+ var init_admin = __esm(() => {
2693
+ init_dist2();
2694
+ grant = defineCommand({
2695
+ meta: {
2696
+ name: "grant",
2697
+ description: "Grant credits to a client (looked up by email or clientId). Runs the Convex mutation directly via 'npx convex run'."
2698
+ },
2699
+ args: {
2700
+ email: {
2701
+ type: "string",
2702
+ description: "Email of the client to grant credits to"
2703
+ },
2704
+ client: {
2705
+ type: "string",
2706
+ description: "clientId of the client to grant credits to"
2707
+ },
2708
+ amount: {
2709
+ type: "string",
2710
+ description: "Number of credits to grant (positive integer)",
2711
+ required: true
2712
+ },
2713
+ note: {
2714
+ type: "string",
2715
+ description: "Optional admin note recorded on the ledger row"
2716
+ },
2717
+ repo: {
2718
+ type: "string",
2719
+ description: "Path to the vidjutsu Convex repo where 'npx convex run' executes (defaults to cwd)"
2720
+ }
2721
+ },
2722
+ async run({ args }) {
2723
+ if (!args.email && !args.client) {
2724
+ console.error("Error: provide --email <e> or --client <clientId>");
2725
+ process.exit(1);
2726
+ }
2727
+ const amount = Number(args.amount);
2728
+ if (!Number.isInteger(amount) || amount <= 0) {
2729
+ console.error("Error: --amount must be a positive integer");
2730
+ process.exit(1);
2731
+ }
2732
+ const payload = { amount };
2733
+ if (args.email)
2734
+ payload.email = args.email;
2735
+ if (args.client)
2736
+ payload.clientId = args.client;
2737
+ if (args.note)
2738
+ payload.note = args.note;
2739
+ const cwd = args.repo ?? process.cwd();
2740
+ const cmdArgs = [
2741
+ "convex",
2742
+ "run",
2743
+ "services/machineClients:grantCreditsByLookup",
2744
+ JSON.stringify(payload)
2745
+ ];
2746
+ const result = spawnSync("npx", cmdArgs, {
2747
+ cwd,
2748
+ encoding: "utf-8",
2749
+ stdio: ["ignore", "pipe", "pipe"]
2750
+ });
2751
+ if (result.error) {
2752
+ console.error("Error invoking npx convex:", result.error.message);
2753
+ process.exit(1);
2754
+ }
2755
+ if (result.stdout)
2756
+ process.stdout.write(result.stdout);
2757
+ if (result.stderr)
2758
+ process.stderr.write(result.stderr);
2759
+ if (result.status !== 0) {
2760
+ process.exit(result.status ?? 1);
2761
+ }
2762
+ }
2763
+ });
2764
+ admin_default = defineCommand({
2765
+ meta: {
2766
+ name: "admin",
2767
+ description: "Admin operations (credit grants, etc.)"
2768
+ },
2769
+ subCommands: {
2770
+ grant: () => grant
2771
+ }
2772
+ });
2773
+ });
2774
+
2625
2775
  // src/cli/commands/version.ts
2626
2776
  var exports_version = {};
2627
2777
  __export(exports_version, {
@@ -2716,6 +2866,293 @@ var init_update = __esm(() => {
2716
2866
  });
2717
2867
  });
2718
2868
 
2869
+ // src/cli/commands/jobs.ts
2870
+ var exports_jobs = {};
2871
+ __export(exports_jobs, {
2872
+ default: () => jobs_default
2873
+ });
2874
+ async function getJob(jobId) {
2875
+ return await apiRequest("GET", `/v1/jobs?id=${encodeURIComponent(jobId)}`);
2876
+ }
2877
+ var jobs_default;
2878
+ var init_jobs = __esm(() => {
2879
+ init_dist2();
2880
+ init_client();
2881
+ jobs_default = defineCommand({
2882
+ meta: { name: "jobs", description: "Inspect durable VidJutsu media jobs" },
2883
+ subCommands: {
2884
+ get: defineCommand({
2885
+ meta: { name: "get", description: "Get a job" },
2886
+ args: {
2887
+ id: { type: "positional", description: "Job ID", required: true }
2888
+ },
2889
+ async run({ args }) {
2890
+ console.log(JSON.stringify(await getJob(args.id), null, 2));
2891
+ }
2892
+ }),
2893
+ wait: defineCommand({
2894
+ meta: { name: "wait", description: "Wait for a job to complete" },
2895
+ args: {
2896
+ id: { type: "positional", description: "Job ID", required: true },
2897
+ timeout: { type: "string", description: "Timeout in seconds", default: "600" },
2898
+ interval: { type: "string", description: "Poll interval in milliseconds", default: "1000" }
2899
+ },
2900
+ async run({ args }) {
2901
+ const deadline = Date.now() + Number(args.timeout) * 1000;
2902
+ while (true) {
2903
+ const job = await getJob(args.id);
2904
+ if (job?.status === "completed") {
2905
+ console.log(JSON.stringify(job, null, 2));
2906
+ return;
2907
+ }
2908
+ if (job?.status === "failed" || job?.status === "cancelled") {
2909
+ throw new Error(job.error?.message ?? `Job ${job.status}`);
2910
+ }
2911
+ if (Date.now() >= deadline)
2912
+ throw new Error(`Timed out waiting for ${args.id}`);
2913
+ await new Promise((resolve) => setTimeout(resolve, Number(args.interval)));
2914
+ }
2915
+ }
2916
+ })
2917
+ }
2918
+ });
2919
+ });
2920
+
2921
+ // src/cli/commands/clone.ts
2922
+ var exports_clone = {};
2923
+ __export(exports_clone, {
2924
+ default: () => clone_default
2925
+ });
2926
+ function validateModel(model) {
2927
+ if (model === undefined)
2928
+ return;
2929
+ if (!VALID_MODELS.includes(model)) {
2930
+ throw new Error(`--model must be one of: ${VALID_MODELS.join(", ")}`);
2931
+ }
2932
+ return model;
2933
+ }
2934
+ function isCharacterId(value) {
2935
+ return value.startsWith("char_");
2936
+ }
2937
+ function characterRef(value) {
2938
+ return isCharacterId(value) ? { characterId: value } : { characterImageUrl: value };
2939
+ }
2940
+ async function pollCloneVideo(id, { intervalMs = 3000, timeoutMs = 600000 } = {}) {
2941
+ const deadline = Date.now() + timeoutMs;
2942
+ while (true) {
2943
+ const status = await apiRequest("GET", `/v1/clones/video/${encodeURIComponent(id)}`);
2944
+ if (status.status === "completed")
2945
+ return status;
2946
+ if (status.status === "failed")
2947
+ throw new Error(status.error ?? `Clone video ${id} failed`);
2948
+ if (Date.now() >= deadline)
2949
+ throw new Error(`Timed out waiting for clone video ${id}`);
2950
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
2951
+ }
2952
+ }
2953
+ var VALID_MODELS, clone_default;
2954
+ var init_clone = __esm(() => {
2955
+ init_dist2();
2956
+ init_client();
2957
+ VALID_MODELS = ["kling", "seedance"];
2958
+ clone_default = defineCommand({
2959
+ meta: {
2960
+ name: "clone",
2961
+ description: "Clone a source video's motion onto a new character. About 5 minutes end to end for the full chain. " + "Kling motion control is the default model; output is 480p 9:16. Source clips are capped at 15 seconds. " + "Each step below is metered per call."
2962
+ },
2963
+ subCommands: {
2964
+ check: defineCommand({
2965
+ meta: {
2966
+ name: "check",
2967
+ description: "Evaluate whether a source video can be cloned reliably. Metered per call. " + "Exits nonzero when the verdict is weak."
2968
+ },
2969
+ args: {
2970
+ videoUrl: { type: "positional", description: "Public HTTPS URL of the source video", required: true },
2971
+ context: { type: "string", description: "Optional context about the intended clone" }
2972
+ },
2973
+ async run({ args }) {
2974
+ const result = await apiRequest("POST", "/v1/clones/check", {
2975
+ videoUrl: args.videoUrl,
2976
+ context: args.context
2977
+ });
2978
+ console.log(`Verdict: ${result.verdict}`);
2979
+ console.log(`Score: ${result.score}/100`);
2980
+ console.log("Evidence:");
2981
+ for (const item of result.evidence ?? []) {
2982
+ console.log(` - ${item}`);
2983
+ }
2984
+ if (result.verdict === "weak") {
2985
+ process.exit(1);
2986
+ }
2987
+ }
2988
+ }),
2989
+ character: defineCommand({
2990
+ meta: {
2991
+ name: "character",
2992
+ 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>`). " + "Metered per call."
2993
+ },
2994
+ subCommands: {
2995
+ list: defineCommand({
2996
+ meta: {
2997
+ name: "list",
2998
+ description: "List your persisted characters. Reads are not billed."
2999
+ },
3000
+ async run() {
3001
+ const result = await apiRequest("GET", "/v1/characters");
3002
+ const characters = Array.isArray(result) ? result : result.characters ?? [];
3003
+ if (characters.length === 0) {
3004
+ console.log("No characters yet. Create one with: vidjutsu clone character --prompt ...");
3005
+ return;
3006
+ }
3007
+ for (const c3 of characters) {
3008
+ console.log(`${c3.id} ${c3.model} ${c3.createdAt}`);
3009
+ }
3010
+ }
3011
+ })
3012
+ },
3013
+ args: {
3014
+ prompt: { type: "string", description: "Text description of the character to generate" },
3015
+ reference: { type: "string", description: "Optional public HTTPS reference image URL to guide identity" }
3016
+ },
3017
+ async run({ args, rawArgs }) {
3018
+ if (rawArgs[0] === "list")
3019
+ return;
3020
+ if (!args.prompt) {
3021
+ throw new Error("--prompt is required to create a character (or run `vidjutsu clone character list`).");
3022
+ }
3023
+ const result = await apiRequest("POST", "/v1/characters", {
3024
+ prompt: args.prompt,
3025
+ referenceImageUrl: args.reference
3026
+ });
3027
+ console.log(`Character created: ${result.id}`);
3028
+ console.log(`Image: ${result.imageUrl}`);
3029
+ console.log(`Model: ${result.model}`);
3030
+ console.log("");
3031
+ console.log(`Reuse this character with: --character ${result.id}`);
3032
+ }
3033
+ }),
3034
+ "starting-image": defineCommand({
3035
+ meta: {
3036
+ name: "starting-image",
3037
+ description: "Create a character-swapped starting frame, with no overlays. --character accepts either a stored " + "character id (char_...) or a public HTTPS character image URL. Metered per call."
3038
+ },
3039
+ args: {
3040
+ character: {
3041
+ type: "string",
3042
+ description: "Stored character id (char_...) from `clone character`, or a public HTTPS character image URL",
3043
+ required: true
3044
+ },
3045
+ prompt: { type: "string", description: "Instructions for composing the starting frame", required: true },
3046
+ source: { type: "string", description: "Optional public HTTPS source video URL to ground the composition" }
3047
+ },
3048
+ async run({ args }) {
3049
+ const result = await apiRequest("POST", "/v1/clones/starting-image", {
3050
+ ...characterRef(args.character),
3051
+ prompt: args.prompt,
3052
+ sourceVideoUrl: args.source
3053
+ });
3054
+ console.log(result.imageUrl);
3055
+ }
3056
+ }),
3057
+ video: defineCommand({
3058
+ meta: {
3059
+ name: "video",
3060
+ description: "Clone source motion onto a starting image. Kling motion control is the default; seedance is the " + "alternate model, 9:16 at 480p. Metered per call. Returns a task id (HTTP 202); use --wait to poll to completion."
3061
+ },
3062
+ args: {
3063
+ "starting-image": { type: "string", description: "Public HTTPS URL of the starting frame", required: true },
3064
+ source: { type: "string", description: "Public HTTPS URL of the source video whose motion is cloned", required: true },
3065
+ model: { type: "string", description: "kling (default) or seedance" },
3066
+ prompt: { type: "string", description: "Optional override for the default identity-swap prompt (seedance only)" },
3067
+ wait: { type: "boolean", description: "Poll GET status until completed or failed, then print videoUrl" }
3068
+ },
3069
+ async run({ args }) {
3070
+ const model = validateModel(args.model);
3071
+ const accepted = await apiRequest("POST", "/v1/clones/video", {
3072
+ startingImageUrl: args["starting-image"],
3073
+ sourceVideoUrl: args.source,
3074
+ model,
3075
+ prompt: args.prompt
3076
+ });
3077
+ console.log(accepted.id);
3078
+ if (args.wait) {
3079
+ const final = await pollCloneVideo(accepted.id);
3080
+ console.log(final.videoUrl);
3081
+ }
3082
+ }
3083
+ }),
3084
+ status: defineCommand({
3085
+ meta: {
3086
+ name: "status",
3087
+ description: "Poll a clone video task by id. Status reads are not billed."
3088
+ },
3089
+ args: {
3090
+ id: { type: "positional", description: "Clone video task id", required: true }
3091
+ },
3092
+ async run({ args }) {
3093
+ const result = await apiRequest("GET", `/v1/clones/video/${encodeURIComponent(args.id)}`);
3094
+ console.log(JSON.stringify(result, null, 2));
3095
+ }
3096
+ }),
3097
+ run: defineCommand({
3098
+ meta: {
3099
+ name: "run",
3100
+ 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 motion control is the default model; output is 480p 9:16, source clips " + "are capped at 15 seconds. Each step is metered per call. Stops before spending on generation when the " + "check verdict is weak, unless --force is given."
3101
+ },
3102
+ args: {
3103
+ tiktokUrl: { type: "positional", description: "TikTok video URL to clone", required: true },
3104
+ character: {
3105
+ type: "string",
3106
+ 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."
3107
+ },
3108
+ "starting-prompt": { type: "string", description: "Prompt for the starting frame composition" },
3109
+ model: { type: "string", description: "kling (default) or seedance" },
3110
+ force: { type: "boolean", description: "Proceed even if the clone check verdict is weak" }
3111
+ },
3112
+ async run({ args }) {
3113
+ const model = validateModel(args.model);
3114
+ if (!args.character) {
3115
+ console.log("Missing --character. Create a reusable character first with: " + 'vidjutsu clone character --prompt "A neutral, camera-ready presenter"');
3116
+ console.log("Then pass its id here: vidjutsu clone run <url> --character char_...");
3117
+ process.exit(1);
3118
+ }
3119
+ console.log(`Downloading ${args.tiktokUrl}...`);
3120
+ const download = await apiRequest("POST", "/v1/videos/download/tiktok", {
3121
+ url: args.tiktokUrl
3122
+ });
3123
+ console.log("Checking cloneability...");
3124
+ const check = await apiRequest("POST", "/v1/clones/check", {
3125
+ videoUrl: download.url
3126
+ });
3127
+ console.log(`Verdict: ${check.verdict} (score ${check.score}/100)`);
3128
+ for (const item of check.evidence ?? []) {
3129
+ console.log(` - ${item}`);
3130
+ }
3131
+ if (check.verdict === "weak" && !args.force) {
3132
+ console.log("Stopping: the source clip is unlikely to clone well. Pass --force to continue anyway.");
3133
+ process.exit(1);
3134
+ }
3135
+ console.log("Generating starting image...");
3136
+ const startingImage = await apiRequest("POST", "/v1/clones/starting-image", {
3137
+ ...characterRef(args.character),
3138
+ prompt: args["starting-prompt"] ?? "Match the framing and pose of the source video's opening frame",
3139
+ sourceVideoUrl: download.url
3140
+ });
3141
+ console.log("Generating clone video...");
3142
+ const accepted = await apiRequest("POST", "/v1/clones/video", {
3143
+ startingImageUrl: startingImage.imageUrl,
3144
+ sourceVideoUrl: download.url,
3145
+ model
3146
+ });
3147
+ console.log(`Task ${accepted.id} accepted, polling...`);
3148
+ const final = await pollCloneVideo(accepted.id);
3149
+ console.log(final.videoUrl);
3150
+ }
3151
+ })
3152
+ }
3153
+ });
3154
+ });
3155
+
2719
3156
  // src/cli/commands/generated/watch.ts
2720
3157
  var exports_watch = {};
2721
3158
  __export(exports_watch, {
@@ -3377,6 +3814,110 @@ var init_info = __esm(() => {
3377
3814
  });
3378
3815
  });
3379
3816
 
3817
+ // src/cli/commands/generated/project.ts
3818
+ var exports_project = {};
3819
+ __export(exports_project, {
3820
+ default: () => project_default
3821
+ });
3822
+ function parseTags5(raw) {
3823
+ if (!raw)
3824
+ return;
3825
+ return raw.split(",").map((pair) => {
3826
+ const [key, ...rest] = pair.split("=");
3827
+ return { key: key.trim(), value: rest.join("=").trim() };
3828
+ });
3829
+ }
3830
+ var project_default;
3831
+ var init_project = __esm(() => {
3832
+ init_dist2();
3833
+ init_client();
3834
+ project_default = defineCommand({
3835
+ meta: { name: "project", description: "Manage projects" },
3836
+ subCommands: {
3837
+ create: defineCommand({
3838
+ meta: { name: "create", description: "Create an editor project" },
3839
+ args: {
3840
+ name: { type: "string", description: "name", required: true },
3841
+ project: { type: "string", description: "project" },
3842
+ tags: { type: "string", description: "Tags as key=value pairs, comma-separated" },
3843
+ metadata: { type: "string", description: "metadata" }
3844
+ },
3845
+ async run({ args }) {
3846
+ const body = {};
3847
+ if (args["name"] !== undefined)
3848
+ body["name"] = args["name"];
3849
+ if (args["project"] !== undefined)
3850
+ body["project"] = args["project"];
3851
+ const tags = parseTags5(args.tags);
3852
+ if (tags)
3853
+ body.tags = tags;
3854
+ if (args["metadata"] !== undefined)
3855
+ body["metadata"] = args["metadata"];
3856
+ const result = await apiRequest("POST", "/v1/projects", body);
3857
+ console.log(JSON.stringify(result, null, 2));
3858
+ }
3859
+ }),
3860
+ edit: defineCommand({
3861
+ meta: { name: "edit", description: "Edit an editor project" },
3862
+ args: {
3863
+ id: { type: "positional", description: "Project ID", required: true },
3864
+ name: { type: "string", description: "name" },
3865
+ status: { type: "string", description: "status" },
3866
+ project: { type: "string", description: "project" },
3867
+ tags: { type: "string", description: "Tags as key=value pairs, comma-separated" },
3868
+ metadata: { type: "string", description: "metadata" }
3869
+ },
3870
+ async run({ args }) {
3871
+ const body = {};
3872
+ if (args["name"] !== undefined)
3873
+ body["name"] = args["name"];
3874
+ if (args["status"] !== undefined)
3875
+ body["status"] = args["status"];
3876
+ if (args["project"] !== undefined)
3877
+ body["project"] = args["project"];
3878
+ const tags = parseTags5(args.tags);
3879
+ if (tags)
3880
+ body.tags = tags;
3881
+ if (args["metadata"] !== undefined)
3882
+ body["metadata"] = args["metadata"];
3883
+ const result = await apiRequest("PUT", "/v1/projects?id=" + args.id, body);
3884
+ console.log(JSON.stringify(result, null, 2));
3885
+ }
3886
+ }),
3887
+ list: defineCommand({
3888
+ meta: { name: "list", description: "List editor projects" },
3889
+ args: {
3890
+ id: { type: "string", description: "Project ID (omit to list all)" },
3891
+ status: { type: "string", description: "status" }
3892
+ },
3893
+ async run({ args }) {
3894
+ let path = "/v1/projects";
3895
+ const params = new URLSearchParams;
3896
+ if (args["id"])
3897
+ params.set("id", args["id"]);
3898
+ if (args["status"])
3899
+ params.set("status", args["status"]);
3900
+ const qs = params.toString();
3901
+ if (qs)
3902
+ path += "?" + qs;
3903
+ const result = await apiRequest("GET", path);
3904
+ console.log(JSON.stringify(result, null, 2));
3905
+ }
3906
+ }),
3907
+ delete: defineCommand({
3908
+ meta: { name: "delete", description: "Archive an editor project" },
3909
+ args: {
3910
+ id: { type: "positional", description: "Project ID", required: true }
3911
+ },
3912
+ async run({ args }) {
3913
+ const result = await apiRequest("DELETE", "/v1/projects?id=" + args.id);
3914
+ console.log(JSON.stringify(result, null, 2));
3915
+ }
3916
+ })
3917
+ }
3918
+ });
3919
+ });
3920
+
3380
3921
  // src/cli/index.ts
3381
3922
  init_dist2();
3382
3923
  var main = defineCommand({
@@ -3391,8 +3932,12 @@ var main = defineCommand({
3391
3932
  upload: () => Promise.resolve().then(() => (init_upload(), exports_upload)).then((m2) => m2.default),
3392
3933
  subscribe: () => Promise.resolve().then(() => (init_subscribe(), exports_subscribe)).then((m2) => m2.default),
3393
3934
  status: () => Promise.resolve().then(() => (init_status(), exports_status)).then((m2) => m2.default),
3935
+ balance: () => Promise.resolve().then(() => (init_balance(), exports_balance)).then((m2) => m2.default),
3936
+ admin: () => Promise.resolve().then(() => (init_admin(), exports_admin)).then((m2) => m2.default),
3394
3937
  version: () => Promise.resolve().then(() => (init_version(), exports_version)).then((m2) => m2.default),
3395
3938
  update: () => Promise.resolve().then(() => (init_update(), exports_update)).then((m2) => m2.default),
3939
+ jobs: () => Promise.resolve().then(() => (init_jobs(), exports_jobs)).then((m2) => m2.default),
3940
+ clone: () => Promise.resolve().then(() => (init_clone(), exports_clone)).then((m2) => m2.default),
3396
3941
  watch: () => Promise.resolve().then(() => (init_watch(), exports_watch)).then((m2) => m2.default),
3397
3942
  extract: () => Promise.resolve().then(() => (init_extract(), exports_extract)).then((m2) => m2.default),
3398
3943
  transcribe: () => Promise.resolve().then(() => (init_transcribe(), exports_transcribe)).then((m2) => m2.default),
@@ -3403,7 +3948,8 @@ var main = defineCommand({
3403
3948
  asset: () => Promise.resolve().then(() => (init_asset(), exports_asset)).then((m2) => m2.default),
3404
3949
  reference: () => Promise.resolve().then(() => (init_reference(), exports_reference)).then((m2) => m2.default),
3405
3950
  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)
3951
+ info: () => Promise.resolve().then(() => (init_info(), exports_info)).then((m2) => m2.default),
3952
+ project: () => Promise.resolve().then(() => (init_project(), exports_project)).then((m2) => m2.default)
3407
3953
  }
3408
3954
  });
3409
3955
  runMain(main);
package/dist/client.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import createFetchClient from "openapi-fetch";
2
2
  import type { paths } from "./schema.js";
3
3
  import { type VidJutsuMethods } from "./methods.js";
4
+ import { type DistributionNamespaces } from "./distribution.js";
5
+ import { type JobsNamespaces } from "./jobs.js";
4
6
  export interface VidJutsuConfig {
5
7
  /** API key. Falls back to VIDJUTSU_API_KEY env var, then ~/.vidjutsu/config.json. */
6
8
  apiKey?: string;
@@ -10,7 +12,7 @@ export interface VidJutsuConfig {
10
12
  /** The raw openapi-fetch client with typed GET/POST/PUT/DELETE */
11
13
  type FetchClient = ReturnType<typeof createFetchClient<paths>>;
12
14
  /** Combined client: typed convenience methods + raw openapi-fetch escape hatch */
13
- export type VidJutsuClient = VidJutsuMethods & {
15
+ export type VidJutsuClient = VidJutsuMethods & DistributionNamespaces & JobsNamespaces & {
14
16
  api: FetchClient;
15
17
  };
16
18
  /**
package/dist/client.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import createFetchClient from "openapi-fetch";
2
2
  import { bindMethods } from "./methods.js";
3
+ import { bindDistribution } from "./distribution.js";
4
+ import { bindJobs } from "./jobs.js";
3
5
  import { readFileSync, existsSync } from "fs";
4
6
  import { join } from "path";
5
7
  import { homedir } from "os";
@@ -50,5 +52,7 @@ export function createClient(config = {}) {
50
52
  },
51
53
  });
52
54
  const methods = bindMethods(fetchClient);
53
- return { ...methods, api: fetchClient };
55
+ const distribution = bindDistribution(fetchClient);
56
+ const jobs = bindJobs(fetchClient);
57
+ return { ...methods, ...distribution, ...jobs, api: fetchClient };
54
58
  }
@@ -0,0 +1,23 @@
1
+ import type createFetchClient from "openapi-fetch";
2
+ import type { components, paths } from "./schema.js";
3
+ type FetchClient = ReturnType<typeof createFetchClient<paths>>;
4
+ type Job = components["schemas"]["DistributionJob"];
5
+ type AddVideoRequest = components["schemas"]["AddVideoRequest"];
6
+ export interface MutationOptions {
7
+ idempotencyKey?: string;
8
+ }
9
+ export interface WaitOptions {
10
+ timeoutMs?: number;
11
+ pollIntervalMs?: number;
12
+ }
13
+ export interface DistributionNamespaces {
14
+ videos: {
15
+ add(body: AddVideoRequest, options?: MutationOptions): Promise<Job>;
16
+ };
17
+ jobs: {
18
+ get(jobId: string): Promise<Job>;
19
+ waitForResult(jobId: string, options?: WaitOptions): Promise<Job>;
20
+ };
21
+ }
22
+ export declare function bindDistribution(client: FetchClient): DistributionNamespaces;
23
+ export {};
@@ -0,0 +1,56 @@
1
+ export function bindDistribution(client) {
2
+ const jobs = {
3
+ async get(jobId) {
4
+ const result = await client.GET("/v1/distribution/jobs", {
5
+ params: { query: { id: jobId } },
6
+ });
7
+ return requireData(result, "get distribution job");
8
+ },
9
+ async waitForResult(jobId, options = {}) {
10
+ const timeoutMs = options.timeoutMs ?? 10 * 60_000;
11
+ const pollIntervalMs = options.pollIntervalMs ?? 1_000;
12
+ const deadline = Date.now() + timeoutMs;
13
+ while (true) {
14
+ const job = await jobs.get(jobId);
15
+ if (job.status === "completed")
16
+ return job;
17
+ if (job.status === "failed" || job.status === "cancelled") {
18
+ throw new Error(job.error?.message ?? `Distribution job ${job.status}`);
19
+ }
20
+ if (Date.now() >= deadline)
21
+ throw new Error(`Timed out waiting for distribution job ${jobId}`);
22
+ await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
23
+ }
24
+ },
25
+ };
26
+ return {
27
+ videos: {
28
+ async add(body, options) {
29
+ const result = await client.POST("/v1/videos/add", {
30
+ body,
31
+ params: { header: idempotencyHeader(options) },
32
+ });
33
+ return requireData(result, "add video");
34
+ },
35
+ },
36
+ jobs,
37
+ };
38
+ }
39
+ function idempotencyHeader(options) {
40
+ return options?.idempotencyKey ? { "Idempotency-Key": options.idempotencyKey } : {};
41
+ }
42
+ function requireData(result, operation) {
43
+ if (result.data !== undefined)
44
+ return result.data;
45
+ const message = extractErrorMessage(result.error) ?? `Vidjutsu failed to ${operation} (HTTP ${result.response.status})`;
46
+ throw new Error(message);
47
+ }
48
+ function extractErrorMessage(error) {
49
+ if (!error || typeof error !== "object")
50
+ return undefined;
51
+ if ("message" in error && typeof error.message === "string")
52
+ return error.message;
53
+ if ("error" in error && typeof error.error === "string")
54
+ return error.error;
55
+ return undefined;
56
+ }
package/dist/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  export { createClient } from "./client.js";
2
2
  export type { VidJutsuConfig, VidJutsuClient } from "./client.js";
3
3
  export type { VidJutsuMethods } from "./methods.js";
4
+ export type { DistributionNamespaces, MutationOptions, WaitOptions } from "./distribution.js";
5
+ export type { JobsNamespaces } from "./jobs.js";
4
6
  export type { paths, components } from "./schema.js";
package/dist/jobs.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ import type createFetchClient from "openapi-fetch";
2
+ import type { components, paths } from "./schema.js";
3
+ import type { WaitOptions } from "./distribution.js";
4
+ type FetchClient = ReturnType<typeof createFetchClient<paths>>;
5
+ type MediaJob = components["schemas"]["MediaJob"];
6
+ export interface JobsNamespaces {
7
+ mediaJobs: {
8
+ get(jobId: string): Promise<MediaJob>;
9
+ awaitJob(jobId: string, options?: WaitOptions): Promise<MediaJob>;
10
+ };
11
+ awaitJob(jobId: string, options?: WaitOptions): Promise<MediaJob>;
12
+ }
13
+ export declare function bindJobs(client: FetchClient): JobsNamespaces;
14
+ export {};
package/dist/jobs.js ADDED
@@ -0,0 +1,50 @@
1
+ export function bindJobs(client) {
2
+ const mediaJobs = {
3
+ async get(jobId) {
4
+ const result = await client.GET("/v1/jobs", {
5
+ params: { query: { id: jobId } },
6
+ });
7
+ return requireData(result, "get media job");
8
+ },
9
+ async awaitJob(jobId, options = {}) {
10
+ const timeoutMs = options.timeoutMs ?? 10 * 60_000;
11
+ const pollIntervalMs = options.pollIntervalMs ?? 1_000;
12
+ if (timeoutMs <= 0 || pollIntervalMs <= 0) {
13
+ throw new Error("timeoutMs and pollIntervalMs must be positive");
14
+ }
15
+ const deadline = Date.now() + timeoutMs;
16
+ while (true) {
17
+ const job = await mediaJobs.get(jobId);
18
+ if (job.status === "completed")
19
+ return job;
20
+ if (job.status === "failed" || job.status === "cancelled") {
21
+ throw new Error(job.error?.message ?? `Media job ${job.status}`);
22
+ }
23
+ if (Date.now() >= deadline) {
24
+ throw new Error(`Timed out waiting for media job ${jobId}`);
25
+ }
26
+ await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
27
+ }
28
+ },
29
+ };
30
+ return {
31
+ mediaJobs,
32
+ awaitJob: mediaJobs.awaitJob,
33
+ };
34
+ }
35
+ function requireData(result, operation) {
36
+ if (result.data !== undefined)
37
+ return result.data;
38
+ const message = extractErrorMessage(result.error) ??
39
+ `VidJutsu failed to ${operation} (HTTP ${result.response.status})`;
40
+ throw new Error(message);
41
+ }
42
+ function extractErrorMessage(error) {
43
+ if (!error || typeof error !== "object")
44
+ return undefined;
45
+ if ("message" in error && typeof error.message === "string")
46
+ return error.message;
47
+ if ("error" in error && typeof error.error === "string")
48
+ return error.error;
49
+ return undefined;
50
+ }
package/dist/methods.d.ts CHANGED
@@ -34,16 +34,38 @@ export interface VidJutsuMethods {
34
34
  listOrGetAssets(query?: QueryParams<"/v1/assets", "get">): ReturnType<Client["GET"]>;
35
35
  /** Delete asset (soft) Auth required. */
36
36
  deleteAsset(query?: QueryParams<"/v1/assets", "delete">): ReturnType<Client["DELETE"]>;
37
+ /** Agent registration (auth.md) */
38
+ signupAgent(body: ReqBody<"/v1/auth/agent", "post">): ReturnType<Client["POST"]>;
39
+ /** Initiate claim flow */
40
+ agentClaim(body: ReqBody<"/v1/auth/agent/claim", "post">): ReturnType<Client["POST"]>;
41
+ /** Complete claim flow */
42
+ agentClaimComplete(body: ReqBody<"/v1/auth/agent/claim/complete", "post">): ReturnType<Client["POST"]>;
43
+ /** Revoke credential */
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"]>;
49
+ /** Create a reusable, persisted character image */
50
+ createCharacter(body: ReqBody<"/v1/characters", "post">): ReturnType<Client["POST"]>;
51
+ /** List your persisted characters */
52
+ listCharacters(): ReturnType<Client["GET"]>;
53
+ /** Fetch a persisted character by id */
54
+ getCharacter(id: string): ReturnType<Client["GET"]>;
41
55
  /** Check spec Auth required. 5 credits. */
42
56
  checkSpec(body: ReqBody<"/v1/check", "post">): ReturnType<Client["POST"]>;
43
57
  /** Get check rules Auth required. */
44
58
  getCheckRules(): ReturnType<Client["GET"]>;
45
59
  /** Update check rules Auth required. */
46
60
  updateCheckRules(body: ReqBody<"/v1/check/rules", "put">): ReturnType<Client["PUT"]>;
61
+ /** Evaluate whether a source video can be cloned reliably */
62
+ cloneCheck(body: ReqBody<"/v1/clones/check", "post">): ReturnType<Client["POST"]>;
63
+ /** Create a character-swapped starting frame */
64
+ cloneStartingImage(body: ReqBody<"/v1/clones/starting-image", "post">): ReturnType<Client["POST"]>;
65
+ /** Clone source motion with Kling motion control or Seedance */
66
+ cloneVideo(body: ReqBody<"/v1/clones/video", "post">): ReturnType<Client["POST"]>;
67
+ /** Poll a clone video task */
68
+ getCloneVideo(id: string): ReturnType<Client["GET"]>;
47
69
  /** Check prompt compliance */
48
70
  checkCompliancePrompt(body: ReqBody<"/v1/compliance/prompt", "post">): ReturnType<Client["POST"]>;
49
71
  /** Check video compliance */
@@ -52,10 +74,14 @@ export interface VidJutsuMethods {
52
74
  getCheckoutStatus(query?: QueryParams<"/v1/credits/status", "get">): ReturnType<Client["GET"]>;
53
75
  /** Burn fine-print disclaimer onto video */
54
76
  createDisclaimer(body: ReqBody<"/v1/disclaimer", "post">): ReturnType<Client["POST"]>;
77
+ /** Inspect a distribution job Auth required. */
78
+ getDistributionJob(query?: QueryParams<"/v1/distribution/jobs", "get">): ReturnType<Client["GET"]>;
55
79
  /** Extract from media Auth required. 5 credits. */
56
80
  extractMedia(body: ReqBody<"/v1/extract", "post">): ReturnType<Client["POST"]>;
57
81
  /** API info Public endpoint. */
58
82
  getInfo(): ReturnType<Client["GET"]>;
83
+ /** Inspect a durable VidJutsu media job Auth required. */
84
+ getJob(query?: QueryParams<"/v1/jobs", "get">): ReturnType<Client["GET"]>;
59
85
  /** Burn text overlay onto video Auth required. 5 credits. */
60
86
  createOverlay(body: ReqBody<"/v1/overlay", "post">): ReturnType<Client["POST"]>;
61
87
  /** Create post Auth required. */
@@ -68,6 +94,14 @@ export interface VidJutsuMethods {
68
94
  deletePost(query?: QueryParams<"/v1/posts", "delete">): ReturnType<Client["DELETE"]>;
69
95
  /** Get pricing Public endpoint. */
70
96
  getPricing(): ReturnType<Client["GET"]>;
97
+ /** Create editor project */
98
+ createEditorProject(body: ReqBody<"/v1/projects", "post">): ReturnType<Client["POST"]>;
99
+ /** Update editor project */
100
+ updateEditorProject(body: ReqBody<"/v1/projects", "put">, query?: QueryParams<"/v1/projects", "put">): ReturnType<Client["PUT"]>;
101
+ /** List projects or get by ID */
102
+ listOrGetEditorProjects(query?: QueryParams<"/v1/projects", "get">): ReturnType<Client["GET"]>;
103
+ /** Archive editor project (soft) */
104
+ deleteEditorProject(query?: QueryParams<"/v1/projects", "delete">): ReturnType<Client["DELETE"]>;
71
105
  /** Create reference Auth required. */
72
106
  createReference(body: ReqBody<"/v1/references", "post">): ReturnType<Client["POST"]>;
73
107
  /** Update reference Auth required. */
@@ -76,6 +110,30 @@ export interface VidJutsuMethods {
76
110
  listOrGetReferences(query?: QueryParams<"/v1/references", "get">): ReturnType<Client["GET"]>;
77
111
  /** Delete reference Auth required. */
78
112
  deleteReference(query?: QueryParams<"/v1/references", "delete">): ReturnType<Client["DELETE"]>;
113
+ /** Scrape Instagram post or reel Auth required. 1 credits. */
114
+ scrapeInstagramPost(body: ReqBody<"/v1/scrape/instagram/post", "post">): ReturnType<Client["POST"]>;
115
+ /** Scrape Instagram post comments Auth required. 1 credits. */
116
+ scrapeInstagramPostComments(body: ReqBody<"/v1/scrape/instagram/post/comments", "post">): ReturnType<Client["POST"]>;
117
+ /** Scrape Instagram profile Auth required. 1 credits. */
118
+ scrapeInstagramProfile(body: ReqBody<"/v1/scrape/instagram/profile", "post">): ReturnType<Client["POST"]>;
119
+ /** Scrape Instagram user posts Auth required. 1 credits. */
120
+ scrapeInstagramUserPosts(body: ReqBody<"/v1/scrape/instagram/user/posts", "post">): ReturnType<Client["POST"]>;
121
+ /** Scrape Instagram user reels Auth required. 1 credits. */
122
+ scrapeInstagramUserReels(body: ReqBody<"/v1/scrape/instagram/user/reels", "post">): ReturnType<Client["POST"]>;
123
+ /** Scrape TikTok profile Auth required. 1 credits. */
124
+ scrapeTikTokProfile(body: ReqBody<"/v1/scrape/tiktok/profile", "post">): ReturnType<Client["POST"]>;
125
+ /** Scrape TikTok profile videos Auth required. 1 credits. */
126
+ scrapeTikTokProfileVideos(body: ReqBody<"/v1/scrape/tiktok/profile/videos", "post">): ReturnType<Client["POST"]>;
127
+ /** Scrape TikTok user search Auth required. 1 credits. */
128
+ scrapeTikTokSearchUsers(body: ReqBody<"/v1/scrape/tiktok/search/users", "post">): ReturnType<Client["POST"]>;
129
+ /** Scrape TikTok trending feed Auth required. 1 credits. */
130
+ scrapeTikTokTrending(body: ReqBody<"/v1/scrape/tiktok/trending", "post">): ReturnType<Client["POST"]>;
131
+ /** Scrape TikTok video Auth required. 1 credits. */
132
+ scrapeTikTokVideo(body: ReqBody<"/v1/scrape/tiktok/video", "post">): ReturnType<Client["POST"]>;
133
+ /** Scrape TikTok video comments Auth required. 1 credits. */
134
+ scrapeTikTokVideoComments(body: ReqBody<"/v1/scrape/tiktok/video/comments", "post">): ReturnType<Client["POST"]>;
135
+ /** Scrape TikTok video transcript Auth required. 1 credits. */
136
+ scrapeTikTokVideoTranscript(body: ReqBody<"/v1/scrape/tiktok/video/transcript", "post">): ReturnType<Client["POST"]>;
79
137
  /** Create subscription Public endpoint. */
80
138
  createSubscription(body: ReqBody<"/v1/subscribe", "post">): ReturnType<Client["POST"]>;
81
139
  /** Transcribe media Auth required. 10 credits. */
@@ -86,6 +144,12 @@ export interface VidJutsuMethods {
86
144
  uploadFromUrl(body: ReqBody<"/v1/upload/url", "post">): ReturnType<Client["POST"]>;
87
145
  /** Get daily usage */
88
146
  getUsage(): ReturnType<Client["GET"]>;
147
+ /** Add a video from an uploaded asset or direct MP4 URL Auth required. */
148
+ addVideo(body: ReqBody<"/v1/videos/add", "post">): ReturnType<Client["POST"]>;
149
+ /** Import an Instagram video into VidJutsu Auth required. 1 credits. */
150
+ downloadInstagramVideo(body: ReqBody<"/v1/videos/download/instagram", "post">): ReturnType<Client["POST"]>;
151
+ /** Import a TikTok video into VidJutsu Auth required. 1 credits. */
152
+ downloadTikTokVideo(body: ReqBody<"/v1/videos/download/tiktok", "post">): ReturnType<Client["POST"]>;
89
153
  /** Watch media Auth required. 10 credits. */
90
154
  watchMedia(body: ReqBody<"/v1/watch", "post">): ReturnType<Client["POST"]>;
91
155
  }
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
  },
@@ -57,12 +90,18 @@ export function bindMethods(client) {
57
90
  createDisclaimer(body) {
58
91
  return client.POST("/v1/disclaimer", { body });
59
92
  },
93
+ getDistributionJob(query) {
94
+ return client.GET("/v1/distribution/jobs", { params: { query } });
95
+ },
60
96
  extractMedia(body) {
61
97
  return client.POST("/v1/extract", { body });
62
98
  },
63
99
  getInfo() {
64
100
  return client.GET("/v1/info", {});
65
101
  },
102
+ getJob(query) {
103
+ return client.GET("/v1/jobs", { params: { query } });
104
+ },
66
105
  createOverlay(body) {
67
106
  return client.POST("/v1/overlay", { body });
68
107
  },
@@ -81,6 +120,18 @@ export function bindMethods(client) {
81
120
  getPricing() {
82
121
  return client.GET("/v1/pricing", {});
83
122
  },
123
+ createEditorProject(body) {
124
+ return client.POST("/v1/projects", { body });
125
+ },
126
+ updateEditorProject(body, query) {
127
+ return client.PUT("/v1/projects", { body, params: { query } });
128
+ },
129
+ listOrGetEditorProjects(query) {
130
+ return client.GET("/v1/projects", { params: { query } });
131
+ },
132
+ deleteEditorProject(query) {
133
+ return client.DELETE("/v1/projects", { params: { query } });
134
+ },
84
135
  createReference(body) {
85
136
  return client.POST("/v1/references", { body });
86
137
  },
@@ -93,6 +144,42 @@ export function bindMethods(client) {
93
144
  deleteReference(query) {
94
145
  return client.DELETE("/v1/references", { params: { query } });
95
146
  },
147
+ scrapeInstagramPost(body) {
148
+ return client.POST("/v1/scrape/instagram/post", { body });
149
+ },
150
+ scrapeInstagramPostComments(body) {
151
+ return client.POST("/v1/scrape/instagram/post/comments", { body });
152
+ },
153
+ scrapeInstagramProfile(body) {
154
+ return client.POST("/v1/scrape/instagram/profile", { body });
155
+ },
156
+ scrapeInstagramUserPosts(body) {
157
+ return client.POST("/v1/scrape/instagram/user/posts", { body });
158
+ },
159
+ scrapeInstagramUserReels(body) {
160
+ return client.POST("/v1/scrape/instagram/user/reels", { body });
161
+ },
162
+ scrapeTikTokProfile(body) {
163
+ return client.POST("/v1/scrape/tiktok/profile", { body });
164
+ },
165
+ scrapeTikTokProfileVideos(body) {
166
+ return client.POST("/v1/scrape/tiktok/profile/videos", { body });
167
+ },
168
+ scrapeTikTokSearchUsers(body) {
169
+ return client.POST("/v1/scrape/tiktok/search/users", { body });
170
+ },
171
+ scrapeTikTokTrending(body) {
172
+ return client.POST("/v1/scrape/tiktok/trending", { body });
173
+ },
174
+ scrapeTikTokVideo(body) {
175
+ return client.POST("/v1/scrape/tiktok/video", { body });
176
+ },
177
+ scrapeTikTokVideoComments(body) {
178
+ return client.POST("/v1/scrape/tiktok/video/comments", { body });
179
+ },
180
+ scrapeTikTokVideoTranscript(body) {
181
+ return client.POST("/v1/scrape/tiktok/video/transcript", { body });
182
+ },
96
183
  createSubscription(body) {
97
184
  return client.POST("/v1/subscribe", { body });
98
185
  },
@@ -108,6 +195,15 @@ export function bindMethods(client) {
108
195
  getUsage() {
109
196
  return client.GET("/v1/usage", {});
110
197
  },
198
+ addVideo(body) {
199
+ return client.POST("/v1/videos/add", { body });
200
+ },
201
+ downloadInstagramVideo(body) {
202
+ return client.POST("/v1/videos/download/instagram", { body });
203
+ },
204
+ downloadTikTokVideo(body) {
205
+ return client.POST("/v1/videos/download/tiktok", { body });
206
+ },
111
207
  watchMedia(body) {
112
208
  return client.POST("/v1/watch", { body });
113
209
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vidjutsu",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "VidJutsu — video intelligence API. SDK + CLI.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",