@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 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
@@ -3,7 +3,7 @@ import {
3
3
  executeSkill,
4
4
  installSkills,
5
5
  listSkills
6
- } from "./chunk-FAGVVFFQ.js";
6
+ } from "./chunk-CVOSGIUI.js";
7
7
 
8
8
  // src/cli.ts
9
9
  import { program } from "commander";
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
@@ -6,7 +6,7 @@ import {
6
6
  installSkills,
7
7
  listSkills,
8
8
  parseSkill
9
- } from "./chunk-FAGVVFFQ.js";
9
+ } from "./chunk-CVOSGIUI.js";
10
10
  export {
11
11
  executeSkill,
12
12
  getClaudeSkillsDir,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unityclaw/skills",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "UnityClaw Skills - Claude Code and OpenClaw skill definitions for AI-powered image/video generation, media analysis, and more",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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