@unityclaw/skills 1.0.4 → 1.0.6
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 +0 -3
- package/dist/{chunk-FAGVVFFQ.js → chunk-CVOSGIUI.js} +91 -3
- package/dist/cli.cjs +89 -1
- package/dist/cli.js +1 -1
- package/dist/index.cjs +89 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/unityclaw-video-generation-sora/SKILL.md +0 -286
- package/unityclaw-video-generation-veo/SKILL.md +0 -275
package/README.md
CHANGED
|
@@ -90,11 +90,8 @@ npx @unityclaw/skills /unityclaw-media-analysis https://youtube.com/watch?v=...
|
|
|
90
90
|
| [unityclaw-image-compress](./unityclaw-image-compress/SKILL.md) | Compress images with quality control | 🗜️ |
|
|
91
91
|
|
|
92
92
|
### Video Skills
|
|
93
|
-
|
|
94
93
|
| Skill | Description | Emoji |
|
|
95
94
|
|-------|-------------|-------|
|
|
96
|
-
| [unityclaw-video-generation-sora](./unityclaw-video-generation-sora/SKILL.md) | Generate videos using OpenAI Sora | 🎬 |
|
|
97
|
-
| [unityclaw-video-generation-veo](./unityclaw-video-generation-veo/SKILL.md) | Generate videos using Google Veo | 🎥 |
|
|
98
95
|
| [unityclaw-video-generation-kling](./unityclaw-video-generation-kling/SKILL.md) | Generate videos using Kling AI | 📹 |
|
|
99
96
|
| [unityclaw-video-generation-other](./unityclaw-video-generation-other/SKILL.md) | Generate videos using Doubao, Wan, MiniMax, JiMeng | 🎞️ |
|
|
100
97
|
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import { spawn } from "child_process";
|
|
2
|
+
import { spawn, execSync } from "child_process";
|
|
3
3
|
import { cp, mkdir, readdir, readFile } from "fs/promises";
|
|
4
|
-
import { existsSync, readdirSync } from "fs";
|
|
4
|
+
import { existsSync, readdirSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
5
5
|
import path from "path";
|
|
6
6
|
import { fileURLToPath } from "url";
|
|
7
|
+
import * as os from "os";
|
|
8
|
+
import * as readline from "readline";
|
|
7
9
|
function getSkillsDir() {
|
|
8
10
|
const hasSkillDirs = (dir) => {
|
|
9
11
|
try {
|
|
@@ -106,7 +108,58 @@ function getClaudeSkillsDir() {
|
|
|
106
108
|
}
|
|
107
109
|
function getOpenClawSkillsDir() {
|
|
108
110
|
const homeDir = process.env.HOME || process.env.USERPROFILE || "";
|
|
109
|
-
return process.env.OPENCLAW_SKILLS_DIR || path.join(homeDir, ".openclaw", "skills");
|
|
111
|
+
return process.env.OPENCLAW_SKILLS_DIR || path.join(homeDir, ".openclaw", "workspace", "skills");
|
|
112
|
+
}
|
|
113
|
+
async function promptForApiKey() {
|
|
114
|
+
const rl = readline.createInterface({
|
|
115
|
+
input: process.stdin,
|
|
116
|
+
output: process.stdout
|
|
117
|
+
});
|
|
118
|
+
return new Promise((resolve) => {
|
|
119
|
+
rl.question("\nEnter your UnityClaw API key (or press Enter to skip): ", (answer) => {
|
|
120
|
+
rl.close();
|
|
121
|
+
resolve(answer.trim() || void 0);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
function updateOpenClawEnvConfig(apiKey) {
|
|
126
|
+
const openclawDir = path.join(os.homedir(), ".openclaw");
|
|
127
|
+
const openclawPath = path.join(openclawDir, "openclaw.json");
|
|
128
|
+
let config = {};
|
|
129
|
+
if (existsSync(openclawPath)) {
|
|
130
|
+
try {
|
|
131
|
+
config = JSON.parse(readFileSync(openclawPath, "utf-8"));
|
|
132
|
+
} catch {
|
|
133
|
+
config = {};
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (!config.env) config.env = {};
|
|
137
|
+
const env = config.env;
|
|
138
|
+
if (!env.vars) env.vars = {};
|
|
139
|
+
env.vars.UNITYCLAW_API_KEY = apiKey;
|
|
140
|
+
if (!existsSync(openclawDir)) {
|
|
141
|
+
mkdirSync(openclawDir, { recursive: true });
|
|
142
|
+
}
|
|
143
|
+
writeFileSync(openclawPath, JSON.stringify(config, null, 2), "utf-8");
|
|
144
|
+
}
|
|
145
|
+
function hasExistingApiKey() {
|
|
146
|
+
const sdkConfigPath = path.join(os.homedir(), ".unityclaw", "config.json");
|
|
147
|
+
if (existsSync(sdkConfigPath)) {
|
|
148
|
+
try {
|
|
149
|
+
const config = JSON.parse(readFileSync(sdkConfigPath, "utf-8"));
|
|
150
|
+
if (config?.apiKey) return true;
|
|
151
|
+
} catch {
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
const openclawPath = path.join(os.homedir(), ".openclaw", "openclaw.json");
|
|
155
|
+
if (existsSync(openclawPath)) {
|
|
156
|
+
try {
|
|
157
|
+
const config = JSON.parse(readFileSync(openclawPath, "utf-8"));
|
|
158
|
+
if (config?.env?.vars?.UNITYCLAW_API_KEY) return true;
|
|
159
|
+
} catch {
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return false;
|
|
110
163
|
}
|
|
111
164
|
async function installSkills(target, skills) {
|
|
112
165
|
const targetDir = target === "claude" ? getClaudeSkillsDir() : getOpenClawSkillsDir();
|
|
@@ -137,6 +190,41 @@ Installing ${skillsToInstall.length} skill(s):
|
|
|
137
190
|
}
|
|
138
191
|
console.log(`
|
|
139
192
|
\u2728 Done! Skills installed to: ${targetDir}`);
|
|
193
|
+
console.log("target", target);
|
|
194
|
+
console.log("hasExistingApiKey", hasExistingApiKey());
|
|
195
|
+
if (target === "openclaw" && !hasExistingApiKey()) {
|
|
196
|
+
console.log("\n\u{1F527} Configuration:");
|
|
197
|
+
console.log(" UnityClaw API key not found.\n");
|
|
198
|
+
const apiKey = await promptForApiKey();
|
|
199
|
+
if (apiKey) {
|
|
200
|
+
try {
|
|
201
|
+
execSync(`npx @unityclaw/sdk config set apiKey ${apiKey}`, { stdio: "pipe" });
|
|
202
|
+
} catch {
|
|
203
|
+
const configDir = path.join(os.homedir(), ".unityclaw");
|
|
204
|
+
const configPath = path.join(configDir, "config.json");
|
|
205
|
+
let config = {};
|
|
206
|
+
if (existsSync(configPath)) {
|
|
207
|
+
try {
|
|
208
|
+
config = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
209
|
+
} catch {
|
|
210
|
+
config = {};
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
config.apiKey = apiKey;
|
|
214
|
+
if (!existsSync(configDir)) {
|
|
215
|
+
mkdirSync(configDir, { recursive: true });
|
|
216
|
+
}
|
|
217
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
218
|
+
}
|
|
219
|
+
updateOpenClawEnvConfig(apiKey);
|
|
220
|
+
console.log("\n \u2705 API key saved to:");
|
|
221
|
+
console.log(" - ~/.openclaw/openclaw.json");
|
|
222
|
+
console.log("\n Get your API key at: https://unityclaw.com");
|
|
223
|
+
} else {
|
|
224
|
+
console.log("\n \u26A0\uFE0F Skipped. Configure later with:");
|
|
225
|
+
console.log(" npx @unityclaw/sdk config set apiKey <your-key>");
|
|
226
|
+
}
|
|
227
|
+
}
|
|
140
228
|
}
|
|
141
229
|
async function executeSkill(skillCommand) {
|
|
142
230
|
const args = [
|
package/dist/cli.cjs
CHANGED
|
@@ -33,6 +33,8 @@ var import_promises = require("fs/promises");
|
|
|
33
33
|
var import_fs = require("fs");
|
|
34
34
|
var import_path = __toESM(require("path"), 1);
|
|
35
35
|
var import_url = require("url");
|
|
36
|
+
var os = __toESM(require("os"), 1);
|
|
37
|
+
var readline = __toESM(require("readline"), 1);
|
|
36
38
|
var import_meta = {};
|
|
37
39
|
function getSkillsDir() {
|
|
38
40
|
const hasSkillDirs = (dir) => {
|
|
@@ -136,7 +138,58 @@ function getClaudeSkillsDir() {
|
|
|
136
138
|
}
|
|
137
139
|
function getOpenClawSkillsDir() {
|
|
138
140
|
const homeDir = process.env.HOME || process.env.USERPROFILE || "";
|
|
139
|
-
return process.env.OPENCLAW_SKILLS_DIR || import_path.default.join(homeDir, ".openclaw", "skills");
|
|
141
|
+
return process.env.OPENCLAW_SKILLS_DIR || import_path.default.join(homeDir, ".openclaw", "workspace", "skills");
|
|
142
|
+
}
|
|
143
|
+
async function promptForApiKey() {
|
|
144
|
+
const rl = readline.createInterface({
|
|
145
|
+
input: process.stdin,
|
|
146
|
+
output: process.stdout
|
|
147
|
+
});
|
|
148
|
+
return new Promise((resolve) => {
|
|
149
|
+
rl.question("\nEnter your UnityClaw API key (or press Enter to skip): ", (answer) => {
|
|
150
|
+
rl.close();
|
|
151
|
+
resolve(answer.trim() || void 0);
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
function updateOpenClawEnvConfig(apiKey) {
|
|
156
|
+
const openclawDir = import_path.default.join(os.homedir(), ".openclaw");
|
|
157
|
+
const openclawPath = import_path.default.join(openclawDir, "openclaw.json");
|
|
158
|
+
let config = {};
|
|
159
|
+
if ((0, import_fs.existsSync)(openclawPath)) {
|
|
160
|
+
try {
|
|
161
|
+
config = JSON.parse((0, import_fs.readFileSync)(openclawPath, "utf-8"));
|
|
162
|
+
} catch {
|
|
163
|
+
config = {};
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (!config.env) config.env = {};
|
|
167
|
+
const env = config.env;
|
|
168
|
+
if (!env.vars) env.vars = {};
|
|
169
|
+
env.vars.UNITYCLAW_API_KEY = apiKey;
|
|
170
|
+
if (!(0, import_fs.existsSync)(openclawDir)) {
|
|
171
|
+
(0, import_fs.mkdirSync)(openclawDir, { recursive: true });
|
|
172
|
+
}
|
|
173
|
+
(0, import_fs.writeFileSync)(openclawPath, JSON.stringify(config, null, 2), "utf-8");
|
|
174
|
+
}
|
|
175
|
+
function hasExistingApiKey() {
|
|
176
|
+
const sdkConfigPath = import_path.default.join(os.homedir(), ".unityclaw", "config.json");
|
|
177
|
+
if ((0, import_fs.existsSync)(sdkConfigPath)) {
|
|
178
|
+
try {
|
|
179
|
+
const config = JSON.parse((0, import_fs.readFileSync)(sdkConfigPath, "utf-8"));
|
|
180
|
+
if (config?.apiKey) return true;
|
|
181
|
+
} catch {
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
const openclawPath = import_path.default.join(os.homedir(), ".openclaw", "openclaw.json");
|
|
185
|
+
if ((0, import_fs.existsSync)(openclawPath)) {
|
|
186
|
+
try {
|
|
187
|
+
const config = JSON.parse((0, import_fs.readFileSync)(openclawPath, "utf-8"));
|
|
188
|
+
if (config?.env?.vars?.UNITYCLAW_API_KEY) return true;
|
|
189
|
+
} catch {
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return false;
|
|
140
193
|
}
|
|
141
194
|
async function installSkills(target, skills) {
|
|
142
195
|
const targetDir = target === "claude" ? getClaudeSkillsDir() : getOpenClawSkillsDir();
|
|
@@ -167,6 +220,41 @@ Installing ${skillsToInstall.length} skill(s):
|
|
|
167
220
|
}
|
|
168
221
|
console.log(`
|
|
169
222
|
\u2728 Done! Skills installed to: ${targetDir}`);
|
|
223
|
+
console.log("target", target);
|
|
224
|
+
console.log("hasExistingApiKey", hasExistingApiKey());
|
|
225
|
+
if (target === "openclaw" && !hasExistingApiKey()) {
|
|
226
|
+
console.log("\n\u{1F527} Configuration:");
|
|
227
|
+
console.log(" UnityClaw API key not found.\n");
|
|
228
|
+
const apiKey = await promptForApiKey();
|
|
229
|
+
if (apiKey) {
|
|
230
|
+
try {
|
|
231
|
+
(0, import_child_process.execSync)(`npx @unityclaw/sdk config set apiKey ${apiKey}`, { stdio: "pipe" });
|
|
232
|
+
} catch {
|
|
233
|
+
const configDir = import_path.default.join(os.homedir(), ".unityclaw");
|
|
234
|
+
const configPath = import_path.default.join(configDir, "config.json");
|
|
235
|
+
let config = {};
|
|
236
|
+
if ((0, import_fs.existsSync)(configPath)) {
|
|
237
|
+
try {
|
|
238
|
+
config = JSON.parse((0, import_fs.readFileSync)(configPath, "utf-8"));
|
|
239
|
+
} catch {
|
|
240
|
+
config = {};
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
config.apiKey = apiKey;
|
|
244
|
+
if (!(0, import_fs.existsSync)(configDir)) {
|
|
245
|
+
(0, import_fs.mkdirSync)(configDir, { recursive: true });
|
|
246
|
+
}
|
|
247
|
+
(0, import_fs.writeFileSync)(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
248
|
+
}
|
|
249
|
+
updateOpenClawEnvConfig(apiKey);
|
|
250
|
+
console.log("\n \u2705 API key saved to:");
|
|
251
|
+
console.log(" - ~/.openclaw/openclaw.json");
|
|
252
|
+
console.log("\n Get your API key at: https://unityclaw.com");
|
|
253
|
+
} else {
|
|
254
|
+
console.log("\n \u26A0\uFE0F Skipped. Configure later with:");
|
|
255
|
+
console.log(" npx @unityclaw/sdk config set apiKey <your-key>");
|
|
256
|
+
}
|
|
257
|
+
}
|
|
170
258
|
}
|
|
171
259
|
async function executeSkill(skillCommand) {
|
|
172
260
|
const args2 = [
|
package/dist/cli.js
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -44,6 +44,8 @@ var import_promises = require("fs/promises");
|
|
|
44
44
|
var import_fs = require("fs");
|
|
45
45
|
var import_path = __toESM(require("path"), 1);
|
|
46
46
|
var import_url = require("url");
|
|
47
|
+
var os = __toESM(require("os"), 1);
|
|
48
|
+
var readline = __toESM(require("readline"), 1);
|
|
47
49
|
var import_meta = {};
|
|
48
50
|
function getSkillsDir() {
|
|
49
51
|
const hasSkillDirs = (dir) => {
|
|
@@ -147,7 +149,58 @@ function getClaudeSkillsDir() {
|
|
|
147
149
|
}
|
|
148
150
|
function getOpenClawSkillsDir() {
|
|
149
151
|
const homeDir = process.env.HOME || process.env.USERPROFILE || "";
|
|
150
|
-
return process.env.OPENCLAW_SKILLS_DIR || import_path.default.join(homeDir, ".openclaw", "skills");
|
|
152
|
+
return process.env.OPENCLAW_SKILLS_DIR || import_path.default.join(homeDir, ".openclaw", "workspace", "skills");
|
|
153
|
+
}
|
|
154
|
+
async function promptForApiKey() {
|
|
155
|
+
const rl = readline.createInterface({
|
|
156
|
+
input: process.stdin,
|
|
157
|
+
output: process.stdout
|
|
158
|
+
});
|
|
159
|
+
return new Promise((resolve) => {
|
|
160
|
+
rl.question("\nEnter your UnityClaw API key (or press Enter to skip): ", (answer) => {
|
|
161
|
+
rl.close();
|
|
162
|
+
resolve(answer.trim() || void 0);
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
function updateOpenClawEnvConfig(apiKey) {
|
|
167
|
+
const openclawDir = import_path.default.join(os.homedir(), ".openclaw");
|
|
168
|
+
const openclawPath = import_path.default.join(openclawDir, "openclaw.json");
|
|
169
|
+
let config = {};
|
|
170
|
+
if ((0, import_fs.existsSync)(openclawPath)) {
|
|
171
|
+
try {
|
|
172
|
+
config = JSON.parse((0, import_fs.readFileSync)(openclawPath, "utf-8"));
|
|
173
|
+
} catch {
|
|
174
|
+
config = {};
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (!config.env) config.env = {};
|
|
178
|
+
const env = config.env;
|
|
179
|
+
if (!env.vars) env.vars = {};
|
|
180
|
+
env.vars.UNITYCLAW_API_KEY = apiKey;
|
|
181
|
+
if (!(0, import_fs.existsSync)(openclawDir)) {
|
|
182
|
+
(0, import_fs.mkdirSync)(openclawDir, { recursive: true });
|
|
183
|
+
}
|
|
184
|
+
(0, import_fs.writeFileSync)(openclawPath, JSON.stringify(config, null, 2), "utf-8");
|
|
185
|
+
}
|
|
186
|
+
function hasExistingApiKey() {
|
|
187
|
+
const sdkConfigPath = import_path.default.join(os.homedir(), ".unityclaw", "config.json");
|
|
188
|
+
if ((0, import_fs.existsSync)(sdkConfigPath)) {
|
|
189
|
+
try {
|
|
190
|
+
const config = JSON.parse((0, import_fs.readFileSync)(sdkConfigPath, "utf-8"));
|
|
191
|
+
if (config?.apiKey) return true;
|
|
192
|
+
} catch {
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
const openclawPath = import_path.default.join(os.homedir(), ".openclaw", "openclaw.json");
|
|
196
|
+
if ((0, import_fs.existsSync)(openclawPath)) {
|
|
197
|
+
try {
|
|
198
|
+
const config = JSON.parse((0, import_fs.readFileSync)(openclawPath, "utf-8"));
|
|
199
|
+
if (config?.env?.vars?.UNITYCLAW_API_KEY) return true;
|
|
200
|
+
} catch {
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return false;
|
|
151
204
|
}
|
|
152
205
|
async function installSkills(target, skills) {
|
|
153
206
|
const targetDir = target === "claude" ? getClaudeSkillsDir() : getOpenClawSkillsDir();
|
|
@@ -178,6 +231,41 @@ Installing ${skillsToInstall.length} skill(s):
|
|
|
178
231
|
}
|
|
179
232
|
console.log(`
|
|
180
233
|
\u2728 Done! Skills installed to: ${targetDir}`);
|
|
234
|
+
console.log("target", target);
|
|
235
|
+
console.log("hasExistingApiKey", hasExistingApiKey());
|
|
236
|
+
if (target === "openclaw" && !hasExistingApiKey()) {
|
|
237
|
+
console.log("\n\u{1F527} Configuration:");
|
|
238
|
+
console.log(" UnityClaw API key not found.\n");
|
|
239
|
+
const apiKey = await promptForApiKey();
|
|
240
|
+
if (apiKey) {
|
|
241
|
+
try {
|
|
242
|
+
(0, import_child_process.execSync)(`npx @unityclaw/sdk config set apiKey ${apiKey}`, { stdio: "pipe" });
|
|
243
|
+
} catch {
|
|
244
|
+
const configDir = import_path.default.join(os.homedir(), ".unityclaw");
|
|
245
|
+
const configPath = import_path.default.join(configDir, "config.json");
|
|
246
|
+
let config = {};
|
|
247
|
+
if ((0, import_fs.existsSync)(configPath)) {
|
|
248
|
+
try {
|
|
249
|
+
config = JSON.parse((0, import_fs.readFileSync)(configPath, "utf-8"));
|
|
250
|
+
} catch {
|
|
251
|
+
config = {};
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
config.apiKey = apiKey;
|
|
255
|
+
if (!(0, import_fs.existsSync)(configDir)) {
|
|
256
|
+
(0, import_fs.mkdirSync)(configDir, { recursive: true });
|
|
257
|
+
}
|
|
258
|
+
(0, import_fs.writeFileSync)(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
259
|
+
}
|
|
260
|
+
updateOpenClawEnvConfig(apiKey);
|
|
261
|
+
console.log("\n \u2705 API key saved to:");
|
|
262
|
+
console.log(" - ~/.openclaw/openclaw.json");
|
|
263
|
+
console.log("\n Get your API key at: https://unityclaw.com");
|
|
264
|
+
} else {
|
|
265
|
+
console.log("\n \u26A0\uFE0F Skipped. Configure later with:");
|
|
266
|
+
console.log(" npx @unityclaw/sdk config set apiKey <your-key>");
|
|
267
|
+
}
|
|
268
|
+
}
|
|
181
269
|
}
|
|
182
270
|
async function executeSkill(skillCommand) {
|
|
183
271
|
const args = [
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,286 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: unityclaw-video-generation-sora
|
|
3
|
-
description: Generate cinematic videos using OpenAI Sora AI model
|
|
4
|
-
version: 1.0.1
|
|
5
|
-
metadata:
|
|
6
|
-
openclaw:
|
|
7
|
-
requires:
|
|
8
|
-
env:
|
|
9
|
-
- UNITYCLAW_API_KEY
|
|
10
|
-
bins:
|
|
11
|
-
- node
|
|
12
|
-
- npm
|
|
13
|
-
primaryEnv: UNITYCLAW_API_KEY
|
|
14
|
-
emoji: "🎬"
|
|
15
|
-
homepage: https://unityclaw.com
|
|
16
|
-
install:
|
|
17
|
-
- kind: node
|
|
18
|
-
package: "@unityclaw/sdk"
|
|
19
|
-
bins: []
|
|
20
|
-
---
|
|
21
|
-
|
|
22
|
-
# UnityClaw Video Generation - Sora
|
|
23
|
-
|
|
24
|
-
Generate cinematic videos using OpenAI's Sora AI model.
|
|
25
|
-
|
|
26
|
-
## Installation
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
npm install @unityclaw/sdk
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## Configuration
|
|
33
|
-
|
|
34
|
-
Set your API key using one of these methods:
|
|
35
|
-
|
|
36
|
-
```bash
|
|
37
|
-
# Method 1: Use SDK CLI (recommended - persists across sessions)
|
|
38
|
-
npx @unityclaw/sdk config set apiKey your-api-key
|
|
39
|
-
|
|
40
|
-
# Method 2: Environment variable
|
|
41
|
-
export UNITYCLAW_API_KEY=your-api-key
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
## Response Structure
|
|
45
|
-
|
|
46
|
-
> **IMPORTANT:** The result has a nested structure. Use `result.success` to check overall success, and access data via `result.response.data`.
|
|
47
|
-
|
|
48
|
-
```typescript
|
|
49
|
-
interface UnityClawResult {
|
|
50
|
-
success: boolean; // ✅ Use this to check if SDK call succeeded
|
|
51
|
-
taskId: string; // Task identifier
|
|
52
|
-
taskFolder: string; // Path to task folder with logs
|
|
53
|
-
duration: number; // Request duration in ms
|
|
54
|
-
response: { // API response object
|
|
55
|
-
code: number; // 0 = success
|
|
56
|
-
data: Array<{ // ✅ Result data here
|
|
57
|
-
name: string;
|
|
58
|
-
contentType: string;
|
|
59
|
-
content: string; // URL to generated video
|
|
60
|
-
}> | null;
|
|
61
|
-
};
|
|
62
|
-
logs: Array<{ timestamp; level; message }>;
|
|
63
|
-
attachments: any[];
|
|
64
|
-
}
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
## Quick Start
|
|
68
|
-
|
|
69
|
-
```typescript
|
|
70
|
-
import { UnityClawClient } from '@unityclaw/sdk';
|
|
71
|
-
|
|
72
|
-
const client = new UnityClawClient();
|
|
73
|
-
|
|
74
|
-
const result = await client.video.sora({
|
|
75
|
-
prompt: 'A cat playing piano in a cozy living room',
|
|
76
|
-
orientation: { value: 'landscape', label: 'Landscape' }
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
// ✅ Correct: Check result.success, access data via result.response.data
|
|
80
|
-
if (result.success && result.response?.data) {
|
|
81
|
-
console.log('Generated video:', result.response.data);
|
|
82
|
-
}
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
## Available APIs
|
|
86
|
-
|
|
87
|
-
### Sora Video Generation
|
|
88
|
-
|
|
89
|
-
```typescript
|
|
90
|
-
// Basic usage
|
|
91
|
-
const result = await client.video.sora({
|
|
92
|
-
prompt: 'A drone shot flying over snowy mountains',
|
|
93
|
-
orientation: { value: 'landscape', label: 'Landscape' }
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
// With reference image
|
|
97
|
-
const result = await client.video.sora({
|
|
98
|
-
prompt: 'Animate this scene with gentle movement',
|
|
99
|
-
attachment: [{ tmp_url: 'https://...', name: 'scene.jpg' }],
|
|
100
|
-
orientation: { value: 'portrait', label: 'Portrait' }
|
|
101
|
-
});
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
### Sora Stable Video Generation
|
|
105
|
-
|
|
106
|
-
```typescript
|
|
107
|
-
// With duration and size control
|
|
108
|
-
const result = await client.video.soraStable({
|
|
109
|
-
prompt: 'A peaceful ocean wave at sunset',
|
|
110
|
-
size: { value: '1920x1080', label: '1080p' },
|
|
111
|
-
seconds: { value: '10', label: '10s' }
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
// Image-to-video
|
|
115
|
-
const result = await client.video.soraStable({
|
|
116
|
-
attachment: [{ tmp_url: 'https://...', name: 'image.jpg' }],
|
|
117
|
-
prompt: 'Bring this image to life',
|
|
118
|
-
size: { value: '1280x720', label: '720p' },
|
|
119
|
-
seconds: { value: '5', label: '5s' }
|
|
120
|
-
});
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
## Parameter Types
|
|
124
|
-
|
|
125
|
-
### SoraVideoParams
|
|
126
|
-
|
|
127
|
-
| Parameter | Type | Required | Description |
|
|
128
|
-
|-----------|------|----------|-------------|
|
|
129
|
-
| `prompt` | `string \| TextFieldItem[]` | No | Text description of the video |
|
|
130
|
-
| `attachment` | `AttachmentFieldItem[]` | No | Reference image for image-to-video |
|
|
131
|
-
| `orientation` | `LabelFieldItem` | Yes | Video orientation |
|
|
132
|
-
|
|
133
|
-
### SoraStableParams
|
|
134
|
-
|
|
135
|
-
| Parameter | Type | Required | Description |
|
|
136
|
-
|-----------|------|----------|-------------|
|
|
137
|
-
| `prompt` | `string \| TextFieldItem[]` | No | Text description |
|
|
138
|
-
| `attachment` | `AttachmentFieldItem[]` | No | Reference image |
|
|
139
|
-
| `size` | `LabelFieldItem` | No | Video resolution |
|
|
140
|
-
| `seconds` | `LabelFieldItem` | No | Video duration |
|
|
141
|
-
|
|
142
|
-
## Examples
|
|
143
|
-
|
|
144
|
-
### Text-to-Video
|
|
145
|
-
|
|
146
|
-
```typescript
|
|
147
|
-
const client = new UnityClawClient();
|
|
148
|
-
|
|
149
|
-
const result = await client.video.sora({
|
|
150
|
-
prompt: 'A cinematic drone shot flying through a futuristic neon-lit city at night, with flying cars and holographic billboards',
|
|
151
|
-
orientation: { value: 'landscape', label: 'Landscape' }
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
if (result.success && result.response?.data) {
|
|
155
|
-
console.log('Video URL:', result.response.data[0].content);
|
|
156
|
-
}
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
### Image-to-Video
|
|
160
|
-
|
|
161
|
-
```typescript
|
|
162
|
-
const result = await client.video.sora({
|
|
163
|
-
prompt: 'Animate this image with subtle camera movement and parallax effect',
|
|
164
|
-
attachment: [
|
|
165
|
-
{ tmp_url: 'https://example.com/landscape.jpg', name: 'landscape.jpg' }
|
|
166
|
-
],
|
|
167
|
-
orientation: { value: 'landscape', label: 'Landscape' }
|
|
168
|
-
});
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
### Long Duration Video
|
|
172
|
-
|
|
173
|
-
```typescript
|
|
174
|
-
const result = await client.video.soraStable({
|
|
175
|
-
prompt: 'A time-lapse of a flower blooming in a garden',
|
|
176
|
-
size: { value: '1920x1080', label: '1080p Landscape' },
|
|
177
|
-
seconds: { value: '15', label: '15s' }
|
|
178
|
-
});
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
### Portrait Video for Social Media
|
|
182
|
-
|
|
183
|
-
```typescript
|
|
184
|
-
const result = await client.video.sora({
|
|
185
|
-
prompt: 'A person walking through a misty forest with sunlight filtering through the trees',
|
|
186
|
-
orientation: { value: 'portrait', label: 'Portrait' }
|
|
187
|
-
});
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
### Batch Generation
|
|
191
|
-
|
|
192
|
-
```typescript
|
|
193
|
-
const prompts = [
|
|
194
|
-
'A serene beach at golden hour with waves gently rolling',
|
|
195
|
-
'A busy Tokyo street crossing at night with neon signs',
|
|
196
|
-
'An astronaut floating in space with Earth in the background'
|
|
197
|
-
];
|
|
198
|
-
|
|
199
|
-
const results = await Promise.all(
|
|
200
|
-
prompts.map(prompt => client.video.sora({
|
|
201
|
-
prompt,
|
|
202
|
-
orientation: { value: 'landscape', label: 'Landscape' }
|
|
203
|
-
}))
|
|
204
|
-
);
|
|
205
|
-
|
|
206
|
-
results.forEach((result, i) => {
|
|
207
|
-
if (result.success && result.response?.data) {
|
|
208
|
-
console.log(`Video ${i + 1}: ${result.response.data?.[0]?.content}`);
|
|
209
|
-
}
|
|
210
|
-
});
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
## Orientation Options
|
|
214
|
-
|
|
215
|
-
| Value | Label | Aspect Ratio | Use Case |
|
|
216
|
-
|-------|-------|--------------|----------|
|
|
217
|
-
| `landscape` | Landscape | 16:9 | YouTube, presentations |
|
|
218
|
-
| `portrait` | Portrait | 9:16 | TikTok, Instagram Reels |
|
|
219
|
-
| `square` | Square | 1:1 | Instagram feed |
|
|
220
|
-
|
|
221
|
-
## Response Format
|
|
222
|
-
|
|
223
|
-
```typescript
|
|
224
|
-
interface AttachmentResult {
|
|
225
|
-
name: string;
|
|
226
|
-
contentType: 'attachment/url';
|
|
227
|
-
content: string; // URL to generated video
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
interface APIResponse<AttachmentResult[]> {
|
|
231
|
-
code: number;
|
|
232
|
-
msg?: string;
|
|
233
|
-
data?: AttachmentResult[];
|
|
234
|
-
}
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
## Error Handling
|
|
238
|
-
|
|
239
|
-
```typescript
|
|
240
|
-
const result = await client.video.sora({
|
|
241
|
-
prompt: 'A test video',
|
|
242
|
-
orientation: { value: 'landscape', label: 'Landscape' }
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
if (!result.success) {
|
|
246
|
-
console.error('Request failed');
|
|
247
|
-
console.log('Check logs:', result.logs);
|
|
248
|
-
return;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
if (result.response?.code !== 0) {
|
|
252
|
-
console.error('API error:', result.response);
|
|
253
|
-
return;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
// Success
|
|
257
|
-
console.log('Success:', result.response.data);
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
## Task Folders
|
|
261
|
-
|
|
262
|
-
Each execution creates a task folder:
|
|
263
|
-
|
|
264
|
-
```typescript
|
|
265
|
-
const result = await client.video.sora({
|
|
266
|
-
prompt: 'test',
|
|
267
|
-
orientation: { value: 'landscape', label: 'Landscape' }
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
console.log('Task ID:', result.taskId);
|
|
271
|
-
console.log('Task Folder:', result.taskFolder);
|
|
272
|
-
console.log('Downloaded Videos:', result.attachments);
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
## Best Practices
|
|
276
|
-
|
|
277
|
-
1. **Detailed Prompts**: Include camera angles, lighting, and movement descriptions
|
|
278
|
-
2. **Reference Images**: Use high-quality images for image-to-video
|
|
279
|
-
3. **Orientation**: Match orientation to target platform
|
|
280
|
-
4. **Duration**: Use `soraStable` for precise duration control
|
|
281
|
-
|
|
282
|
-
## Related Skills
|
|
283
|
-
|
|
284
|
-
- [unityclaw-video-generation-veo](../unityclaw-video-generation-veo/SKILL.md) - Google Veo video generation
|
|
285
|
-
- [unityclaw-video-generation-kling](../unityclaw-video-generation-kling/SKILL.md) - Kling video generation
|
|
286
|
-
- [unityclaw-image-generation](../unityclaw-image-generation/SKILL.md) - Generate reference images
|
|
@@ -1,275 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: unityclaw-video-generation-veo
|
|
3
|
-
description: Generate high-quality videos using Google Veo AI model
|
|
4
|
-
version: 1.0.1
|
|
5
|
-
metadata:
|
|
6
|
-
openclaw:
|
|
7
|
-
requires:
|
|
8
|
-
env:
|
|
9
|
-
- UNITYCLAW_API_KEY
|
|
10
|
-
bins:
|
|
11
|
-
- node
|
|
12
|
-
- npm
|
|
13
|
-
primaryEnv: UNITYCLAW_API_KEY
|
|
14
|
-
emoji: "🎥"
|
|
15
|
-
homepage: https://unityclaw.com
|
|
16
|
-
install:
|
|
17
|
-
- kind: node
|
|
18
|
-
package: "@unityclaw/sdk"
|
|
19
|
-
bins: []
|
|
20
|
-
---
|
|
21
|
-
|
|
22
|
-
# UnityClaw Video Generation - Veo
|
|
23
|
-
|
|
24
|
-
Generate high-quality videos using Google's Veo AI model with advanced control over aspect ratio, resolution, and duration.
|
|
25
|
-
|
|
26
|
-
## Installation
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
npm install @unityclaw/sdk
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## Configuration
|
|
33
|
-
|
|
34
|
-
Set your API key using one of these methods:
|
|
35
|
-
|
|
36
|
-
```bash
|
|
37
|
-
# Method 1: Use SDK CLI (recommended - persists across sessions)
|
|
38
|
-
npx @unityclaw/sdk config set apiKey your-api-key
|
|
39
|
-
|
|
40
|
-
# Method 2: Environment variable
|
|
41
|
-
export UNITYCLAW_API_KEY=your-api-key
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
## Response Structure
|
|
45
|
-
|
|
46
|
-
> **IMPORTANT:** The result has a nested structure. Use `result.success` to check overall success, and access data via `result.response.data`.
|
|
47
|
-
|
|
48
|
-
```typescript
|
|
49
|
-
interface UnityClawResult {
|
|
50
|
-
success: boolean; // ✅ Use this to check if SDK call succeeded
|
|
51
|
-
taskId: string; // Task identifier
|
|
52
|
-
taskFolder: string; // Path to task folder with logs
|
|
53
|
-
duration: number; // Request duration in ms
|
|
54
|
-
response: { // API response object
|
|
55
|
-
code: number; // 0 = success
|
|
56
|
-
data: Array<{ // ✅ Result data here
|
|
57
|
-
name: string;
|
|
58
|
-
contentType: string;
|
|
59
|
-
content: string; // URL to generated video
|
|
60
|
-
}> | null;
|
|
61
|
-
};
|
|
62
|
-
logs: Array<{ timestamp; level; message }>;
|
|
63
|
-
attachments: any[];
|
|
64
|
-
}
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
## Quick Start
|
|
68
|
-
|
|
69
|
-
```typescript
|
|
70
|
-
import { UnityClawClient } from '@unityclaw/sdk';
|
|
71
|
-
|
|
72
|
-
const client = new UnityClawClient();
|
|
73
|
-
|
|
74
|
-
const result = await client.video.veo({
|
|
75
|
-
prompt: 'A beautiful sunset over the ocean with waves crashing',
|
|
76
|
-
aspect_ratio: { value: '16:9', label: '16:9' },
|
|
77
|
-
duration: { value: '8', label: '8s' }
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
// ✅ Correct: Check result.success, access data via result.response.data
|
|
81
|
-
if (result.success && result.response?.data) {
|
|
82
|
-
console.log('Generated video:', result.response.data);
|
|
83
|
-
}
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
## API Reference
|
|
87
|
-
|
|
88
|
-
### veo()
|
|
89
|
-
|
|
90
|
-
Generate video using Google Veo model.
|
|
91
|
-
|
|
92
|
-
```typescript
|
|
93
|
-
await client.video.veo({
|
|
94
|
-
prompt?: string | TextFieldItem[];
|
|
95
|
-
attachment?: AttachmentFieldItem[];
|
|
96
|
-
first_frame?: AttachmentFieldItem[];
|
|
97
|
-
last_frame?: AttachmentFieldItem[];
|
|
98
|
-
aspect_ratio?: LabelFieldItem | string;
|
|
99
|
-
resolution?: LabelFieldItem | string;
|
|
100
|
-
duration?: LabelFieldItem | string;
|
|
101
|
-
}): Promise<APIResponse<AttachmentResult[]>>
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
## Parameters
|
|
105
|
-
|
|
106
|
-
| Parameter | Type | Required | Description |
|
|
107
|
-
|-----------|------|----------|-------------|
|
|
108
|
-
| `prompt` | `string \| TextFieldItem[]` | No* | Text description (*required if no attachment) |
|
|
109
|
-
| `attachment` | `AttachmentFieldItem[]` | No* | Reference image (*required if no prompt) |
|
|
110
|
-
| `first_frame` | `AttachmentFieldItem[]` | No | Starting frame for video |
|
|
111
|
-
| `last_frame` | `AttachmentFieldItem[]` | No | Ending frame for video |
|
|
112
|
-
| `aspect_ratio` | `LabelFieldItem \| string` | No | Video aspect ratio |
|
|
113
|
-
| `resolution` | `LabelFieldItem \| string` | No | Video resolution |
|
|
114
|
-
| `duration` | `LabelFieldItem \| string` | No | Video duration in seconds |
|
|
115
|
-
|
|
116
|
-
## Examples
|
|
117
|
-
|
|
118
|
-
### Text-to-Video
|
|
119
|
-
|
|
120
|
-
```typescript
|
|
121
|
-
const client = new UnityClawClient();
|
|
122
|
-
|
|
123
|
-
const result = await client.video.veo({
|
|
124
|
-
prompt: 'A majestic eagle soaring through misty mountain peaks at sunrise',
|
|
125
|
-
aspect_ratio: { value: '16:9', label: '16:9' },
|
|
126
|
-
resolution: { value: '1080p', label: '1080p' },
|
|
127
|
-
duration: { value: '8', label: '8s' }
|
|
128
|
-
});
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
### Image-to-Video
|
|
132
|
-
|
|
133
|
-
```typescript
|
|
134
|
-
const result = await client.video.veo({
|
|
135
|
-
attachment: [
|
|
136
|
-
{ tmp_url: 'https://example.com/landscape.jpg', name: 'landscape.jpg' }
|
|
137
|
-
],
|
|
138
|
-
aspect_ratio: { value: '16:9', label: '16:9' },
|
|
139
|
-
duration: { value: '5', label: '5s' }
|
|
140
|
-
});
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
### First/Last Frame Control
|
|
144
|
-
|
|
145
|
-
```typescript
|
|
146
|
-
// Generate video with specific start and end frames
|
|
147
|
-
const result = await client.video.veo({
|
|
148
|
-
first_frame: [
|
|
149
|
-
{ tmp_url: 'https://example.com/start.jpg', name: 'start.jpg' }
|
|
150
|
-
],
|
|
151
|
-
last_frame: [
|
|
152
|
-
{ tmp_url: 'https://example.com/end.jpg', name: 'end.jpg' }
|
|
153
|
-
],
|
|
154
|
-
prompt: 'Smooth transition between the two scenes',
|
|
155
|
-
duration: { value: '8', label: '8s' }
|
|
156
|
-
});
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
### High Resolution Video
|
|
160
|
-
|
|
161
|
-
```typescript
|
|
162
|
-
const result = await client.video.veo({
|
|
163
|
-
prompt: 'A cinematic shot of a luxury car driving through a city at night',
|
|
164
|
-
aspect_ratio: { value: '16:9', label: '16:9' },
|
|
165
|
-
resolution: { value: '4K', label: '4K' },
|
|
166
|
-
duration: { value: '10', label: '10s' }
|
|
167
|
-
});
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
### Portrait Video
|
|
171
|
-
|
|
172
|
-
```typescript
|
|
173
|
-
const result = await client.video.veo({
|
|
174
|
-
prompt: 'A model walking down a fashion runway with dramatic lighting',
|
|
175
|
-
aspect_ratio: { value: '9:16', label: '9:16' },
|
|
176
|
-
duration: { value: '5', label: '5s' }
|
|
177
|
-
});
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
### Simple String Parameters
|
|
181
|
-
|
|
182
|
-
```typescript
|
|
183
|
-
// You can also use simple strings instead of LabelFieldItem
|
|
184
|
-
const result = await client.video.veo({
|
|
185
|
-
prompt: 'A peaceful garden scene',
|
|
186
|
-
aspect_ratio: '16:9',
|
|
187
|
-
resolution: '1080p',
|
|
188
|
-
duration: '8'
|
|
189
|
-
});
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
## Aspect Ratio Options
|
|
193
|
-
|
|
194
|
-
| Value | Use Case |
|
|
195
|
-
|-------|----------|
|
|
196
|
-
| `16:9` | Standard landscape (YouTube, presentations) |
|
|
197
|
-
| `9:16` | Portrait (TikTok, Instagram Reels) |
|
|
198
|
-
| `1:1` | Square (Instagram feed) |
|
|
199
|
-
|
|
200
|
-
## Duration Options
|
|
201
|
-
|
|
202
|
-
| Value | Description |
|
|
203
|
-
|-------|-------------|
|
|
204
|
-
| `5` | 5 seconds |
|
|
205
|
-
| `8` | 8 seconds (default) |
|
|
206
|
-
| `10` | 10 seconds |
|
|
207
|
-
|
|
208
|
-
## Resolution Options
|
|
209
|
-
|
|
210
|
-
| Value | Description |
|
|
211
|
-
|-------|-------------|
|
|
212
|
-
| `720p` | HD quality |
|
|
213
|
-
| `1080p` | Full HD |
|
|
214
|
-
| `4K` | Ultra HD |
|
|
215
|
-
|
|
216
|
-
## Response Format
|
|
217
|
-
|
|
218
|
-
```typescript
|
|
219
|
-
interface AttachmentResult {
|
|
220
|
-
name: string;
|
|
221
|
-
contentType: 'attachment/url';
|
|
222
|
-
content: string; // URL to generated video
|
|
223
|
-
}
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
## Error Handling
|
|
227
|
-
|
|
228
|
-
```typescript
|
|
229
|
-
const result = await client.video.veo({
|
|
230
|
-
prompt: 'A test video',
|
|
231
|
-
aspect_ratio: '16:9'
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
if (!result.success) {
|
|
235
|
-
console.error('Request failed');
|
|
236
|
-
console.log('Check logs:', result.logs);
|
|
237
|
-
return;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
if (result.response?.code !== 0) {
|
|
241
|
-
console.error('API error:', result.response);
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// Success
|
|
246
|
-
console.log('Success:', result.response.data);
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
## Task Folders
|
|
250
|
-
|
|
251
|
-
Each execution creates a task folder:
|
|
252
|
-
|
|
253
|
-
```typescript
|
|
254
|
-
const result = await client.video.veo({
|
|
255
|
-
prompt: 'test',
|
|
256
|
-
aspect_ratio: '16:9'
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
console.log('Task ID:', result.taskId);
|
|
260
|
-
console.log('Task Folder:', result.taskFolder);
|
|
261
|
-
console.log('Downloaded Videos:', result.attachments);
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
## Best Practices
|
|
265
|
-
|
|
266
|
-
1. **First/Last Frame**: Use for precise control over video start/end
|
|
267
|
-
2. **Resolution**: Match to your output requirements
|
|
268
|
-
3. **Duration**: Shorter videos generate faster
|
|
269
|
-
4. **Prompts**: Be specific about camera movement and style
|
|
270
|
-
|
|
271
|
-
## Related Skills
|
|
272
|
-
|
|
273
|
-
- [unityclaw-video-generation-sora](../unityclaw-video-generation-sora/SKILL.md) - OpenAI Sora video generation
|
|
274
|
-
- [unityclaw-video-generation-kling](../unityclaw-video-generation-kling/SKILL.md) - Kling video generation
|
|
275
|
-
- [unityclaw-image-generation](../unityclaw-image-generation/SKILL.md) - Generate reference images
|