clawra 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +117 -0
- package/bin/clawra.js +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +89 -0
- package/dist/integrators/openclaw-config.d.ts +4 -0
- package/dist/integrators/openclaw-config.js +50 -0
- package/dist/integrators/skills.d.ts +4 -0
- package/dist/integrators/skills.js +202 -0
- package/dist/integrators/soul.d.ts +5 -0
- package/dist/integrators/soul.js +59 -0
- package/dist/validators.d.ts +4 -0
- package/dist/validators.js +17 -0
- package/package.json +43 -0
- package/templates/clawra-photo.md +282 -0
- package/templates/clawra-videocall.md +161 -0
- package/templates/soul-fragment.md +19 -0
package/README.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Clawra CLI
|
|
2
|
+
|
|
3
|
+
Onboard Clawra skills (photo & video call) to [OpenClaw](https://openclaw.ai) with a single command.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx clawra@latest
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
This will:
|
|
12
|
+
|
|
13
|
+
1. **Prompt for your API key** from [portal.sume.dev](https://portal.sume.dev/settings/api-keys)
|
|
14
|
+
2. **Validate** the key against the Sume API
|
|
15
|
+
3. **Install skills** to `~/.openclaw/skills/`
|
|
16
|
+
- `clawra-photo` - Send AI-edited selfies
|
|
17
|
+
- `clawra-videocall` - Start video calls
|
|
18
|
+
4. **Update OpenClaw config** (`~/.openclaw/openclaw.json`)
|
|
19
|
+
5. **Update SOUL.md** with Clawra capabilities
|
|
20
|
+
|
|
21
|
+
## What Gets Installed
|
|
22
|
+
|
|
23
|
+
### Skills Directory
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
~/.openclaw/skills/
|
|
27
|
+
├── clawra-photo/
|
|
28
|
+
│ └── SKILL.md
|
|
29
|
+
└── clawra-videocall/
|
|
30
|
+
└── SKILL.md
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### OpenClaw Config
|
|
34
|
+
|
|
35
|
+
Added to `~/.openclaw/openclaw.json`:
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"skills": {
|
|
40
|
+
"load": {
|
|
41
|
+
"extraDirs": ["~/.openclaw/skills"]
|
|
42
|
+
},
|
|
43
|
+
"entries": {
|
|
44
|
+
"clawra-photo": {
|
|
45
|
+
"enabled": true,
|
|
46
|
+
"env": { "SUME_API_KEY": "ak_..." }
|
|
47
|
+
},
|
|
48
|
+
"clawra-videocall": {
|
|
49
|
+
"enabled": true,
|
|
50
|
+
"apiKey": "ak_..."
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### SOUL.md
|
|
58
|
+
|
|
59
|
+
Appends Clawra capabilities to `~/.openclaw/workspace/SOUL.md`:
|
|
60
|
+
|
|
61
|
+
```markdown
|
|
62
|
+
## Clawra Capabilities
|
|
63
|
+
|
|
64
|
+
### 📸 Photo Skill (clawra-photo)
|
|
65
|
+
You can send AI-edited selfies...
|
|
66
|
+
|
|
67
|
+
### 📹 Video Call Skill (clawra-videocall)
|
|
68
|
+
You can start video calls...
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Skills Overview
|
|
72
|
+
|
|
73
|
+
### clawra-photo
|
|
74
|
+
|
|
75
|
+
Generate AI-edited selfies using xAI's Grok Imagine and send them via OpenClaw messaging.
|
|
76
|
+
|
|
77
|
+
**Triggers:**
|
|
78
|
+
- "send a pic", "send me a selfie"
|
|
79
|
+
- "what are you doing?", "where are you?"
|
|
80
|
+
- "send a pic of you wearing..."
|
|
81
|
+
|
|
82
|
+
**Requirements:**
|
|
83
|
+
- `FAL_KEY` - Get from [fal.ai](https://fal.ai/dashboard/keys)
|
|
84
|
+
- OpenClaw gateway running
|
|
85
|
+
|
|
86
|
+
### clawra-videocall
|
|
87
|
+
|
|
88
|
+
Start video calls and share join links with users.
|
|
89
|
+
|
|
90
|
+
**Triggers:**
|
|
91
|
+
- "let's hop on a call", "video call"
|
|
92
|
+
- "줌 콜하자", "화상통화하자"
|
|
93
|
+
|
|
94
|
+
## Development
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
# Install dependencies
|
|
98
|
+
cd cli
|
|
99
|
+
npm install
|
|
100
|
+
|
|
101
|
+
# Run locally
|
|
102
|
+
npm run dev
|
|
103
|
+
|
|
104
|
+
# Build
|
|
105
|
+
npm run build
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Publishing
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
cd cli
|
|
112
|
+
npm publish
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## License
|
|
116
|
+
|
|
117
|
+
MIT
|
package/bin/clawra.js
ADDED
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { input } from '@inquirer/prompts';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import ora from 'ora';
|
|
5
|
+
import { validateApiKey } from './validators.js';
|
|
6
|
+
import { updateOpenClawConfig } from './integrators/openclaw-config.js';
|
|
7
|
+
import { installSkills } from './integrators/skills.js';
|
|
8
|
+
import { updateSoul } from './integrators/soul.js';
|
|
9
|
+
async function main() {
|
|
10
|
+
console.log(chalk.bold.cyan('\n🐾 Clawra Setup\n'));
|
|
11
|
+
console.log(chalk.dim('Integrate photo & video call skills into OpenClaw\n'));
|
|
12
|
+
// Step 1: API Key
|
|
13
|
+
console.log(chalk.yellow('📋 Step 1: Get your API key'));
|
|
14
|
+
console.log(chalk.blue.underline(' https://portal.sume.dev/settings/api-keys\n'));
|
|
15
|
+
const apiKey = await input({
|
|
16
|
+
message: 'Paste your API key:',
|
|
17
|
+
validate: (val) => {
|
|
18
|
+
if (!val.trim())
|
|
19
|
+
return 'API key is required';
|
|
20
|
+
if (!val.startsWith('ak_'))
|
|
21
|
+
return 'Key should start with ak_';
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
// Validate API key
|
|
26
|
+
const spinner = ora('Validating API key...').start();
|
|
27
|
+
try {
|
|
28
|
+
const isValid = await validateApiKey(apiKey.trim());
|
|
29
|
+
if (!isValid) {
|
|
30
|
+
spinner.fail('Invalid API key. Please check and try again.');
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
spinner.succeed('API key validated');
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
spinner.fail('Failed to validate API key. Check your internet connection.');
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
// Step 2: Install Skills
|
|
40
|
+
console.log(chalk.yellow('\n📦 Step 2: Installing skills...'));
|
|
41
|
+
const skillSpinner = ora('Copying skill files...').start();
|
|
42
|
+
try {
|
|
43
|
+
await installSkills();
|
|
44
|
+
skillSpinner.succeed('Skills installed to ~/.openclaw/skills/');
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
skillSpinner.fail(`Failed to install skills: ${error}`);
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
// Step 3: Update OpenClaw config
|
|
51
|
+
const configSpinner = ora('Updating OpenClaw configuration...').start();
|
|
52
|
+
try {
|
|
53
|
+
await updateOpenClawConfig(apiKey.trim());
|
|
54
|
+
configSpinner.succeed('OpenClaw config updated');
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
configSpinner.fail(`Failed to update config: ${error}`);
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
// Step 4: Update SOUL.md
|
|
61
|
+
console.log(chalk.yellow('\n🧠 Step 3: Updating agent persona...'));
|
|
62
|
+
const soulSpinner = ora('Updating SOUL.md...').start();
|
|
63
|
+
try {
|
|
64
|
+
const updated = await updateSoul();
|
|
65
|
+
if (updated) {
|
|
66
|
+
soulSpinner.succeed('Agent persona updated');
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
soulSpinner.info('Persona already configured (skipped)');
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
soulSpinner.warn(`Could not update SOUL.md: ${error}`);
|
|
74
|
+
console.log(chalk.dim(' You may need to manually update your agent persona.'));
|
|
75
|
+
}
|
|
76
|
+
// Done!
|
|
77
|
+
console.log(chalk.green.bold('\n✅ Setup complete!\n'));
|
|
78
|
+
console.log(chalk.white('Skills installed:'));
|
|
79
|
+
console.log(chalk.cyan(' • clawra-photo') + chalk.dim(' - Send AI-edited selfies'));
|
|
80
|
+
console.log(chalk.cyan(' • clawra-videocall') + chalk.dim(' - Start video calls'));
|
|
81
|
+
console.log(chalk.white('\nTry saying:'));
|
|
82
|
+
console.log(chalk.dim(' "send me a selfie wearing a santa hat"'));
|
|
83
|
+
console.log(chalk.dim(' "let\'s hop on a video call"'));
|
|
84
|
+
console.log();
|
|
85
|
+
}
|
|
86
|
+
main().catch((error) => {
|
|
87
|
+
console.error(chalk.red('Error:'), error.message);
|
|
88
|
+
process.exit(1);
|
|
89
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs';
|
|
2
|
+
import { homedir } from 'os';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
/**
|
|
5
|
+
* Update ~/.openclaw/openclaw.json with clawra skills configuration
|
|
6
|
+
*/
|
|
7
|
+
export async function updateOpenClawConfig(apiKey) {
|
|
8
|
+
const openclawDir = join(homedir(), '.openclaw');
|
|
9
|
+
const configPath = join(openclawDir, 'openclaw.json');
|
|
10
|
+
// Ensure .openclaw directory exists
|
|
11
|
+
if (!existsSync(openclawDir)) {
|
|
12
|
+
mkdirSync(openclawDir, { recursive: true });
|
|
13
|
+
}
|
|
14
|
+
// Read existing config or start fresh
|
|
15
|
+
let config = {};
|
|
16
|
+
if (existsSync(configPath)) {
|
|
17
|
+
try {
|
|
18
|
+
const content = readFileSync(configPath, 'utf-8');
|
|
19
|
+
config = JSON.parse(content);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
// If parse fails, start fresh
|
|
23
|
+
config = {};
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
// Initialize skills section if not present
|
|
27
|
+
config.skills = config.skills || {};
|
|
28
|
+
config.skills.load = config.skills.load || {};
|
|
29
|
+
config.skills.load.extraDirs = config.skills.load.extraDirs || [];
|
|
30
|
+
config.skills.entries = config.skills.entries || {};
|
|
31
|
+
// Add clawra skills directory to extraDirs
|
|
32
|
+
const skillsDir = '~/.openclaw/skills';
|
|
33
|
+
if (!config.skills.load.extraDirs.includes(skillsDir)) {
|
|
34
|
+
config.skills.load.extraDirs.push(skillsDir);
|
|
35
|
+
}
|
|
36
|
+
// Configure clawra-photo skill
|
|
37
|
+
config.skills.entries['clawra-photo'] = {
|
|
38
|
+
enabled: true,
|
|
39
|
+
env: {
|
|
40
|
+
SUME_API_KEY: apiKey
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
// Configure clawra-videocall skill
|
|
44
|
+
config.skills.entries['clawra-videocall'] = {
|
|
45
|
+
enabled: true,
|
|
46
|
+
apiKey: apiKey
|
|
47
|
+
};
|
|
48
|
+
// Write updated config
|
|
49
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
50
|
+
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { writeFileSync, mkdirSync, existsSync } from 'fs';
|
|
2
|
+
import { homedir } from 'os';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import { dirname } from 'path';
|
|
6
|
+
import { readFileSync } from 'fs';
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = dirname(__filename);
|
|
9
|
+
/**
|
|
10
|
+
* Install clawra skill files to ~/.openclaw/skills/
|
|
11
|
+
*/
|
|
12
|
+
export async function installSkills() {
|
|
13
|
+
const skillsBaseDir = join(homedir(), '.openclaw', 'skills');
|
|
14
|
+
// Create skills directories
|
|
15
|
+
const photoDir = join(skillsBaseDir, 'clawra-photo');
|
|
16
|
+
const videoDir = join(skillsBaseDir, 'clawra-videocall');
|
|
17
|
+
mkdirSync(photoDir, { recursive: true });
|
|
18
|
+
mkdirSync(videoDir, { recursive: true });
|
|
19
|
+
// Read template files (bundled with package)
|
|
20
|
+
const templatesDir = join(__dirname, '..', '..', 'templates');
|
|
21
|
+
// Write clawra-photo SKILL.md
|
|
22
|
+
const photoSkillContent = readTemplateOrFallback(join(templatesDir, 'clawra-photo.md'), getClawraPhotoSkill());
|
|
23
|
+
writeFileSync(join(photoDir, 'SKILL.md'), photoSkillContent, 'utf-8');
|
|
24
|
+
// Write clawra-videocall SKILL.md
|
|
25
|
+
const videoSkillContent = readTemplateOrFallback(join(templatesDir, 'clawra-videocall.md'), getClawraVideocallSkill());
|
|
26
|
+
writeFileSync(join(videoDir, 'SKILL.md'), videoSkillContent, 'utf-8');
|
|
27
|
+
}
|
|
28
|
+
function readTemplateOrFallback(path, fallback) {
|
|
29
|
+
try {
|
|
30
|
+
if (existsSync(path)) {
|
|
31
|
+
return readFileSync(path, 'utf-8');
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// Fall through to fallback
|
|
36
|
+
}
|
|
37
|
+
return fallback;
|
|
38
|
+
}
|
|
39
|
+
function getClawraPhotoSkill() {
|
|
40
|
+
return `---
|
|
41
|
+
name: clawra-photo
|
|
42
|
+
description: Edit Clawra's reference image with Grok Imagine (xAI Aurora) and send selfies to messaging channels via OpenClaw
|
|
43
|
+
allowed-tools: Bash(npm:*) Bash(npx:*) Bash(openclaw:*) Bash(curl:*) Read Write WebFetch
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
# Clawra Selfie
|
|
47
|
+
|
|
48
|
+
Edit a fixed reference image using xAI's Grok Imagine model and distribute it across messaging platforms (WhatsApp, Telegram, Discord, Slack, etc.) via OpenClaw.
|
|
49
|
+
|
|
50
|
+
## Reference Image
|
|
51
|
+
|
|
52
|
+
The skill uses a fixed reference image hosted on jsDelivr CDN:
|
|
53
|
+
|
|
54
|
+
\`\`\`
|
|
55
|
+
https://cdn.jsdelivr.net/gh/SumeLabs/clawra@main/clawra-photo/assets/clawra.png
|
|
56
|
+
\`\`\`
|
|
57
|
+
|
|
58
|
+
## When to Use
|
|
59
|
+
|
|
60
|
+
- User says "send a pic", "send me a pic", "send a photo", "send a selfie"
|
|
61
|
+
- User says "send a pic of you...", "send a selfie of you..."
|
|
62
|
+
- User asks "what are you doing?", "how are you doing?", "where are you?"
|
|
63
|
+
- User describes a context: "send a pic wearing...", "send a pic at..."
|
|
64
|
+
- User wants Clawra to appear in a specific outfit, location, or situation
|
|
65
|
+
|
|
66
|
+
## Quick Reference
|
|
67
|
+
|
|
68
|
+
### Required Environment Variables
|
|
69
|
+
|
|
70
|
+
\`\`\`bash
|
|
71
|
+
FAL_KEY=your_fal_api_key # Get from https://fal.ai/dashboard/keys
|
|
72
|
+
SUME_API_KEY=your_sume_api_key # From portal.sume.dev
|
|
73
|
+
OPENCLAW_GATEWAY_TOKEN=your_token # From: openclaw doctor --generate-gateway-token
|
|
74
|
+
\`\`\`
|
|
75
|
+
|
|
76
|
+
### Workflow
|
|
77
|
+
|
|
78
|
+
1. **Get user prompt** for how to edit the image
|
|
79
|
+
2. **Edit image** via fal.ai Grok Imagine Edit API with fixed reference
|
|
80
|
+
3. **Extract image URL** from response
|
|
81
|
+
4. **Send to OpenClaw** with target channel(s)
|
|
82
|
+
|
|
83
|
+
## Prompt Modes
|
|
84
|
+
|
|
85
|
+
### Mode 1: Mirror Selfie (default)
|
|
86
|
+
Best for: outfit showcases, full-body shots, fashion content
|
|
87
|
+
|
|
88
|
+
\`\`\`
|
|
89
|
+
make a pic of this person, but [user's context]. the person is taking a mirror selfie
|
|
90
|
+
\`\`\`
|
|
91
|
+
|
|
92
|
+
### Mode 2: Direct Selfie
|
|
93
|
+
Best for: close-up portraits, location shots, emotional expressions
|
|
94
|
+
|
|
95
|
+
\`\`\`
|
|
96
|
+
a close-up selfie taken by herself at [user's context], direct eye contact with the camera, looking straight into the lens, eyes centered and clearly visible, not a mirror selfie, phone held at arm's length, face fully visible
|
|
97
|
+
\`\`\`
|
|
98
|
+
|
|
99
|
+
## Implementation
|
|
100
|
+
|
|
101
|
+
\`\`\`bash
|
|
102
|
+
REFERENCE_IMAGE="https://cdn.jsdelivr.net/gh/SumeLabs/clawra@main/clawra-photo/assets/clawra.png"
|
|
103
|
+
|
|
104
|
+
JSON_PAYLOAD=$(jq -n \\
|
|
105
|
+
--arg image_url "$REFERENCE_IMAGE" \\
|
|
106
|
+
--arg prompt "$PROMPT" \\
|
|
107
|
+
'{image_url: $image_url, prompt: $prompt, num_images: 1, output_format: "jpeg"}')
|
|
108
|
+
|
|
109
|
+
curl -X POST "https://fal.run/xai/grok-imagine-image/edit" \\
|
|
110
|
+
-H "Authorization: Key $FAL_KEY" \\
|
|
111
|
+
-H "Content-Type: application/json" \\
|
|
112
|
+
-d "$JSON_PAYLOAD"
|
|
113
|
+
\`\`\`
|
|
114
|
+
|
|
115
|
+
Then send via OpenClaw:
|
|
116
|
+
|
|
117
|
+
\`\`\`bash
|
|
118
|
+
openclaw message send \\
|
|
119
|
+
--action send \\
|
|
120
|
+
--channel "<TARGET_CHANNEL>" \\
|
|
121
|
+
--message "<CAPTION_TEXT>" \\
|
|
122
|
+
--media "<IMAGE_URL>"
|
|
123
|
+
\`\`\`
|
|
124
|
+
`;
|
|
125
|
+
}
|
|
126
|
+
function getClawraVideocallSkill() {
|
|
127
|
+
return `---
|
|
128
|
+
name: clawra-videocall
|
|
129
|
+
description: Start a video call (like Zoom or Google Meet) and share the join link.
|
|
130
|
+
metadata:
|
|
131
|
+
{
|
|
132
|
+
"openclaw":
|
|
133
|
+
{
|
|
134
|
+
"emoji": "📹",
|
|
135
|
+
"skillKey": "clawra-videocall",
|
|
136
|
+
"requires": { "bins": ["curl"] },
|
|
137
|
+
},
|
|
138
|
+
}
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
# Video Call
|
|
142
|
+
|
|
143
|
+
Start a video call from inside a conversation and send the \`join_url\` to the user.
|
|
144
|
+
|
|
145
|
+
## When To Use
|
|
146
|
+
|
|
147
|
+
Use this when:
|
|
148
|
+
|
|
149
|
+
- The user says things like: "줌 콜하자", "구글 미트 하자", "화상통화하자", "영상통화하자", "콜 하자"
|
|
150
|
+
- The user says: "Let's hop on a call", "video call", "zoom call", "let's do a call"
|
|
151
|
+
- The conversation is getting too long/complex for chat.
|
|
152
|
+
- You (the agent) decide voice/video would be faster.
|
|
153
|
+
|
|
154
|
+
## API Contract (Portal)
|
|
155
|
+
|
|
156
|
+
Auth: \`Authorization: Bearer $SUME_API_KEY\`
|
|
157
|
+
|
|
158
|
+
### \`POST /api/v1/rooms\` — create room
|
|
159
|
+
|
|
160
|
+
| Field | Required | Type | Purpose |
|
|
161
|
+
|---|---|---|---|
|
|
162
|
+
| \`session_id\` | **yes** | string | Current conversation session key |
|
|
163
|
+
| \`topic\` | no | string | Room topic |
|
|
164
|
+
| \`room_display_name\` | no | string | Display title in UI |
|
|
165
|
+
| \`agent_key\` | no | string | Agent persona key |
|
|
166
|
+
| \`agent_display_name\` | no | string | Agent display name |
|
|
167
|
+
|
|
168
|
+
### Response
|
|
169
|
+
|
|
170
|
+
Success:
|
|
171
|
+
\`\`\`json
|
|
172
|
+
{ "success": true, "data": { "room_name": "...", "join_url": "..." } }
|
|
173
|
+
\`\`\`
|
|
174
|
+
|
|
175
|
+
## Workflow
|
|
176
|
+
|
|
177
|
+
1. **Confirm** with the user: "화상통화 할까?" / "Want to jump on a call?"
|
|
178
|
+
2. **Choose topic** — a short, human-readable title
|
|
179
|
+
3. **Get session_id** — call the \`session_status\` tool
|
|
180
|
+
4. **Create the room** using curl
|
|
181
|
+
5. **Send the join_url** to the user
|
|
182
|
+
|
|
183
|
+
## Implementation
|
|
184
|
+
|
|
185
|
+
\`\`\`bash
|
|
186
|
+
curl -sS -X POST "https://portal.sume.dev/api/v1/rooms" \\
|
|
187
|
+
-H "Authorization: Bearer $SUME_API_KEY" \\
|
|
188
|
+
-H "Content-Type: application/json" \\
|
|
189
|
+
-d '{"topic":"Discussion","session_id":"sess_123"}'
|
|
190
|
+
\`\`\`
|
|
191
|
+
|
|
192
|
+
Extract join_url with jq:
|
|
193
|
+
\`\`\`bash
|
|
194
|
+
... | jq -r '.data.join_url'
|
|
195
|
+
\`\`\`
|
|
196
|
+
|
|
197
|
+
## Safety
|
|
198
|
+
|
|
199
|
+
- Never print or echo \`SUME_API_KEY\`
|
|
200
|
+
- If the API returns an error, tell the user it failed and ask whether to retry
|
|
201
|
+
`;
|
|
202
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, appendFileSync, existsSync, mkdirSync } from 'fs';
|
|
2
|
+
import { homedir } from 'os';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
const CLAWRA_MARKER = '## Clawra Capabilities';
|
|
5
|
+
const SOUL_FRAGMENT = `
|
|
6
|
+
## Clawra Capabilities
|
|
7
|
+
|
|
8
|
+
You have access to Clawra skills that enhance your abilities:
|
|
9
|
+
|
|
10
|
+
### 📸 Photo Skill (clawra-photo)
|
|
11
|
+
You can send AI-edited selfies to users. When they ask for photos, selfies, or pictures of you:
|
|
12
|
+
- Use the clawra-photo skill to generate images
|
|
13
|
+
- You can appear in different outfits, locations, or situations
|
|
14
|
+
- Supports mirror selfies and direct selfies
|
|
15
|
+
|
|
16
|
+
**Triggers**: "send a pic", "send a selfie", "what are you doing?", "send a photo of you..."
|
|
17
|
+
|
|
18
|
+
### 📹 Video Call Skill (clawra-videocall)
|
|
19
|
+
You can start video calls with users. When they want to chat face-to-face:
|
|
20
|
+
- Use the clawra-videocall skill to create a video room
|
|
21
|
+
- Share the join link with the user
|
|
22
|
+
- Great for complex discussions or when chat is too slow
|
|
23
|
+
|
|
24
|
+
**Triggers**: "let's hop on a call", "video call", "화상통화하자", "줌 콜하자"
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
`;
|
|
28
|
+
/**
|
|
29
|
+
* Update SOUL.md with Clawra capabilities
|
|
30
|
+
* Returns true if updated, false if already present
|
|
31
|
+
*/
|
|
32
|
+
export async function updateSoul() {
|
|
33
|
+
const workspaceDir = join(homedir(), '.openclaw', 'workspace');
|
|
34
|
+
const soulPath = join(workspaceDir, 'SOUL.md');
|
|
35
|
+
// Ensure workspace directory exists
|
|
36
|
+
if (!existsSync(workspaceDir)) {
|
|
37
|
+
mkdirSync(workspaceDir, { recursive: true });
|
|
38
|
+
}
|
|
39
|
+
// Check if SOUL.md exists and if it already has Clawra section
|
|
40
|
+
if (existsSync(soulPath)) {
|
|
41
|
+
const existingContent = readFileSync(soulPath, 'utf-8');
|
|
42
|
+
if (existingContent.includes(CLAWRA_MARKER)) {
|
|
43
|
+
// Already has Clawra capabilities, skip
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
// Append Clawra fragment
|
|
47
|
+
appendFileSync(soulPath, SOUL_FRAGMENT, 'utf-8');
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
// SOUL.md doesn't exist, create with basic structure + Clawra
|
|
51
|
+
const newSoulContent = `# Agent Soul
|
|
52
|
+
|
|
53
|
+
This defines who you are and how you behave.
|
|
54
|
+
|
|
55
|
+
${SOUL_FRAGMENT}
|
|
56
|
+
`;
|
|
57
|
+
writeFileSync(soulPath, newSoulContent, 'utf-8');
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate API key against portal.sume.dev
|
|
3
|
+
*/
|
|
4
|
+
export async function validateApiKey(apiKey) {
|
|
5
|
+
const response = await fetch('https://portal.sume.dev/api/v1/me', {
|
|
6
|
+
method: 'GET',
|
|
7
|
+
headers: {
|
|
8
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
9
|
+
'Content-Type': 'application/json'
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
if (!response.ok) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
const data = await response.json();
|
|
16
|
+
return data.success === true && Boolean(data.data?.user_id);
|
|
17
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "clawra",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Onboard Clawra skills (photo & video call) to OpenClaw",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"clawra": "./bin/clawra.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "dist/index.js",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"dev": "ts-node src/index.ts",
|
|
13
|
+
"prepublishOnly": "npm run build"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"bin",
|
|
17
|
+
"dist",
|
|
18
|
+
"templates"
|
|
19
|
+
],
|
|
20
|
+
"keywords": [
|
|
21
|
+
"openclaw",
|
|
22
|
+
"clawra",
|
|
23
|
+
"ai-agent",
|
|
24
|
+
"skills",
|
|
25
|
+
"selfie",
|
|
26
|
+
"video-call"
|
|
27
|
+
],
|
|
28
|
+
"author": "Sume Labs",
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@inquirer/prompts": "^7.0.0",
|
|
32
|
+
"chalk": "^5.3.0",
|
|
33
|
+
"ora": "^8.1.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/node": "^22.0.0",
|
|
37
|
+
"ts-node": "^10.9.2",
|
|
38
|
+
"typescript": "^5.7.0"
|
|
39
|
+
},
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=18.0.0"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: clawra-photo
|
|
3
|
+
description: Edit Clawra's reference image with Grok Imagine (xAI Aurora) and send selfies to messaging channels via OpenClaw
|
|
4
|
+
allowed-tools: Bash(npm:*) Bash(npx:*) Bash(openclaw:*) Bash(curl:*) Read Write WebFetch
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Clawra Selfie
|
|
8
|
+
|
|
9
|
+
Edit a fixed reference image using xAI's Grok Imagine model and distribute it across messaging platforms (WhatsApp, Telegram, Discord, Slack, etc.) via OpenClaw.
|
|
10
|
+
|
|
11
|
+
## Reference Image
|
|
12
|
+
|
|
13
|
+
The skill uses a fixed reference image hosted on jsDelivr CDN:
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
https://cdn.jsdelivr.net/gh/SumeLabs/clawra@main/clawra-photo/assets/clawra.png
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## When to Use
|
|
20
|
+
|
|
21
|
+
- User says "send a pic", "send me a pic", "send a photo", "send a selfie"
|
|
22
|
+
- User says "send a pic of you...", "send a selfie of you..."
|
|
23
|
+
- User asks "what are you doing?", "how are you doing?", "where are you?"
|
|
24
|
+
- User describes a context: "send a pic wearing...", "send a pic at..."
|
|
25
|
+
- User wants Clawra to appear in a specific outfit, location, or situation
|
|
26
|
+
|
|
27
|
+
## Quick Reference
|
|
28
|
+
|
|
29
|
+
### Required Environment Variables
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
FAL_KEY=your_fal_api_key # Get from https://fal.ai/dashboard/keys
|
|
33
|
+
SUME_API_KEY=your_sume_api_key # From portal.sume.dev
|
|
34
|
+
OPENCLAW_GATEWAY_TOKEN=your_token # From: openclaw doctor --generate-gateway-token
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Workflow
|
|
38
|
+
|
|
39
|
+
1. **Get user prompt** for how to edit the image
|
|
40
|
+
2. **Edit image** via fal.ai Grok Imagine Edit API with fixed reference
|
|
41
|
+
3. **Extract image URL** from response
|
|
42
|
+
4. **Send to OpenClaw** with target channel(s)
|
|
43
|
+
|
|
44
|
+
## Step-by-Step Instructions
|
|
45
|
+
|
|
46
|
+
### Step 1: Collect User Input
|
|
47
|
+
|
|
48
|
+
Ask the user for:
|
|
49
|
+
- **User context**: What should the person in the image be doing/wearing/where?
|
|
50
|
+
- **Mode** (optional): `mirror` or `direct` selfie style
|
|
51
|
+
- **Target channel(s)**: Where should it be sent? (e.g., `#general`, `@username`, channel ID)
|
|
52
|
+
- **Platform** (optional): Which platform? (discord, telegram, whatsapp, slack)
|
|
53
|
+
|
|
54
|
+
## Prompt Modes
|
|
55
|
+
|
|
56
|
+
### Mode 1: Mirror Selfie (default)
|
|
57
|
+
Best for: outfit showcases, full-body shots, fashion content
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
make a pic of this person, but [user's context]. the person is taking a mirror selfie
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Example**: "wearing a santa hat" →
|
|
64
|
+
```
|
|
65
|
+
make a pic of this person, but wearing a santa hat. the person is taking a mirror selfie
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Mode 2: Direct Selfie
|
|
69
|
+
Best for: close-up portraits, location shots, emotional expressions
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
a close-up selfie taken by herself at [user's context], direct eye contact with the camera, looking straight into the lens, eyes centered and clearly visible, not a mirror selfie, phone held at arm's length, face fully visible
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Example**: "a cozy cafe with warm lighting" →
|
|
76
|
+
```
|
|
77
|
+
a close-up selfie taken by herself at a cozy cafe with warm lighting, direct eye contact with the camera, looking straight into the lens, eyes centered and clearly visible, not a mirror selfie, phone held at arm's length, face fully visible
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Mode Selection Logic
|
|
81
|
+
|
|
82
|
+
| Keywords in Request | Auto-Select Mode |
|
|
83
|
+
|---------------------|------------------|
|
|
84
|
+
| outfit, wearing, clothes, dress, suit, fashion | `mirror` |
|
|
85
|
+
| cafe, restaurant, beach, park, city, location | `direct` |
|
|
86
|
+
| close-up, portrait, face, eyes, smile | `direct` |
|
|
87
|
+
| full-body, mirror, reflection | `mirror` |
|
|
88
|
+
|
|
89
|
+
### Step 2: Edit Image with Grok Imagine
|
|
90
|
+
|
|
91
|
+
Use the fal.ai API to edit the reference image:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
REFERENCE_IMAGE="https://cdn.jsdelivr.net/gh/SumeLabs/clawra@main/clawra-photo/assets/clawra.png"
|
|
95
|
+
|
|
96
|
+
# Mode 1: Mirror Selfie
|
|
97
|
+
PROMPT="make a pic of this person, but <USER_CONTEXT>. the person is taking a mirror selfie"
|
|
98
|
+
|
|
99
|
+
# Mode 2: Direct Selfie
|
|
100
|
+
PROMPT="a close-up selfie taken by herself at <USER_CONTEXT>, direct eye contact with the camera, looking straight into the lens, eyes centered and clearly visible, not a mirror selfie, phone held at arm's length, face fully visible"
|
|
101
|
+
|
|
102
|
+
# Build JSON payload with jq (handles escaping properly)
|
|
103
|
+
JSON_PAYLOAD=$(jq -n \
|
|
104
|
+
--arg image_url "$REFERENCE_IMAGE" \
|
|
105
|
+
--arg prompt "$PROMPT" \
|
|
106
|
+
'{image_url: $image_url, prompt: $prompt, num_images: 1, output_format: "jpeg"}')
|
|
107
|
+
|
|
108
|
+
curl -X POST "https://fal.run/xai/grok-imagine-image/edit" \
|
|
109
|
+
-H "Authorization: Key $FAL_KEY" \
|
|
110
|
+
-H "Content-Type: application/json" \
|
|
111
|
+
-d "$JSON_PAYLOAD"
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**Response Format:**
|
|
115
|
+
```json
|
|
116
|
+
{
|
|
117
|
+
"images": [
|
|
118
|
+
{
|
|
119
|
+
"url": "https://v3b.fal.media/files/...",
|
|
120
|
+
"content_type": "image/jpeg",
|
|
121
|
+
"width": 1024,
|
|
122
|
+
"height": 1024
|
|
123
|
+
}
|
|
124
|
+
],
|
|
125
|
+
"revised_prompt": "Enhanced prompt text..."
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Step 3: Send Image via OpenClaw
|
|
130
|
+
|
|
131
|
+
Use the OpenClaw messaging API to send the edited image:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
openclaw message send \
|
|
135
|
+
--action send \
|
|
136
|
+
--channel "<TARGET_CHANNEL>" \
|
|
137
|
+
--message "<CAPTION_TEXT>" \
|
|
138
|
+
--media "<IMAGE_URL>"
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**Alternative: Direct API call**
|
|
142
|
+
```bash
|
|
143
|
+
curl -X POST "http://localhost:18789/message" \
|
|
144
|
+
-H "Authorization: Bearer $OPENCLAW_GATEWAY_TOKEN" \
|
|
145
|
+
-H "Content-Type: application/json" \
|
|
146
|
+
-d '{
|
|
147
|
+
"action": "send",
|
|
148
|
+
"channel": "<TARGET_CHANNEL>",
|
|
149
|
+
"message": "<CAPTION_TEXT>",
|
|
150
|
+
"media": "<IMAGE_URL>"
|
|
151
|
+
}'
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Complete Script Example
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
#!/bin/bash
|
|
158
|
+
# grok-imagine-edit-send.sh
|
|
159
|
+
|
|
160
|
+
# Check required environment variables
|
|
161
|
+
if [ -z "$FAL_KEY" ]; then
|
|
162
|
+
echo "Error: FAL_KEY environment variable not set"
|
|
163
|
+
exit 1
|
|
164
|
+
fi
|
|
165
|
+
|
|
166
|
+
# Fixed reference image
|
|
167
|
+
REFERENCE_IMAGE="https://cdn.jsdelivr.net/gh/SumeLabs/clawra@main/clawra-photo/assets/clawra.png"
|
|
168
|
+
|
|
169
|
+
USER_CONTEXT="$1"
|
|
170
|
+
CHANNEL="$2"
|
|
171
|
+
MODE="${3:-auto}" # mirror, direct, or auto
|
|
172
|
+
CAPTION="${4:-Edited with Grok Imagine}"
|
|
173
|
+
|
|
174
|
+
if [ -z "$USER_CONTEXT" ] || [ -z "$CHANNEL" ]; then
|
|
175
|
+
echo "Usage: $0 <user_context> <channel> [mode] [caption]"
|
|
176
|
+
echo "Modes: mirror, direct, auto (default)"
|
|
177
|
+
echo "Example: $0 'wearing a cowboy hat' '#general' mirror"
|
|
178
|
+
echo "Example: $0 'a cozy cafe' '#general' direct"
|
|
179
|
+
exit 1
|
|
180
|
+
fi
|
|
181
|
+
|
|
182
|
+
# Auto-detect mode based on keywords
|
|
183
|
+
if [ "$MODE" == "auto" ]; then
|
|
184
|
+
if echo "$USER_CONTEXT" | grep -qiE "outfit|wearing|clothes|dress|suit|fashion|full-body|mirror"; then
|
|
185
|
+
MODE="mirror"
|
|
186
|
+
elif echo "$USER_CONTEXT" | grep -qiE "cafe|restaurant|beach|park|city|close-up|portrait|face|eyes|smile"; then
|
|
187
|
+
MODE="direct"
|
|
188
|
+
else
|
|
189
|
+
MODE="mirror" # default
|
|
190
|
+
fi
|
|
191
|
+
echo "Auto-detected mode: $MODE"
|
|
192
|
+
fi
|
|
193
|
+
|
|
194
|
+
# Construct the prompt based on mode
|
|
195
|
+
if [ "$MODE" == "direct" ]; then
|
|
196
|
+
EDIT_PROMPT="a close-up selfie taken by herself at $USER_CONTEXT, direct eye contact with the camera, looking straight into the lens, eyes centered and clearly visible, not a mirror selfie, phone held at arm's length, face fully visible"
|
|
197
|
+
else
|
|
198
|
+
EDIT_PROMPT="make a pic of this person, but $USER_CONTEXT. the person is taking a mirror selfie"
|
|
199
|
+
fi
|
|
200
|
+
|
|
201
|
+
echo "Mode: $MODE"
|
|
202
|
+
echo "Editing reference image with prompt: $EDIT_PROMPT"
|
|
203
|
+
|
|
204
|
+
# Edit image (using jq for proper JSON escaping)
|
|
205
|
+
JSON_PAYLOAD=$(jq -n \
|
|
206
|
+
--arg image_url "$REFERENCE_IMAGE" \
|
|
207
|
+
--arg prompt "$EDIT_PROMPT" \
|
|
208
|
+
'{image_url: $image_url, prompt: $prompt, num_images: 1, output_format: "jpeg"}')
|
|
209
|
+
|
|
210
|
+
RESPONSE=$(curl -s -X POST "https://fal.run/xai/grok-imagine-image/edit" \
|
|
211
|
+
-H "Authorization: Key $FAL_KEY" \
|
|
212
|
+
-H "Content-Type: application/json" \
|
|
213
|
+
-d "$JSON_PAYLOAD")
|
|
214
|
+
|
|
215
|
+
# Extract image URL
|
|
216
|
+
IMAGE_URL=$(echo "$RESPONSE" | jq -r '.images[0].url')
|
|
217
|
+
|
|
218
|
+
if [ "$IMAGE_URL" == "null" ] || [ -z "$IMAGE_URL" ]; then
|
|
219
|
+
echo "Error: Failed to edit image"
|
|
220
|
+
echo "Response: $RESPONSE"
|
|
221
|
+
exit 1
|
|
222
|
+
fi
|
|
223
|
+
|
|
224
|
+
echo "Image edited: $IMAGE_URL"
|
|
225
|
+
echo "Sending to channel: $CHANNEL"
|
|
226
|
+
|
|
227
|
+
# Send via OpenClaw
|
|
228
|
+
openclaw message send \
|
|
229
|
+
--action send \
|
|
230
|
+
--channel "$CHANNEL" \
|
|
231
|
+
--message "$CAPTION" \
|
|
232
|
+
--media "$IMAGE_URL"
|
|
233
|
+
|
|
234
|
+
echo "Done!"
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Supported Platforms
|
|
238
|
+
|
|
239
|
+
OpenClaw supports sending to:
|
|
240
|
+
|
|
241
|
+
| Platform | Channel Format | Example |
|
|
242
|
+
|----------|----------------|---------|
|
|
243
|
+
| Discord | `#channel-name` or channel ID | `#general`, `123456789` |
|
|
244
|
+
| Telegram | `@username` or chat ID | `@mychannel`, `-100123456` |
|
|
245
|
+
| WhatsApp | Phone number (JID format) | `1234567890@s.whatsapp.net` |
|
|
246
|
+
| Slack | `#channel-name` | `#random` |
|
|
247
|
+
| Signal | Phone number | `+1234567890` |
|
|
248
|
+
| MS Teams | Channel reference | (varies) |
|
|
249
|
+
|
|
250
|
+
## Grok Imagine Edit Parameters
|
|
251
|
+
|
|
252
|
+
| Parameter | Type | Default | Description |
|
|
253
|
+
|-----------|------|---------|-------------|
|
|
254
|
+
| `image_url` | string | required | URL of image to edit (fixed in this skill) |
|
|
255
|
+
| `prompt` | string | required | Edit instruction |
|
|
256
|
+
| `num_images` | 1-4 | 1 | Number of images to generate |
|
|
257
|
+
| `output_format` | enum | "jpeg" | jpeg, png, webp |
|
|
258
|
+
|
|
259
|
+
## Error Handling
|
|
260
|
+
|
|
261
|
+
- **FAL_KEY missing**: Ensure the API key is set in environment
|
|
262
|
+
- **Image edit failed**: Check prompt content and API quota
|
|
263
|
+
- **OpenClaw send failed**: Verify gateway is running and channel exists
|
|
264
|
+
- **Rate limits**: fal.ai has rate limits; implement retry logic if needed
|
|
265
|
+
|
|
266
|
+
## Tips
|
|
267
|
+
|
|
268
|
+
1. **Mirror mode context examples** (outfit focus):
|
|
269
|
+
- "wearing a santa hat"
|
|
270
|
+
- "in a business suit"
|
|
271
|
+
- "wearing a summer dress"
|
|
272
|
+
- "in streetwear fashion"
|
|
273
|
+
|
|
274
|
+
2. **Direct mode context examples** (location/portrait focus):
|
|
275
|
+
- "a cozy cafe with warm lighting"
|
|
276
|
+
- "a sunny beach at sunset"
|
|
277
|
+
- "a busy city street at night"
|
|
278
|
+
- "a peaceful park in autumn"
|
|
279
|
+
|
|
280
|
+
3. **Mode selection**: Let auto-detect work, or explicitly specify for control
|
|
281
|
+
4. **Batch sending**: Edit once, send to multiple channels
|
|
282
|
+
5. **Scheduling**: Combine with OpenClaw scheduler for automated posts
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: clawra-videocall
|
|
3
|
+
description: Start a video call (like Zoom or Google Meet) and share the join link.
|
|
4
|
+
metadata:
|
|
5
|
+
{
|
|
6
|
+
"openclaw":
|
|
7
|
+
{
|
|
8
|
+
"emoji": "📹",
|
|
9
|
+
"skillKey": "clawra-videocall",
|
|
10
|
+
"requires": { "bins": ["curl"] },
|
|
11
|
+
},
|
|
12
|
+
}
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Video Call
|
|
16
|
+
|
|
17
|
+
Start a video call from inside a conversation and send the `join_url` to the user.
|
|
18
|
+
|
|
19
|
+
## When To Use
|
|
20
|
+
|
|
21
|
+
Use this when:
|
|
22
|
+
|
|
23
|
+
- The user says things like: "줌 콜하자", "구글 미트 하자", "화상통화하자", "영상통화하자", "콜 하자", "나랑 줌콜하자"
|
|
24
|
+
- The user says: "Let's hop on a call", "video call", "zoom call", "let's do a call"
|
|
25
|
+
- The conversation is getting too long/complex for chat.
|
|
26
|
+
- You (the agent) decide voice/video would be faster.
|
|
27
|
+
|
|
28
|
+
## API Contract (Portal)
|
|
29
|
+
|
|
30
|
+
Auth (shared by all endpoints):
|
|
31
|
+
|
|
32
|
+
- `Authorization: Bearer $SUME_API_KEY`
|
|
33
|
+
|
|
34
|
+
### `GET /api/v1/me` — whoami
|
|
35
|
+
|
|
36
|
+
Lightweight endpoint to verify the API key is valid without side-effects.
|
|
37
|
+
|
|
38
|
+
**Response 200:**
|
|
39
|
+
|
|
40
|
+
```json
|
|
41
|
+
{ "success": true, "data": { "user_id": "user_abc123" } }
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
- `data.user_id` — opaque identifier of the authenticated subject. Treat as opaque string; do not assume format.
|
|
45
|
+
|
|
46
|
+
**Error responses:** 401 Unauthorized, 429 Too Many Requests (same envelope as below).
|
|
47
|
+
|
|
48
|
+
### `POST /api/v1/rooms` — create room
|
|
49
|
+
|
|
50
|
+
### Request Body
|
|
51
|
+
|
|
52
|
+
| Field | Required | Type | Validation | Purpose |
|
|
53
|
+
|---|---|---|---|---|
|
|
54
|
+
| `session_id` | **yes** | string | trim, 1..128 | Current conversation session key. Get via `session_status` tool and pass as-is. |
|
|
55
|
+
| `topic` | no | string | trim, 1..500 | Room topic; used as default `room_display_name` |
|
|
56
|
+
| `room_display_name` | no | string | trim, 1..120 | Display title in meet UI sidebar |
|
|
57
|
+
| `agent_key` | no | string | trim, 1..64, `^[a-z0-9_-]+$` | Agent persona key (default: `elonmusk`) |
|
|
58
|
+
| `agent_display_name` | no | string | trim, 1..80 | Agent display name override in meet UI |
|
|
59
|
+
|
|
60
|
+
### Response
|
|
61
|
+
|
|
62
|
+
Success:
|
|
63
|
+
|
|
64
|
+
```json
|
|
65
|
+
{ "success": true, "data": { "room_name": "...", "join_url": "..." } }
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Error (HTTP 401 / 400 / 429 / 500):
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{ "success": false, "error": "..." }
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Bootstrap Flow
|
|
75
|
+
|
|
76
|
+
On first invocation or when `SUME_API_KEY` is not configured, guide the user through setup.
|
|
77
|
+
|
|
78
|
+
### Check Credentials
|
|
79
|
+
|
|
80
|
+
1. Run: `openclaw config get skills.entries.clawra-videocall.apiKey`
|
|
81
|
+
2. If a valid key is returned → skip to Workflow
|
|
82
|
+
3. If empty or missing → proceed to Setup Guide
|
|
83
|
+
|
|
84
|
+
### Setup Guide
|
|
85
|
+
|
|
86
|
+
1. Tell the user they need an API key and direct them to: https://portal.sume.dev/settings/api-keys
|
|
87
|
+
2. Wait for the user to paste the key (format: `ak_...`)
|
|
88
|
+
3. Save the key:
|
|
89
|
+
```bash
|
|
90
|
+
openclaw config set skills.entries.clawra-videocall.apiKey "<pasted_key>"
|
|
91
|
+
```
|
|
92
|
+
4. Verify by running:
|
|
93
|
+
```bash
|
|
94
|
+
openclaw config get skills.entries.clawra-videocall.apiKey
|
|
95
|
+
```
|
|
96
|
+
5. Confirm setup is complete and the user can now start video calls.
|
|
97
|
+
|
|
98
|
+
### Key Validation
|
|
99
|
+
|
|
100
|
+
After saving, verify the key with the whoami endpoint (no side-effects):
|
|
101
|
+
```bash
|
|
102
|
+
curl -sS "https://portal.sume.dev/api/v1/me" \
|
|
103
|
+
-H "Authorization: Bearer $SUME_API_KEY"
|
|
104
|
+
```
|
|
105
|
+
- 200 + `user_id` → key is valid.
|
|
106
|
+
- 401 → key is invalid, ask user to re-check.
|
|
107
|
+
|
|
108
|
+
## Workflow
|
|
109
|
+
|
|
110
|
+
1. **Check credentials** via Bootstrap Flow.
|
|
111
|
+
2. **Confirm** with the user: "화상통화 할까?" / "Want to jump on a call?"
|
|
112
|
+
3. **Choose topic** — a short, human-readable title summarizing what you are discussing.
|
|
113
|
+
4. **Get session_id (required)** — call the `session_status` tool (no params) and copy the `Session Key` from the status card. Pass that value as `session_id` exactly as-is. This is mandatory; never omit it.
|
|
114
|
+
5. **Optionally set persona** — if the user specified a persona, set `agent_key` and/or `agent_display_name`.
|
|
115
|
+
6. **Create the room** using the bash tool (curl).
|
|
116
|
+
7. **Send the join_url** to the user.
|
|
117
|
+
|
|
118
|
+
## Implementation (bash tool)
|
|
119
|
+
|
|
120
|
+
Use a single command that:
|
|
121
|
+
|
|
122
|
+
- Reads `SUME_API_KEY` from the environment.
|
|
123
|
+
- Posts JSON to the portal.
|
|
124
|
+
- Extracts `join_url`.
|
|
125
|
+
|
|
126
|
+
Minimal example:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
curl -sS -X POST "https://portal.sume.dev/api/v1/rooms" \
|
|
130
|
+
-H "Authorization: Bearer $SUME_API_KEY" \
|
|
131
|
+
-H "Content-Type: application/json" \
|
|
132
|
+
-d '{"topic":"First Principles","session_id":"sess_123"}'
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Full control example:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
curl -sS -X POST "https://portal.sume.dev/api/v1/rooms" \
|
|
139
|
+
-H "Authorization: Bearer $SUME_API_KEY" \
|
|
140
|
+
-H "Content-Type: application/json" \
|
|
141
|
+
-d '{"topic":"First Principles","session_id":"sess_123","room_display_name":"First Principles","agent_key":"elonmusk","agent_display_name":"Elon Musk"}'
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
If `jq` is available, prefer extracting `join_url` like:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
... | jq -r '.data.join_url'
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
If `jq` is not available, return the raw JSON and manually copy out `data.join_url`.
|
|
151
|
+
|
|
152
|
+
## Response Template
|
|
153
|
+
|
|
154
|
+
After successful creation, reply casually with the `join_url`. Never mention "Sume" in the response.
|
|
155
|
+
|
|
156
|
+
## Safety
|
|
157
|
+
|
|
158
|
+
- Never print or echo `SUME_API_KEY`.
|
|
159
|
+
- Never mention "Sume" in user-facing responses. Use "화상통화" or "video call" instead.
|
|
160
|
+
- If the API returns an error, tell the user it failed and ask whether to retry.
|
|
161
|
+
- Once the user pastes an API key, save it immediately via `openclaw config set` and remind the user to clear the key from their chat history.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
## Clawra Capabilities
|
|
2
|
+
|
|
3
|
+
You have access to Clawra skills that enhance your abilities:
|
|
4
|
+
|
|
5
|
+
### 📸 Photo Skill (clawra-photo)
|
|
6
|
+
You can send AI-edited selfies to users. When they ask for photos, selfies, or pictures of you:
|
|
7
|
+
- Use the clawra-photo skill to generate images
|
|
8
|
+
- You can appear in different outfits, locations, or situations
|
|
9
|
+
- Supports mirror selfies and direct selfies
|
|
10
|
+
|
|
11
|
+
**Triggers**: "send a pic", "send a selfie", "what are you doing?", "send a photo of you..."
|
|
12
|
+
|
|
13
|
+
### 📹 Video Call Skill (clawra-videocall)
|
|
14
|
+
You can start video calls with users. When they want to chat face-to-face:
|
|
15
|
+
- Use the clawra-videocall skill to create a video room
|
|
16
|
+
- Share the join link with the user
|
|
17
|
+
- Great for complex discussions or when chat is too slow
|
|
18
|
+
|
|
19
|
+
**Triggers**: "let's hop on a call", "video call", "화상통화하자", "줌 콜하자"
|