varg.ai-sdk 0.1.0 → 0.4.0-alpha.1
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/.claude/settings.local.json +1 -1
- package/.env.example +3 -0
- package/.github/workflows/ci.yml +23 -0
- package/.husky/README.md +102 -0
- package/.husky/commit-msg +6 -0
- package/.husky/pre-commit +9 -0
- package/.husky/pre-push +6 -0
- package/.size-limit.json +8 -0
- package/.test-hooks.ts +5 -0
- package/CLAUDE.md +10 -3
- package/CONTRIBUTING.md +150 -0
- package/LICENSE.md +53 -0
- package/README.md +56 -209
- package/SKILLS.md +26 -10
- package/biome.json +7 -1
- package/bun.lock +1286 -0
- package/commitlint.config.js +22 -0
- package/docs/index.html +1130 -0
- package/docs/prompting.md +326 -0
- package/docs/react.md +834 -0
- package/docs/sdk.md +812 -0
- package/ffmpeg/CLAUDE.md +68 -0
- package/package.json +48 -8
- package/pipeline/cookbooks/scripts/animate-frames-parallel.ts +84 -0
- package/pipeline/cookbooks/scripts/combine-scenes.sh +53 -0
- package/pipeline/cookbooks/scripts/generate-frames-parallel.ts +99 -0
- package/pipeline/cookbooks/scripts/still-to-video.sh +37 -0
- package/pipeline/cookbooks/text-to-tiktok.md +669 -0
- package/pipeline/cookbooks/trendwatching.md +156 -0
- package/plan.md +281 -0
- package/scripts/.gitkeep +0 -0
- package/src/ai-sdk/cache.ts +142 -0
- package/src/ai-sdk/examples/cached-generation.ts +53 -0
- package/src/ai-sdk/examples/duet-scene-4.ts +53 -0
- package/src/ai-sdk/examples/duet-scene-5-audio.ts +32 -0
- package/src/ai-sdk/examples/duet-video.ts +56 -0
- package/src/ai-sdk/examples/editly-composition.ts +63 -0
- package/src/ai-sdk/examples/editly-test.ts +57 -0
- package/src/ai-sdk/examples/editly-video-test.ts +52 -0
- package/src/ai-sdk/examples/fal-lipsync.ts +43 -0
- package/src/ai-sdk/examples/higgsfield-image.ts +61 -0
- package/src/ai-sdk/examples/music-generation.ts +19 -0
- package/src/ai-sdk/examples/openai-sora.ts +34 -0
- package/src/ai-sdk/examples/replicate-bg-removal.ts +52 -0
- package/src/ai-sdk/examples/simpsons-scene.ts +61 -0
- package/src/ai-sdk/examples/talking-lion.ts +55 -0
- package/src/ai-sdk/examples/video-generation.ts +39 -0
- package/src/ai-sdk/examples/workflow-animated-girl.ts +104 -0
- package/src/ai-sdk/examples/workflow-before-after.ts +114 -0
- package/src/ai-sdk/examples/workflow-character-grid.ts +112 -0
- package/src/ai-sdk/examples/workflow-slideshow.ts +161 -0
- package/src/ai-sdk/file-cache.ts +112 -0
- package/src/ai-sdk/file.ts +238 -0
- package/src/ai-sdk/generate-element.ts +92 -0
- package/src/ai-sdk/generate-music.ts +46 -0
- package/src/ai-sdk/generate-video.ts +165 -0
- package/src/ai-sdk/index.ts +72 -0
- package/src/ai-sdk/music-model.ts +110 -0
- package/src/ai-sdk/providers/editly/editly.test.ts +1108 -0
- package/src/ai-sdk/providers/editly/ffmpeg.ts +60 -0
- package/src/ai-sdk/providers/editly/index.ts +817 -0
- package/src/ai-sdk/providers/editly/layers.ts +776 -0
- package/src/ai-sdk/providers/editly/plan.md +144 -0
- package/src/ai-sdk/providers/editly/types.ts +328 -0
- package/src/ai-sdk/providers/elevenlabs-provider.ts +255 -0
- package/src/ai-sdk/providers/fal-provider.ts +512 -0
- package/src/ai-sdk/providers/higgsfield.ts +379 -0
- package/src/ai-sdk/providers/openai.ts +251 -0
- package/src/ai-sdk/providers/replicate.ts +16 -0
- package/src/ai-sdk/video-model.ts +185 -0
- package/src/cli/commands/find.tsx +137 -0
- package/src/cli/commands/help.tsx +85 -0
- package/src/cli/commands/index.ts +6 -0
- package/src/cli/commands/list.tsx +238 -0
- package/src/cli/commands/render.tsx +71 -0
- package/src/cli/commands/run.tsx +511 -0
- package/src/cli/commands/which.tsx +253 -0
- package/src/cli/index.ts +114 -0
- package/src/cli/quiet.ts +44 -0
- package/src/cli/types.ts +32 -0
- package/src/cli/ui/components/Badge.tsx +29 -0
- package/src/cli/ui/components/DataTable.tsx +51 -0
- package/src/cli/ui/components/Header.tsx +23 -0
- package/src/cli/ui/components/HelpBlock.tsx +44 -0
- package/src/cli/ui/components/KeyValue.tsx +33 -0
- package/src/cli/ui/components/OptionRow.tsx +81 -0
- package/src/cli/ui/components/Separator.tsx +23 -0
- package/src/cli/ui/components/StatusBox.tsx +108 -0
- package/src/cli/ui/components/VargBox.tsx +51 -0
- package/src/cli/ui/components/VargProgress.tsx +36 -0
- package/src/cli/ui/components/VargSpinner.tsx +34 -0
- package/src/cli/ui/components/VargText.tsx +56 -0
- package/src/cli/ui/components/index.ts +19 -0
- package/src/cli/ui/index.ts +12 -0
- package/src/cli/ui/render.ts +35 -0
- package/src/cli/ui/theme.ts +63 -0
- package/src/cli/utils.ts +78 -0
- package/src/core/executor/executor.ts +201 -0
- package/src/core/executor/index.ts +13 -0
- package/src/core/executor/job.ts +214 -0
- package/src/core/executor/pipeline.ts +222 -0
- package/src/core/index.ts +11 -0
- package/src/core/registry/index.ts +9 -0
- package/src/core/registry/loader.ts +149 -0
- package/src/core/registry/registry.ts +221 -0
- package/src/core/registry/resolver.ts +206 -0
- package/src/core/schema/helpers.ts +134 -0
- package/src/core/schema/index.ts +8 -0
- package/src/core/schema/shared.ts +102 -0
- package/src/core/schema/types.ts +279 -0
- package/src/core/schema/validator.ts +92 -0
- package/src/definitions/actions/captions.ts +261 -0
- package/src/definitions/actions/edit.ts +298 -0
- package/src/definitions/actions/image.ts +125 -0
- package/src/definitions/actions/index.ts +114 -0
- package/src/definitions/actions/music.ts +205 -0
- package/src/definitions/actions/sync.ts +128 -0
- package/{action/transcribe/index.ts → src/definitions/actions/transcribe.ts} +63 -90
- package/src/definitions/actions/upload.ts +111 -0
- package/src/definitions/actions/video.ts +163 -0
- package/src/definitions/actions/voice.ts +119 -0
- package/src/definitions/index.ts +23 -0
- package/src/definitions/models/elevenlabs.ts +50 -0
- package/src/definitions/models/flux.ts +56 -0
- package/src/definitions/models/index.ts +36 -0
- package/src/definitions/models/kling.ts +56 -0
- package/src/definitions/models/llama.ts +54 -0
- package/src/definitions/models/nano-banana-pro.ts +102 -0
- package/src/definitions/models/sonauto.ts +68 -0
- package/src/definitions/models/soul.ts +65 -0
- package/src/definitions/models/wan.ts +54 -0
- package/src/definitions/models/whisper.ts +44 -0
- package/src/definitions/skills/index.ts +12 -0
- package/src/definitions/skills/talking-character.ts +87 -0
- package/src/definitions/skills/text-to-tiktok.ts +97 -0
- package/src/index.ts +118 -0
- package/src/providers/apify.ts +269 -0
- package/src/providers/base.ts +264 -0
- package/src/providers/elevenlabs.ts +217 -0
- package/src/providers/fal.ts +392 -0
- package/src/providers/ffmpeg.ts +544 -0
- package/src/providers/fireworks.ts +193 -0
- package/src/providers/groq.ts +149 -0
- package/src/providers/higgsfield.ts +145 -0
- package/src/providers/index.ts +143 -0
- package/src/providers/replicate.ts +147 -0
- package/src/providers/storage.ts +206 -0
- package/src/react/cli.ts +52 -0
- package/src/react/elements.ts +146 -0
- package/src/react/examples/branching.tsx +66 -0
- package/src/react/examples/captions-demo.tsx +37 -0
- package/src/react/examples/character-video.tsx +84 -0
- package/src/react/examples/grid.tsx +53 -0
- package/src/react/examples/layouts-demo.tsx +57 -0
- package/src/react/examples/madi.tsx +60 -0
- package/src/react/examples/music-test.tsx +35 -0
- package/src/react/examples/onlyfans-1m/workflow.tsx +88 -0
- package/src/react/examples/orange-portrait.tsx +41 -0
- package/src/react/examples/split-element-demo.tsx +60 -0
- package/src/react/examples/split-layout-demo.tsx +60 -0
- package/src/react/examples/split.tsx +41 -0
- package/src/react/examples/video-grid.tsx +46 -0
- package/src/react/index.ts +43 -0
- package/src/react/layouts/grid.tsx +28 -0
- package/src/react/layouts/index.ts +2 -0
- package/src/react/layouts/split.tsx +20 -0
- package/src/react/react.test.ts +309 -0
- package/src/react/render.ts +21 -0
- package/src/react/renderers/animate.ts +59 -0
- package/src/react/renderers/captions.ts +297 -0
- package/src/react/renderers/clip.ts +248 -0
- package/src/react/renderers/context.ts +17 -0
- package/src/react/renderers/image.ts +109 -0
- package/src/react/renderers/index.ts +22 -0
- package/src/react/renderers/music.ts +60 -0
- package/src/react/renderers/packshot.ts +84 -0
- package/src/react/renderers/progress.ts +173 -0
- package/src/react/renderers/render.ts +243 -0
- package/src/react/renderers/slider.ts +69 -0
- package/src/react/renderers/speech.ts +53 -0
- package/src/react/renderers/split.ts +91 -0
- package/src/react/renderers/subtitle.ts +16 -0
- package/src/react/renderers/swipe.ts +75 -0
- package/src/react/renderers/title.ts +17 -0
- package/src/react/renderers/utils.ts +124 -0
- package/src/react/renderers/video.ts +127 -0
- package/src/react/runtime/jsx-dev-runtime.ts +43 -0
- package/src/react/runtime/jsx-runtime.ts +35 -0
- package/src/react/types.ts +232 -0
- package/src/studio/index.ts +26 -0
- package/src/studio/scanner.ts +102 -0
- package/src/studio/server.ts +554 -0
- package/src/studio/stages.ts +251 -0
- package/src/studio/step-renderer.ts +279 -0
- package/src/studio/types.ts +60 -0
- package/src/studio/ui/cache.html +303 -0
- package/src/studio/ui/index.html +1820 -0
- package/src/tests/all.test.ts +509 -0
- package/src/tests/index.ts +33 -0
- package/src/tests/unit.test.ts +403 -0
- package/tsconfig.cli.json +8 -0
- package/tsconfig.json +21 -3
- package/TEST_RESULTS.md +0 -122
- package/action/captions/SKILL.md +0 -170
- package/action/captions/index.ts +0 -227
- package/action/edit/SKILL.md +0 -235
- package/action/edit/index.ts +0 -493
- package/action/image/SKILL.md +0 -140
- package/action/image/index.ts +0 -112
- package/action/sync/SKILL.md +0 -136
- package/action/sync/index.ts +0 -187
- package/action/transcribe/SKILL.md +0 -179
- package/action/video/SKILL.md +0 -116
- package/action/video/index.ts +0 -135
- package/action/voice/SKILL.md +0 -125
- package/action/voice/index.ts +0 -201
- package/index.ts +0 -38
- package/lib/README.md +0 -144
- package/lib/ai-sdk/fal.ts +0 -106
- package/lib/ai-sdk/replicate.ts +0 -107
- package/lib/elevenlabs.ts +0 -382
- package/lib/fal.ts +0 -478
- package/lib/ffmpeg.ts +0 -467
- package/lib/fireworks.ts +0 -235
- package/lib/groq.ts +0 -246
- package/lib/higgsfield.ts +0 -176
- package/lib/remotion/SKILL.md +0 -823
- package/lib/remotion/cli.ts +0 -115
- package/lib/remotion/functions.ts +0 -283
- package/lib/remotion/index.ts +0 -19
- package/lib/remotion/templates.ts +0 -73
- package/lib/replicate.ts +0 -304
- package/output.txt +0 -1
- package/test-import.ts +0 -7
- package/test-services.ts +0 -97
- package/utilities/s3.ts +0 -147
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { defineCommand } from "citty";
|
|
2
|
+
import { render } from "../../react/render";
|
|
3
|
+
import type { VargElement } from "../../react/types";
|
|
4
|
+
|
|
5
|
+
export const renderCmd = defineCommand({
|
|
6
|
+
meta: {
|
|
7
|
+
name: "render",
|
|
8
|
+
description: "render a react component to video",
|
|
9
|
+
},
|
|
10
|
+
args: {
|
|
11
|
+
file: {
|
|
12
|
+
type: "positional",
|
|
13
|
+
description: "component file (.tsx)",
|
|
14
|
+
required: true,
|
|
15
|
+
},
|
|
16
|
+
output: {
|
|
17
|
+
type: "string",
|
|
18
|
+
alias: "o",
|
|
19
|
+
description: "output path",
|
|
20
|
+
},
|
|
21
|
+
cache: {
|
|
22
|
+
type: "string",
|
|
23
|
+
alias: "c",
|
|
24
|
+
description: "cache directory",
|
|
25
|
+
default: ".cache/ai",
|
|
26
|
+
},
|
|
27
|
+
quiet: {
|
|
28
|
+
type: "boolean",
|
|
29
|
+
alias: "q",
|
|
30
|
+
description: "minimal output",
|
|
31
|
+
default: false,
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
async run({ args }) {
|
|
35
|
+
const file = args.file as string;
|
|
36
|
+
|
|
37
|
+
if (!file) {
|
|
38
|
+
console.error("usage: varg render <component.tsx> [-o output.mp4]");
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const resolvedPath = Bun.resolveSync(file, process.cwd());
|
|
43
|
+
const mod = await import(resolvedPath);
|
|
44
|
+
const component: VargElement = mod.default;
|
|
45
|
+
|
|
46
|
+
if (!component || component.type !== "render") {
|
|
47
|
+
console.error("error: default export must be a <Render> element");
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const outputPath =
|
|
52
|
+
args.output ??
|
|
53
|
+
`output/${file
|
|
54
|
+
.replace(/\.tsx?$/, "")
|
|
55
|
+
.split("/")
|
|
56
|
+
.pop()}.mp4`;
|
|
57
|
+
|
|
58
|
+
if (!args.quiet) {
|
|
59
|
+
console.log(`rendering ${file} → ${outputPath}`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const buffer = await render(component, {
|
|
63
|
+
output: outputPath,
|
|
64
|
+
cache: args.cache,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
if (!args.quiet) {
|
|
68
|
+
console.log(`done! ${buffer.byteLength} bytes → ${outputPath}`);
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
});
|
|
@@ -0,0 +1,511 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* varg run command
|
|
3
|
+
* Ink-based execution with live status updates
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { existsSync } from "node:fs";
|
|
7
|
+
import { defineCommand } from "citty";
|
|
8
|
+
import { Box, Text } from "ink";
|
|
9
|
+
import { executor } from "../../core/executor/index.ts";
|
|
10
|
+
import { resolve } from "../../core/registry/resolver.ts";
|
|
11
|
+
import {
|
|
12
|
+
coerceCliValue,
|
|
13
|
+
getCliSchemaInfo,
|
|
14
|
+
toJsonSchema,
|
|
15
|
+
} from "../../core/schema/helpers.ts";
|
|
16
|
+
import type { Definition } from "../../core/schema/types.ts";
|
|
17
|
+
import {
|
|
18
|
+
Header,
|
|
19
|
+
OptionRow,
|
|
20
|
+
StatusBox,
|
|
21
|
+
VargBox,
|
|
22
|
+
VargText,
|
|
23
|
+
} from "../ui/index.ts";
|
|
24
|
+
import { renderLive, renderStatic } from "../ui/render.ts";
|
|
25
|
+
import { theme } from "../ui/theme.ts";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Extract detailed error message from provider errors
|
|
29
|
+
* Handles fal.ai ApiError, Replicate errors, and generic errors
|
|
30
|
+
*/
|
|
31
|
+
function extractErrorMessage(err: unknown): string {
|
|
32
|
+
if (!(err instanceof Error)) {
|
|
33
|
+
return String(err);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Check for fal.ai ApiError with body details
|
|
37
|
+
const apiError = err as Error & {
|
|
38
|
+
body?: { detail?: string | Array<{ msg: string; loc: string[] }> };
|
|
39
|
+
status?: number;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
if (apiError.body?.detail) {
|
|
43
|
+
// Handle validation errors (array of field errors)
|
|
44
|
+
if (Array.isArray(apiError.body.detail)) {
|
|
45
|
+
return apiError.body.detail
|
|
46
|
+
.map((e) => `${e.loc?.join(".") || "field"}: ${e.msg}`)
|
|
47
|
+
.join("; ");
|
|
48
|
+
}
|
|
49
|
+
// Handle string detail
|
|
50
|
+
if (typeof apiError.body.detail === "string") {
|
|
51
|
+
return apiError.body.detail;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Check for body as string or with message
|
|
56
|
+
if (apiError.body) {
|
|
57
|
+
const body = apiError.body as Record<string, unknown>;
|
|
58
|
+
if (body.message && typeof body.message === "string") {
|
|
59
|
+
return body.message;
|
|
60
|
+
}
|
|
61
|
+
if (body.error && typeof body.error === "string") {
|
|
62
|
+
return body.error;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Add status code context if available
|
|
67
|
+
if (apiError.status && apiError.message) {
|
|
68
|
+
return `${apiError.message} (${apiError.status})`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return err.message;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function sanitizeOutput(_key: string, value: unknown): unknown {
|
|
75
|
+
if (value instanceof Buffer) {
|
|
76
|
+
return `<Buffer ${value.length} bytes>`;
|
|
77
|
+
}
|
|
78
|
+
if (
|
|
79
|
+
value &&
|
|
80
|
+
typeof value === "object" &&
|
|
81
|
+
"type" in value &&
|
|
82
|
+
(value as { type: string }).type === "Buffer" &&
|
|
83
|
+
"data" in value
|
|
84
|
+
) {
|
|
85
|
+
const data = (value as { data: unknown[] }).data;
|
|
86
|
+
return `<Buffer ${Array.isArray(data) ? data.length : "?"} bytes>`;
|
|
87
|
+
}
|
|
88
|
+
return value;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
interface RunOptions {
|
|
92
|
+
[key: string]: string | boolean | undefined;
|
|
93
|
+
help?: boolean;
|
|
94
|
+
schema?: boolean;
|
|
95
|
+
json?: boolean;
|
|
96
|
+
quiet?: boolean;
|
|
97
|
+
provider?: string;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function parseArgs(args: string[]): { target: string; options: RunOptions } {
|
|
101
|
+
const options: RunOptions = {};
|
|
102
|
+
let target = "";
|
|
103
|
+
|
|
104
|
+
for (let i = 0; i < args.length; i++) {
|
|
105
|
+
const arg = args[i];
|
|
106
|
+
if (!arg) continue;
|
|
107
|
+
|
|
108
|
+
if (arg === "--help" || arg === "-h") {
|
|
109
|
+
options.help = true;
|
|
110
|
+
} else if (arg.startsWith("--")) {
|
|
111
|
+
const key = arg.slice(2);
|
|
112
|
+
if (["schema", "json", "quiet"].includes(key)) {
|
|
113
|
+
options[key] = true;
|
|
114
|
+
} else {
|
|
115
|
+
const value = args[++i];
|
|
116
|
+
if (value) options[key] = value;
|
|
117
|
+
}
|
|
118
|
+
} else if (!target) {
|
|
119
|
+
target = arg;
|
|
120
|
+
} else {
|
|
121
|
+
// Positional args - check if it looks like a file
|
|
122
|
+
if (existsSync(arg) || arg.startsWith("./") || arg.startsWith("/")) {
|
|
123
|
+
if (!options.image && !options.audio) {
|
|
124
|
+
options.image = arg;
|
|
125
|
+
}
|
|
126
|
+
} else if (!options.prompt && !options.text) {
|
|
127
|
+
options.prompt = arg;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return { target, options };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
interface HelpViewProps {
|
|
136
|
+
item: Definition;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function HelpView({ item }: HelpViewProps) {
|
|
140
|
+
const { properties, required } = getCliSchemaInfo(item.schema.input);
|
|
141
|
+
// Only show truly required args (no default value)
|
|
142
|
+
const trulyRequired = required.filter(
|
|
143
|
+
(r) => properties[r]?.default === undefined,
|
|
144
|
+
);
|
|
145
|
+
const reqArgs = trulyRequired.map((r) => `--${r} <${r}>`).join(" ");
|
|
146
|
+
|
|
147
|
+
return (
|
|
148
|
+
<VargBox title={`${item.type}: ${item.name}`}>
|
|
149
|
+
<Box marginBottom={1}>
|
|
150
|
+
<Text wrap="wrap">{item.description}</Text>
|
|
151
|
+
</Box>
|
|
152
|
+
|
|
153
|
+
<Header>USAGE</Header>
|
|
154
|
+
<Box paddingLeft={2} marginBottom={1}>
|
|
155
|
+
<VargText variant="accent">
|
|
156
|
+
varg run {item.name} {reqArgs} [options]
|
|
157
|
+
</VargText>
|
|
158
|
+
</Box>
|
|
159
|
+
|
|
160
|
+
<Header>OPTIONS</Header>
|
|
161
|
+
<Box flexDirection="column" paddingLeft={2} marginBottom={1}>
|
|
162
|
+
{Object.entries(properties).map(([key, prop]) => {
|
|
163
|
+
const hasDefault = prop.default !== undefined;
|
|
164
|
+
return (
|
|
165
|
+
<OptionRow
|
|
166
|
+
key={key}
|
|
167
|
+
name={key}
|
|
168
|
+
description={prop.description}
|
|
169
|
+
required={required.includes(key) && !hasDefault}
|
|
170
|
+
defaultValue={prop.default}
|
|
171
|
+
enumValues={prop.enum}
|
|
172
|
+
type={prop.type}
|
|
173
|
+
/>
|
|
174
|
+
);
|
|
175
|
+
})}
|
|
176
|
+
</Box>
|
|
177
|
+
|
|
178
|
+
{item.type === "model" && (
|
|
179
|
+
<Box flexDirection="column" paddingLeft={2} marginBottom={1}>
|
|
180
|
+
<OptionRow name="provider" description="override default provider" />
|
|
181
|
+
<Box paddingLeft={theme.layout.optionNameWidth}>
|
|
182
|
+
<Text dimColor>available: {item.providers.join(", ")}</Text>
|
|
183
|
+
</Box>
|
|
184
|
+
</Box>
|
|
185
|
+
)}
|
|
186
|
+
|
|
187
|
+
<Header>GLOBAL OPTIONS</Header>
|
|
188
|
+
<Box flexDirection="column" paddingLeft={2}>
|
|
189
|
+
<OptionRow name="json" description="output result as json" />
|
|
190
|
+
<OptionRow name="quiet" description="minimal output" />
|
|
191
|
+
<OptionRow name="help, -h" description="show this help" />
|
|
192
|
+
</Box>
|
|
193
|
+
</VargBox>
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function ErrorView({ message, hint }: { message: string; hint?: string }) {
|
|
198
|
+
return (
|
|
199
|
+
<Box flexDirection="column" padding={1}>
|
|
200
|
+
<VargText variant="error">error: {message}</VargText>
|
|
201
|
+
{hint && (
|
|
202
|
+
<Box marginTop={1}>
|
|
203
|
+
<VargText variant="muted">run </VargText>
|
|
204
|
+
<VargText variant="accent">{hint}</VargText>
|
|
205
|
+
<VargText variant="muted"> for help</VargText>
|
|
206
|
+
</Box>
|
|
207
|
+
)}
|
|
208
|
+
</Box>
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/** Help view for run command without target */
|
|
213
|
+
function RunHelpView() {
|
|
214
|
+
return (
|
|
215
|
+
<VargBox title="varg run">
|
|
216
|
+
<Box marginBottom={1}>
|
|
217
|
+
<Text>run a model, action, or skill</Text>
|
|
218
|
+
</Box>
|
|
219
|
+
|
|
220
|
+
<Header>USAGE</Header>
|
|
221
|
+
<Box paddingLeft={2} marginBottom={1}>
|
|
222
|
+
<VargText variant="accent">varg run {"<target>"} [--options]</VargText>
|
|
223
|
+
</Box>
|
|
224
|
+
|
|
225
|
+
<Header>OPTIONS</Header>
|
|
226
|
+
<Box flexDirection="column" paddingLeft={2} marginBottom={1}>
|
|
227
|
+
<Text>--json output result as json</Text>
|
|
228
|
+
<Text>--quiet minimal output</Text>
|
|
229
|
+
<Text>--schema show target schema as json</Text>
|
|
230
|
+
<Text>--provider override default provider (models only)</Text>
|
|
231
|
+
<Text>--help, -h show this help</Text>
|
|
232
|
+
</Box>
|
|
233
|
+
|
|
234
|
+
<Header>EXAMPLES</Header>
|
|
235
|
+
<Box flexDirection="column" paddingLeft={2}>
|
|
236
|
+
<Box flexDirection="column" marginBottom={1}>
|
|
237
|
+
<Text dimColor># generate a video from text</Text>
|
|
238
|
+
<VargText variant="accent">
|
|
239
|
+
varg run video --prompt "ocean waves"
|
|
240
|
+
</VargText>
|
|
241
|
+
</Box>
|
|
242
|
+
<Box flexDirection="column" marginBottom={1}>
|
|
243
|
+
<Text dimColor># get help for a specific target</Text>
|
|
244
|
+
<VargText variant="accent">varg run video --help</VargText>
|
|
245
|
+
</Box>
|
|
246
|
+
<Box flexDirection="column">
|
|
247
|
+
<Text dimColor># see available targets</Text>
|
|
248
|
+
<VargText variant="accent">varg list</VargText>
|
|
249
|
+
</Box>
|
|
250
|
+
</Box>
|
|
251
|
+
</VargBox>
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/** Show run command help */
|
|
256
|
+
export function showRunHelp() {
|
|
257
|
+
renderStatic(<RunHelpView />);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/** Show target-specific help */
|
|
261
|
+
export function showTargetHelp(target: string): boolean {
|
|
262
|
+
const result = resolve(target, { fuzzy: true });
|
|
263
|
+
if (result.definition) {
|
|
264
|
+
renderStatic(<HelpView item={result.definition} />);
|
|
265
|
+
return true;
|
|
266
|
+
}
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export const runCmd = defineCommand({
|
|
271
|
+
meta: {
|
|
272
|
+
name: "run",
|
|
273
|
+
description: "run a model, action, or skill",
|
|
274
|
+
},
|
|
275
|
+
args: {
|
|
276
|
+
target: {
|
|
277
|
+
type: "positional",
|
|
278
|
+
description: "what to run",
|
|
279
|
+
required: false,
|
|
280
|
+
},
|
|
281
|
+
schema: { type: "boolean", description: "show schema as json" },
|
|
282
|
+
json: { type: "boolean", description: "output result as json" },
|
|
283
|
+
quiet: { type: "boolean", description: "minimal output" },
|
|
284
|
+
},
|
|
285
|
+
async run({ rawArgs }) {
|
|
286
|
+
const { target, options } = parseArgs(rawArgs);
|
|
287
|
+
|
|
288
|
+
// Show run help when no target or --help without target
|
|
289
|
+
if (!target || (options.help && !target)) {
|
|
290
|
+
showRunHelp();
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Resolve the target
|
|
295
|
+
const result = resolve(target, { fuzzy: true });
|
|
296
|
+
|
|
297
|
+
if (!result.definition) {
|
|
298
|
+
renderStatic(
|
|
299
|
+
<Box flexDirection="column" padding={1}>
|
|
300
|
+
<VargText variant="error">error: '{target}' not found</VargText>
|
|
301
|
+
{result.suggestions && result.suggestions.length > 0 && (
|
|
302
|
+
<Box marginTop={1}>
|
|
303
|
+
<Text>
|
|
304
|
+
did you mean: {result.suggestions.slice(0, 3).join(", ")}?
|
|
305
|
+
</Text>
|
|
306
|
+
</Box>
|
|
307
|
+
)}
|
|
308
|
+
<Box marginTop={1}>
|
|
309
|
+
<VargText variant="muted">run </VargText>
|
|
310
|
+
<VargText variant="accent">varg list</VargText>
|
|
311
|
+
<VargText variant="muted"> to see available targets</VargText>
|
|
312
|
+
</Box>
|
|
313
|
+
</Box>,
|
|
314
|
+
);
|
|
315
|
+
process.exit(1);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const item = result.definition;
|
|
319
|
+
|
|
320
|
+
// Show target-specific help
|
|
321
|
+
if (options.help) {
|
|
322
|
+
renderStatic(<HelpView item={item} />);
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (options.schema) {
|
|
327
|
+
const schema = {
|
|
328
|
+
name: item.name,
|
|
329
|
+
type: item.type,
|
|
330
|
+
description: item.description,
|
|
331
|
+
input: toJsonSchema(item.schema.input),
|
|
332
|
+
output: toJsonSchema(item.schema.output),
|
|
333
|
+
};
|
|
334
|
+
console.log(JSON.stringify(schema, null, 2));
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Get schema info for validation
|
|
339
|
+
const { properties, required } = getCliSchemaInfo(item.schema.input);
|
|
340
|
+
|
|
341
|
+
// Validate required args (skip fields with default values)
|
|
342
|
+
for (const req of required) {
|
|
343
|
+
const prop = properties[req];
|
|
344
|
+
const hasDefault = prop?.default !== undefined;
|
|
345
|
+
if (!options[req] && !hasDefault) {
|
|
346
|
+
renderStatic(
|
|
347
|
+
<ErrorView
|
|
348
|
+
message={`--${req} is required`}
|
|
349
|
+
hint={`varg run ${target} --help`}
|
|
350
|
+
/>,
|
|
351
|
+
);
|
|
352
|
+
process.exit(1);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Build params for display
|
|
357
|
+
const params: Record<string, string> = {};
|
|
358
|
+
for (const key of Object.keys(properties)) {
|
|
359
|
+
if (options[key] && typeof options[key] === "string") {
|
|
360
|
+
params[key] = options[key] as string;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const startTime = Date.now();
|
|
365
|
+
|
|
366
|
+
// For quiet/json modes, no UI
|
|
367
|
+
if (options.quiet || options.json) {
|
|
368
|
+
try {
|
|
369
|
+
const inputs: Record<string, unknown> = {};
|
|
370
|
+
for (const key of Object.keys(properties)) {
|
|
371
|
+
const value = options[key];
|
|
372
|
+
if (value !== undefined) {
|
|
373
|
+
const prop = properties[key];
|
|
374
|
+
inputs[key] =
|
|
375
|
+
typeof value === "string" && prop
|
|
376
|
+
? coerceCliValue(value, prop)
|
|
377
|
+
: value;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const execResult = await executor.run(target, inputs, {
|
|
382
|
+
provider: options.provider,
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
const elapsed = Date.now() - startTime;
|
|
386
|
+
|
|
387
|
+
if (options.json) {
|
|
388
|
+
console.log(
|
|
389
|
+
JSON.stringify(
|
|
390
|
+
{
|
|
391
|
+
success: true,
|
|
392
|
+
result: execResult,
|
|
393
|
+
time: elapsed,
|
|
394
|
+
},
|
|
395
|
+
sanitizeOutput,
|
|
396
|
+
),
|
|
397
|
+
);
|
|
398
|
+
} else {
|
|
399
|
+
console.log(JSON.stringify(execResult.output, sanitizeOutput));
|
|
400
|
+
}
|
|
401
|
+
} catch (err) {
|
|
402
|
+
const elapsed = Date.now() - startTime;
|
|
403
|
+
const errorMsg = extractErrorMessage(err);
|
|
404
|
+
|
|
405
|
+
if (options.json) {
|
|
406
|
+
console.log(
|
|
407
|
+
JSON.stringify({ success: false, error: errorMsg, time: elapsed }),
|
|
408
|
+
);
|
|
409
|
+
} else {
|
|
410
|
+
console.error(errorMsg);
|
|
411
|
+
}
|
|
412
|
+
process.exit(1);
|
|
413
|
+
}
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// Interactive mode with live status
|
|
418
|
+
const { rerender, unmount } = renderLive(
|
|
419
|
+
<StatusBox title={target} status="running" params={params} />,
|
|
420
|
+
);
|
|
421
|
+
|
|
422
|
+
try {
|
|
423
|
+
const inputs: Record<string, unknown> = {};
|
|
424
|
+
for (const key of Object.keys(properties)) {
|
|
425
|
+
const value = options[key];
|
|
426
|
+
if (value !== undefined) {
|
|
427
|
+
const prop = properties[key];
|
|
428
|
+
inputs[key] =
|
|
429
|
+
typeof value === "string" && prop
|
|
430
|
+
? coerceCliValue(value, prop)
|
|
431
|
+
: value;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const execResult = await executor.run(target, inputs, {
|
|
436
|
+
provider: options.provider,
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
const elapsed = Date.now() - startTime;
|
|
440
|
+
|
|
441
|
+
// Extract URL from result
|
|
442
|
+
const output = execResult.output as Record<string, unknown> | string;
|
|
443
|
+
let url: string | null = null;
|
|
444
|
+
|
|
445
|
+
if (typeof output === "string") {
|
|
446
|
+
url = output;
|
|
447
|
+
} else if (output) {
|
|
448
|
+
// Try common URL fields
|
|
449
|
+
url =
|
|
450
|
+
(output.imageUrl as string) ||
|
|
451
|
+
(output.videoUrl as string) ||
|
|
452
|
+
(output.url as string) ||
|
|
453
|
+
null;
|
|
454
|
+
|
|
455
|
+
// Handle images array (nano-banana-pro, flux, etc.)
|
|
456
|
+
if (!url && Array.isArray(output.images) && output.images.length > 0) {
|
|
457
|
+
const firstImage = output.images[0] as Record<string, unknown>;
|
|
458
|
+
url = (firstImage?.url as string) || null;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// Handle video object
|
|
462
|
+
if (!url && output.video && typeof output.video === "object") {
|
|
463
|
+
url = (output.video as Record<string, unknown>).url as string;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// Clear and show done state
|
|
468
|
+
process.stdout.write("\x1b[2J\x1b[H");
|
|
469
|
+
|
|
470
|
+
rerender(
|
|
471
|
+
<Box flexDirection="column">
|
|
472
|
+
<StatusBox
|
|
473
|
+
title={target}
|
|
474
|
+
status="done"
|
|
475
|
+
params={params}
|
|
476
|
+
output={url ? url : "done"}
|
|
477
|
+
duration={elapsed}
|
|
478
|
+
/>
|
|
479
|
+
</Box>,
|
|
480
|
+
);
|
|
481
|
+
|
|
482
|
+
// Also log the URL to console for easy copying
|
|
483
|
+
if (url) {
|
|
484
|
+
console.log(`\n${url}`);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// Allow render to complete then unmount
|
|
488
|
+
setTimeout(() => unmount(), 100);
|
|
489
|
+
} catch (err) {
|
|
490
|
+
const elapsed = Date.now() - startTime;
|
|
491
|
+
const errorMsg = extractErrorMessage(err);
|
|
492
|
+
|
|
493
|
+
process.stdout.write("\x1b[2J\x1b[H");
|
|
494
|
+
|
|
495
|
+
rerender(
|
|
496
|
+
<StatusBox
|
|
497
|
+
title={target}
|
|
498
|
+
status="error"
|
|
499
|
+
params={params}
|
|
500
|
+
error={errorMsg}
|
|
501
|
+
duration={elapsed}
|
|
502
|
+
/>,
|
|
503
|
+
);
|
|
504
|
+
|
|
505
|
+
setTimeout(() => {
|
|
506
|
+
unmount();
|
|
507
|
+
process.exit(1);
|
|
508
|
+
}, 100);
|
|
509
|
+
}
|
|
510
|
+
},
|
|
511
|
+
});
|