callme-openclaw-plugin 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.
Files changed (5) hide show
  1. package/README.md +196 -0
  2. package/index.js +255 -0
  3. package/install.sh +65 -0
  4. package/package.json +62 -0
  5. package/setup.js +138 -0
package/README.md ADDED
@@ -0,0 +1,196 @@
1
+ # šŸ“ž callme.ai OpenClaw Plugin
2
+
3
+ **Hands-free voice calls with your AI assistant - seamlessly integrated into OpenClaw**
4
+
5
+ Talk naturally with Commodus using voice. No buttons, no typing. Just speak.
6
+
7
+ ---
8
+
9
+ ## šŸš€ Quick Start
10
+
11
+ ### 1. Get Your API Key
12
+
13
+ Sign up at [callme.ai](https://callme.ai) and get your API key from the dashboard.
14
+
15
+ ### 2. Install Plugin
16
+
17
+ ```bash
18
+ cd ~/.openclaw/plugins
19
+ git clone https://github.com/Benedict-VC/openclaw-voice.git callme
20
+ cd callme/plugin-callme
21
+ npm install
22
+ ```
23
+
24
+ Or via npm (when published):
25
+ ```bash
26
+ npm install -g @callme/openclaw-plugin
27
+ ```
28
+
29
+ ### 3. Configure OpenClaw
30
+
31
+ Add to your `~/.openclaw/config.yaml`:
32
+
33
+ ```yaml
34
+ plugins:
35
+ callme:
36
+ enabled: true
37
+ apiKey: "callme_your_key_here" # Get from https://callme.ai/dashboard
38
+ defaultVoice: "nova" # optional: alloy, echo, fable, onyx, nova, shimmer
39
+ language: "en" # optional: en, de, es, fr
40
+ ```
41
+
42
+ ### 4. Reload OpenClaw
43
+
44
+ ```bash
45
+ openclaw reload
46
+ ```
47
+
48
+ ### 5. Start Calling!
49
+
50
+ ```bash
51
+ /call # Start a voice call
52
+ /call 5 # Call with 5-minute time limit
53
+ /voice-usage # Check your usage stats
54
+ /voice-keys # List your API keys
55
+ ```
56
+
57
+ ---
58
+
59
+ ## šŸ“‹ Commands
60
+
61
+ | Command | Description |
62
+ |---------|-------------|
63
+ | `/call [minutes]` | Start a hands-free voice call |
64
+ | `/voice-setup` | Show setup instructions |
65
+ | `/voice-usage` | Display usage stats (minutes, cost, limits) |
66
+ | `/voice-keys` | List your API keys |
67
+
68
+ ---
69
+
70
+ ## āš™ļø Configuration Options
71
+
72
+ ```yaml
73
+ plugins:
74
+ callme:
75
+ # Required
76
+ apiKey: "callme_xxx" # Your callme.ai API key
77
+
78
+ # Optional
79
+ defaultVoice: "nova" # Voice: alloy, echo, fable, onyx, nova, shimmer
80
+ language: "en" # Language: en, de, es, fr
81
+ ```
82
+
83
+ ---
84
+
85
+ ## šŸ’° Pricing
86
+
87
+ - **Free:** 50 minutes/month
88
+ - **Pro:** 500 minutes/month - $19/month
89
+ - **Enterprise:** Unlimited - $99/month
90
+
91
+ View plans: [callme.ai/#pricing](https://callme.ai/#pricing)
92
+
93
+ ---
94
+
95
+ ## šŸ”§ How It Works
96
+
97
+ ```
98
+ You speak → callme.ai (STT) → OpenClaw Session → Claude → callme.ai (TTS) → You hear
99
+ ```
100
+
101
+ 1. **Voice Activity Detection** - Automatically detects when you stop speaking
102
+ 2. **Real-time Processing** - Sub-2-second response time
103
+ 3. **Seamless Integration** - Uses your active OpenClaw session
104
+ 4. **Usage Tracking** - Automatically tracked per API key
105
+
106
+ ---
107
+
108
+ ## šŸ› ļø Development
109
+
110
+ ### Local Testing
111
+
112
+ ```bash
113
+ cd plugin-callme
114
+ npm install
115
+ npm test
116
+ ```
117
+
118
+ ### Environment Variables
119
+
120
+ ```bash
121
+ export CALLME_API_BASE="http://localhost:3000"
122
+ export CALLME_WS_BASE="ws://localhost:3000"
123
+ ```
124
+
125
+ ---
126
+
127
+ ## šŸ“– Examples
128
+
129
+ ### Basic Call
130
+ ```bash
131
+ /call
132
+ # Speaks naturally, conversation ends when you stop talking
133
+ ```
134
+
135
+ ### Time-Limited Call
136
+ ```bash
137
+ /call 10
138
+ # Automatic cutoff after 10 minutes
139
+ ```
140
+
141
+ ### Check Usage
142
+ ```bash
143
+ /voice-usage
144
+ # Shows: calls, minutes used, remaining quota, cost
145
+ ```
146
+
147
+ ### Manage Keys
148
+ ```bash
149
+ /voice-keys
150
+ # Lists all your API keys with status and expiry
151
+ ```
152
+
153
+ ---
154
+
155
+ ## šŸ”’ Security
156
+
157
+ - API keys are stored securely in OpenClaw config
158
+ - Never log or expose keys in plain text
159
+ - All connections use TLS encryption
160
+ - Keys can be revoked anytime at dashboard
161
+
162
+ ---
163
+
164
+ ## šŸ› Troubleshooting
165
+
166
+ ### "API key not configured"
167
+ ```bash
168
+ /voice-setup
169
+ # Follow the setup instructions
170
+ ```
171
+
172
+ ### "Monthly limit reached"
173
+ Check usage:
174
+ ```bash
175
+ /voice-usage
176
+ ```
177
+ Upgrade plan at: https://callme.ai/dashboard
178
+
179
+ ### "WebSocket connection failed"
180
+ 1. Check your internet connection
181
+ 2. Verify API key is valid
182
+ 3. Check callme.ai status page
183
+
184
+ ---
185
+
186
+ ## šŸ“š Documentation
187
+
188
+ - **API Reference:** https://docs.callme.ai/api
189
+ - **OpenClaw Docs:** https://docs.openclaw.ai
190
+ - **Support:** https://discord.com/invite/clawd
191
+
192
+ ---
193
+
194
+ ## šŸ“„ License
195
+
196
+ MIT Ā© callme.ai
package/index.js ADDED
@@ -0,0 +1,255 @@
1
+ /**
2
+ * callme.ai OpenClaw Plugin
3
+ * Hands-free voice calls with your AI assistant
4
+ */
5
+
6
+ const WebSocket = require('ws');
7
+ const fetch = require('node-fetch');
8
+
9
+ const API_BASE = process.env.CALLME_API_BASE || 'https://callme.ai/api';
10
+ const WS_BASE = process.env.CALLME_WS_BASE || 'wss://callme.ai/ws';
11
+
12
+ class CallmePlugin {
13
+ constructor(config, openclaw) {
14
+ this.config = config;
15
+ this.openclaw = openclaw;
16
+ this.apiKey = config.apiKey;
17
+
18
+ if (!this.apiKey) {
19
+ throw new Error('callme.ai API key not configured. Run /voice-setup');
20
+ }
21
+ }
22
+
23
+ /**
24
+ * Start a voice call
25
+ */
26
+ async startCall(duration = null) {
27
+ if (!this.apiKey) {
28
+ return {
29
+ error: true,
30
+ message: 'āŒ API key not configured. Get yours at https://callme.ai/dashboard'
31
+ };
32
+ }
33
+
34
+ try {
35
+ // Verify API key & get user info
36
+ const userResponse = await fetch(`${API_BASE}/auth/me`, {
37
+ headers: {
38
+ 'Authorization': `Bearer ${this.apiKey}`
39
+ }
40
+ });
41
+
42
+ if (!userResponse.ok) {
43
+ throw new Error('Invalid API key');
44
+ }
45
+
46
+ const userData = await userResponse.json();
47
+ const user = userData.data.user;
48
+
49
+ // Check usage limits
50
+ const usageResponse = await fetch(`${API_BASE}/usage/current`, {
51
+ headers: {
52
+ 'Authorization': `Bearer ${this.apiKey}`
53
+ }
54
+ });
55
+
56
+ const usageData = await usageResponse.json();
57
+ const usage = usageData.data;
58
+
59
+ if (!usage.withinLimit) {
60
+ return {
61
+ error: true,
62
+ message: `āŒ Monthly limit reached (${usage.limit} minutes). Upgrade at https://callme.ai/dashboard`
63
+ };
64
+ }
65
+
66
+ // Connect to WebSocket
67
+ const ws = new WebSocket(WS_BASE, {
68
+ headers: {
69
+ 'Authorization': `Bearer ${this.apiKey}`
70
+ }
71
+ });
72
+
73
+ return new Promise((resolve, reject) => {
74
+ ws.on('open', () => {
75
+ ws.send(JSON.stringify({
76
+ type: 'start_call',
77
+ voice: this.config.defaultVoice || 'nova',
78
+ language: this.config.language || 'en',
79
+ duration: duration
80
+ }));
81
+
82
+ resolve({
83
+ success: true,
84
+ message: 'šŸ“ž Call started! Speak naturally, I\'m listening...',
85
+ ws: ws,
86
+ user: user,
87
+ usage: usage
88
+ });
89
+ });
90
+
91
+ ws.on('error', (error) => {
92
+ reject(new Error(`WebSocket error: ${error.message}`));
93
+ });
94
+ });
95
+
96
+ } catch (error) {
97
+ return {
98
+ error: true,
99
+ message: `āŒ Failed to start call: ${error.message}`
100
+ };
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Get usage stats
106
+ */
107
+ async getUsage() {
108
+ try {
109
+ const response = await fetch(`${API_BASE}/usage/current`, {
110
+ headers: {
111
+ 'Authorization': `Bearer ${this.apiKey}`
112
+ }
113
+ });
114
+
115
+ if (!response.ok) {
116
+ throw new Error('Failed to fetch usage');
117
+ }
118
+
119
+ const data = await response.json();
120
+ return data.data;
121
+
122
+ } catch (error) {
123
+ throw new Error(`Failed to get usage: ${error.message}`);
124
+ }
125
+ }
126
+
127
+ /**
128
+ * List API keys
129
+ */
130
+ async listKeys() {
131
+ try {
132
+ const response = await fetch(`${API_BASE}/keys`, {
133
+ headers: {
134
+ 'Authorization': `Bearer ${this.apiKey}`
135
+ }
136
+ });
137
+
138
+ if (!response.ok) {
139
+ throw new Error('Failed to fetch keys');
140
+ }
141
+
142
+ const data = await response.json();
143
+ return data.data.keys;
144
+
145
+ } catch (error) {
146
+ throw new Error(`Failed to list keys: ${error.message}`);
147
+ }
148
+ }
149
+ }
150
+
151
+ /**
152
+ * Plugin initialization
153
+ */
154
+ module.exports = {
155
+ name: '@callme/openclaw-plugin',
156
+ version: '1.0.0',
157
+
158
+ /**
159
+ * Initialize plugin
160
+ */
161
+ async init(config, openclaw) {
162
+ const plugin = new CallmePlugin(config, openclaw);
163
+
164
+ // Register /call command
165
+ openclaw.registerCommand('call', async (args) => {
166
+ const duration = args[0] ? parseInt(args[0]) : null;
167
+ const result = await plugin.startCall(duration);
168
+
169
+ if (result.error) {
170
+ return result.message;
171
+ }
172
+
173
+ return `${result.message}\n\nšŸ“Š Usage: ${result.usage.minutesUsed.toFixed(1)}/${result.usage.limit} min used this month`;
174
+ });
175
+
176
+ // Register /voice-setup command
177
+ openclaw.registerCommand('voice-setup', async () => {
178
+ return `šŸ”§ **Setup callme.ai Voice**
179
+
180
+ 1. Get your API key at: https://callme.ai/dashboard
181
+ 2. Add it to your OpenClaw config:
182
+
183
+ \`\`\`yaml
184
+ plugins:
185
+ callme:
186
+ apiKey: "callme_your_key_here"
187
+ defaultVoice: "nova" # optional
188
+ language: "en" # optional
189
+ \`\`\`
190
+
191
+ 3. Reload OpenClaw: \`openclaw reload\`
192
+ 4. Start calling: \`/call\`
193
+
194
+ Need help? Check the docs: https://docs.callme.ai/openclaw`;
195
+ });
196
+
197
+ // Register /voice-usage command
198
+ openclaw.registerCommand('voice-usage', async () => {
199
+ try {
200
+ const usage = await plugin.getUsage();
201
+
202
+ return `šŸ“Š **callme.ai Usage**
203
+
204
+ **This Month:**
205
+ • Calls: ${usage.calls}
206
+ • Minutes: ${usage.minutesUsed.toFixed(1)} / ${usage.limit}
207
+ • Cost: $${usage.cost.toFixed(2)}
208
+ • Remaining: ${usage.remaining.toFixed(1)} min
209
+
210
+ **Plan:** ${usage.tier}
211
+
212
+ Upgrade or view details: https://callme.ai/dashboard`;
213
+ } catch (error) {
214
+ return `āŒ ${error.message}`;
215
+ }
216
+ });
217
+
218
+ // Register /voice-keys command
219
+ openclaw.registerCommand('voice-keys', async () => {
220
+ try {
221
+ const keys = await plugin.listKeys();
222
+
223
+ if (keys.length === 0) {
224
+ return `šŸ”‘ **API Keys**
225
+
226
+ No keys found. Create one at: https://callme.ai/dashboard`;
227
+ }
228
+
229
+ const keysList = keys.map(key => {
230
+ const expiry = key.expiresAt
231
+ ? new Date(key.expiresAt).toLocaleDateString()
232
+ : 'Never';
233
+ const status = key.isExpired ? 'āŒ Expired' : 'āœ… Active';
234
+ const lastUsed = key.lastUsedAt
235
+ ? new Date(key.lastUsedAt).toLocaleDateString()
236
+ : 'Never';
237
+
238
+ return `• **${key.name}**
239
+ ${key.keyPreview}
240
+ ${status} Ā· Expires: ${expiry} Ā· Last used: ${lastUsed}`;
241
+ }).join('\n\n');
242
+
243
+ return `šŸ”‘ **API Keys**
244
+
245
+ ${keysList}
246
+
247
+ Manage keys: https://callme.ai/dashboard`;
248
+ } catch (error) {
249
+ return `āŒ ${error.message}`;
250
+ }
251
+ });
252
+
253
+ return plugin;
254
+ }
255
+ };
package/install.sh ADDED
@@ -0,0 +1,65 @@
1
+ #!/bin/bash
2
+ # callme.ai One-Command Installer for OpenClaw
3
+ # Usage: curl -fsSL https://callme.ai/install.sh | bash
4
+
5
+ set -e
6
+
7
+ PLUGIN_DIR="$HOME/.openclaw/plugins/callme"
8
+ CONFIG_FILE="$HOME/.openclaw/config.yaml"
9
+ PLUGIN_URL="https://raw.githubusercontent.com/Benedict-VC/openclaw-voice/main/plugin-callme"
10
+
11
+ echo "šŸ“ž Installing callme.ai voice plugin for OpenClaw..."
12
+
13
+ # Create plugin directory
14
+ mkdir -p "$PLUGIN_DIR"
15
+
16
+ # Download plugin files
17
+ echo "šŸ“„ Downloading plugin..."
18
+ curl -fsSL "$PLUGIN_URL/package.json" -o "$PLUGIN_DIR/package.json"
19
+ curl -fsSL "$PLUGIN_URL/index.js" -o "$PLUGIN_DIR/index.js"
20
+
21
+ # Install dependencies
22
+ echo "šŸ“¦ Installing dependencies..."
23
+ cd "$PLUGIN_DIR"
24
+ npm install --silent 2>/dev/null || npm install
25
+
26
+ # Check if config.yaml exists
27
+ if [ ! -f "$CONFIG_FILE" ]; then
28
+ echo "āš ļø No config.yaml found. Creating one..."
29
+ mkdir -p "$(dirname "$CONFIG_FILE")"
30
+ cat > "$CONFIG_FILE" << 'EOF'
31
+ plugins:
32
+ callme:
33
+ enabled: true
34
+ apiKey: "YOUR_API_KEY_HERE"
35
+ defaultVoice: "nova"
36
+ language: "en"
37
+ EOF
38
+ else
39
+ # Check if callme plugin already in config
40
+ if ! grep -q "callme:" "$CONFIG_FILE"; then
41
+ echo "āš™ļø Adding callme plugin to config..."
42
+ cat >> "$CONFIG_FILE" << 'EOF'
43
+
44
+ # callme.ai Voice Plugin
45
+ plugins:
46
+ callme:
47
+ enabled: true
48
+ apiKey: "YOUR_API_KEY_HERE"
49
+ defaultVoice: "nova"
50
+ language: "en"
51
+ EOF
52
+ fi
53
+ fi
54
+
55
+ echo ""
56
+ echo "āœ… callme.ai plugin installed successfully!"
57
+ echo ""
58
+ echo "šŸ“‹ Next steps:"
59
+ echo "1. Get your API key at: https://callme.ai/dashboard"
60
+ echo "2. Add it to: $CONFIG_FILE"
61
+ echo " Replace 'YOUR_API_KEY_HERE' with your actual key"
62
+ echo "3. Reload OpenClaw: openclaw reload"
63
+ echo "4. Start calling: /call"
64
+ echo ""
65
+ echo "Need help? Check: https://docs.callme.ai/openclaw"
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "callme-openclaw-plugin",
3
+ "version": "1.0.0",
4
+ "description": "OpenClaw plugin for callme.ai - hands-free voice calls with your AI assistant",
5
+ "main": "index.js",
6
+ "bin": {
7
+ "callme-setup": "./setup.js"
8
+ },
9
+ "keywords": [
10
+ "openclaw",
11
+ "plugin",
12
+ "voice",
13
+ "ai",
14
+ "calls",
15
+ "callme"
16
+ ],
17
+ "author": "callme.ai",
18
+ "license": "MIT",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "https://github.com/Benedict-VC/openclaw-voice.git"
22
+ },
23
+ "openclaw": {
24
+ "displayName": "callme.ai Voice",
25
+ "description": "Make hands-free voice calls to your AI assistant",
26
+ "category": "communication",
27
+ "commands": [
28
+ {
29
+ "name": "/call",
30
+ "description": "Start a voice call with your AI assistant",
31
+ "usage": "/call [duration]"
32
+ },
33
+ {
34
+ "name": "/voice-setup",
35
+ "description": "Configure your callme.ai API key",
36
+ "usage": "/voice-setup"
37
+ }
38
+ ],
39
+ "config": {
40
+ "apiKey": {
41
+ "type": "string",
42
+ "description": "Your callme.ai API key (from dashboard)",
43
+ "required": true,
44
+ "secret": true
45
+ },
46
+ "defaultVoice": {
47
+ "type": "string",
48
+ "description": "Default voice (alloy, echo, fable, onyx, nova, shimmer)",
49
+ "default": "nova"
50
+ },
51
+ "language": {
52
+ "type": "string",
53
+ "description": "Language code (en, de, es, fr)",
54
+ "default": "en"
55
+ }
56
+ }
57
+ },
58
+ "dependencies": {
59
+ "ws": "^8.16.0",
60
+ "node-fetch": "^2.7.0"
61
+ }
62
+ }
package/setup.js ADDED
@@ -0,0 +1,138 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Interactive callme.ai setup for OpenClaw
4
+ * Run: npx @callme/setup
5
+ */
6
+
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+ const https = require('https');
10
+ const readline = require('readline');
11
+ const { exec } = require('child_process');
12
+ const { promisify } = require('util');
13
+
14
+ const execAsync = promisify(exec);
15
+
16
+ const PLUGIN_DIR = path.join(process.env.HOME, '.openclaw', 'plugins', 'callme');
17
+ const CONFIG_FILE = path.join(process.env.HOME, '.openclaw', 'config.yaml');
18
+
19
+ const rl = readline.createInterface({
20
+ input: process.stdin,
21
+ output: process.stdout
22
+ });
23
+
24
+ function question(query) {
25
+ return new Promise(resolve => rl.question(query, resolve));
26
+ }
27
+
28
+ function download(url, dest) {
29
+ return new Promise((resolve, reject) => {
30
+ const file = fs.createWriteStream(dest);
31
+ https.get(url, (response) => {
32
+ response.pipe(file);
33
+ file.on('finish', () => {
34
+ file.close();
35
+ resolve();
36
+ });
37
+ }).on('error', (err) => {
38
+ fs.unlink(dest, () => {});
39
+ reject(err);
40
+ });
41
+ });
42
+ }
43
+
44
+ async function main() {
45
+ console.log('\nšŸ“ž \x1b[1mcallme.ai Setup for OpenClaw\x1b[0m\n');
46
+
47
+ // Step 1: Check if OpenClaw is installed
48
+ console.log('šŸ” Checking OpenClaw installation...');
49
+ try {
50
+ await execAsync('which openclaw');
51
+ console.log('āœ… OpenClaw found\n');
52
+ } catch (error) {
53
+ console.log('āŒ OpenClaw not found. Install it first: npm install -g openclaw');
54
+ process.exit(1);
55
+ }
56
+
57
+ // Step 2: Get API Key
58
+ console.log('šŸ”‘ \x1b[1mGet your API key:\x1b[0m https://callme.ai/dashboard\n');
59
+ const apiKey = await question('Paste your API key: ');
60
+
61
+ if (!apiKey || !apiKey.startsWith('callme_')) {
62
+ console.log('\nāŒ Invalid API key format. Must start with "callme_"');
63
+ process.exit(1);
64
+ }
65
+
66
+ // Step 3: Download plugin
67
+ console.log('\nšŸ“„ Downloading plugin...');
68
+ fs.mkdirSync(PLUGIN_DIR, { recursive: true });
69
+
70
+ const files = [
71
+ { name: 'package.json', url: 'https://raw.githubusercontent.com/Benedict-VC/openclaw-voice/main/plugin-callme/package.json' },
72
+ { name: 'index.js', url: 'https://raw.githubusercontent.com/Benedict-VC/openclaw-voice/main/plugin-callme/index.js' }
73
+ ];
74
+
75
+ for (const file of files) {
76
+ await download(file.url, path.join(PLUGIN_DIR, file.name));
77
+ }
78
+ console.log('āœ… Plugin downloaded');
79
+
80
+ // Step 4: Install dependencies
81
+ console.log('\nšŸ“¦ Installing dependencies...');
82
+ process.chdir(PLUGIN_DIR);
83
+ await execAsync('npm install --silent');
84
+ console.log('āœ… Dependencies installed');
85
+
86
+ // Step 5: Update config
87
+ console.log('\nāš™ļø Updating OpenClaw config...');
88
+
89
+ const configContent = `
90
+ # callme.ai Voice Plugin
91
+ plugins:
92
+ callme:
93
+ enabled: true
94
+ apiKey: "${apiKey}"
95
+ defaultVoice: "nova"
96
+ language: "en"
97
+ `;
98
+
99
+ if (fs.existsSync(CONFIG_FILE)) {
100
+ const existing = fs.readFileSync(CONFIG_FILE, 'utf8');
101
+ if (existing.includes('callme:')) {
102
+ // Replace existing
103
+ const updated = existing.replace(
104
+ /plugins:\s*\n\s*callme:[\s\S]*?(?=\n\S|$)/,
105
+ configContent.trim()
106
+ );
107
+ fs.writeFileSync(CONFIG_FILE, updated);
108
+ } else {
109
+ // Append
110
+ fs.appendFileSync(CONFIG_FILE, configContent);
111
+ }
112
+ } else {
113
+ fs.mkdirSync(path.dirname(CONFIG_FILE), { recursive: true });
114
+ fs.writeFileSync(CONFIG_FILE, configContent);
115
+ }
116
+ console.log('āœ… Config updated');
117
+
118
+ // Step 6: Done!
119
+ console.log('\n✨ \x1b[1;32mSetup complete!\x1b[0m\n');
120
+ console.log('šŸ“‹ Next steps:');
121
+ console.log(' 1. Reload OpenClaw: \x1b[36mopenclaw reload\x1b[0m');
122
+ console.log(' 2. Start calling: \x1b[36m/call\x1b[0m');
123
+ console.log('');
124
+ console.log('šŸ“š Commands:');
125
+ console.log(' /call - Start voice call');
126
+ console.log(' /voice-usage - Check usage stats');
127
+ console.log(' /voice-keys - Manage API keys');
128
+ console.log('');
129
+ console.log('Need help? https://docs.callme.ai/openclaw');
130
+ console.log('');
131
+
132
+ rl.close();
133
+ }
134
+
135
+ main().catch((error) => {
136
+ console.error('\nāŒ Setup failed:', error.message);
137
+ process.exit(1);
138
+ });