twinclaw 1.2.6 → 1.2.8
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.
|
@@ -109,8 +109,18 @@ function detectActiveFeatures() {
|
|
|
109
109
|
* Individual model conditions only generate issues when no model key is available at all.
|
|
110
110
|
*/
|
|
111
111
|
function hasAnyModelKey() {
|
|
112
|
-
const modelKeys = ['MODAL_API_KEY', 'OPENROUTER_API_KEY', 'GEMINI_API_KEY', 'GITHUB_TOKEN'];
|
|
113
|
-
|
|
112
|
+
const modelKeys = ['MODAL_API_KEY', 'OPENROUTER_API_KEY', 'GEMINI_API_KEY', 'GITHUB_TOKEN', 'GROQ_API_KEY'];
|
|
113
|
+
// Check secret vault first
|
|
114
|
+
const vaultKeys = modelKeys.some((key) => getSecretVaultService().readSecret(key) !== null);
|
|
115
|
+
if (vaultKeys)
|
|
116
|
+
return true;
|
|
117
|
+
// Also check config file via getConfigValue (sync)
|
|
118
|
+
const modalKey = getConfigValue('MODAL_API_KEY');
|
|
119
|
+
const openRouterKey = getConfigValue('OPENROUTER_API_KEY');
|
|
120
|
+
const geminiKey = getConfigValue('GEMINI_API_KEY');
|
|
121
|
+
const githubKey = getConfigValue('GITHUB_TOKEN');
|
|
122
|
+
const groqKey = getConfigValue('GROQ_API_KEY');
|
|
123
|
+
return !!(modalKey || openRouterKey || geminiKey || githubKey || groqKey);
|
|
114
124
|
}
|
|
115
125
|
// ── Public API ───────────────────────────────────────────────────────────────
|
|
116
126
|
/**
|
|
@@ -9,6 +9,16 @@ import { logThought } from '../utils/logger.js';
|
|
|
9
9
|
import { getConfigValue } from '../config/json-config.js';
|
|
10
10
|
const { Client, LocalAuth, MessageMedia } = WAWebJS;
|
|
11
11
|
const RATE_LIMIT_MS = 1500;
|
|
12
|
+
const WHATSAPP_COMMANDS = [
|
|
13
|
+
{ cmd: '/start', desc: 'Show welcome message and menu' },
|
|
14
|
+
{ cmd: '/help', desc: 'Show all commands' },
|
|
15
|
+
{ cmd: '/menu', desc: 'Show menu' },
|
|
16
|
+
{ cmd: '/status', desc: 'Show gateway status' },
|
|
17
|
+
{ cmd: '/models', desc: 'Show configured models' },
|
|
18
|
+
{ cmd: '/keys', desc: 'Show API keys status' },
|
|
19
|
+
{ cmd: '/channel', desc: 'Show channel status' },
|
|
20
|
+
{ cmd: '/clear', desc: 'Clear conversation' },
|
|
21
|
+
];
|
|
12
22
|
/**
|
|
13
23
|
* Create MessageMedia from URL
|
|
14
24
|
*/
|
|
@@ -59,6 +69,73 @@ export class WhatsAppHandler {
|
|
|
59
69
|
this.#maxReconnectDelayMs = options.maxReconnectDelayMs ?? 60000;
|
|
60
70
|
this.#registerListeners();
|
|
61
71
|
}
|
|
72
|
+
// ── Command Handlers ─────────────────────────────────────────────────────────
|
|
73
|
+
async handleCommand(chatId, command) {
|
|
74
|
+
const cmd = command.toLowerCase().trim();
|
|
75
|
+
switch (cmd) {
|
|
76
|
+
case '/start':
|
|
77
|
+
case '/menu':
|
|
78
|
+
const menuText = `🎯 *TwinClaw Menu*
|
|
79
|
+
|
|
80
|
+
${WHATSAPP_COMMANDS.map(c => `${c.cmd} - ${c.desc}`).join('\n')}
|
|
81
|
+
|
|
82
|
+
_How can I help you today?_
|
|
83
|
+
`;
|
|
84
|
+
await this.sendText(chatId, menuText);
|
|
85
|
+
break;
|
|
86
|
+
case '/help':
|
|
87
|
+
await this.sendText(chatId, `📋 *Available Commands:*
|
|
88
|
+
|
|
89
|
+
${WHATSAPP_COMMANDS.map(c => `${c.cmd} - ${c.desc}`).join('\n')}
|
|
90
|
+
|
|
91
|
+
_Just send me a message and I'll respond!_
|
|
92
|
+
`);
|
|
93
|
+
break;
|
|
94
|
+
case '/status':
|
|
95
|
+
await this.sendText(chatId, `✅ *Gateway Status:*
|
|
96
|
+
|
|
97
|
+
• Status: Running
|
|
98
|
+
• Platform: WhatsApp
|
|
99
|
+
• Mode: AI Assistant
|
|
100
|
+
|
|
101
|
+
_All systems operational_
|
|
102
|
+
`);
|
|
103
|
+
break;
|
|
104
|
+
case '/models':
|
|
105
|
+
case '/model':
|
|
106
|
+
await this.sendText(chatId, `📦 *Models*
|
|
107
|
+
|
|
108
|
+
Use *twinclaw config* to manage models.
|
|
109
|
+
|
|
110
|
+
Or run: *twinclaw config model* in terminal
|
|
111
|
+
`);
|
|
112
|
+
break;
|
|
113
|
+
case '/keys':
|
|
114
|
+
case '/key':
|
|
115
|
+
await this.sendText(chatId, `🔑 *API Keys*
|
|
116
|
+
|
|
117
|
+
Use *twinclaw config* to manage API keys.
|
|
118
|
+
|
|
119
|
+
Or run: *twinclaw config* in terminal
|
|
120
|
+
`);
|
|
121
|
+
break;
|
|
122
|
+
case '/channel':
|
|
123
|
+
case '/channels':
|
|
124
|
+
await this.sendText(chatId, `📱 *Channels*
|
|
125
|
+
|
|
126
|
+
WhatsApp: ✅ Connected
|
|
127
|
+
Telegram: Use /channels to check
|
|
128
|
+
|
|
129
|
+
Run: *twinclaw channels* in terminal
|
|
130
|
+
`);
|
|
131
|
+
break;
|
|
132
|
+
case '/clear':
|
|
133
|
+
await this.sendText(chatId, '🗑️ Conversation cleared! Starting fresh.');
|
|
134
|
+
break;
|
|
135
|
+
default:
|
|
136
|
+
await this.sendText(chatId, `Unknown command: ${command}\nType /menu for available commands.`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
62
139
|
onMessage;
|
|
63
140
|
async #applyRateLimit() {
|
|
64
141
|
const elapsed = Date.now() - this.#lastMessageAt;
|
|
@@ -166,6 +243,13 @@ export class WhatsAppHandler {
|
|
|
166
243
|
}
|
|
167
244
|
// Exclude empty bodies unless they were intercepted voice notes
|
|
168
245
|
if (msg.body) {
|
|
246
|
+
const text = msg.body;
|
|
247
|
+
// Handle commands
|
|
248
|
+
if (text.trim().startsWith('/')) {
|
|
249
|
+
const command = text.trim().split(' ')[0];
|
|
250
|
+
await this.handleCommand(msg.from, command);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
169
253
|
await this.onMessage?.(base);
|
|
170
254
|
}
|
|
171
255
|
});
|