twinclaw 1.1.1 → 1.1.2
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 +3 -3
- package/dist/api/handlers/agents.js +82 -0
- package/dist/api/handlers/debug.js +69 -0
- package/dist/api/handlers/devices.js +79 -0
- package/dist/api/handlers/jobs.js +99 -0
- package/dist/api/handlers/status.js +149 -0
- package/dist/api/router.js +272 -2
- package/dist/api/runtime-event-producer.js +20 -0
- package/dist/api/shared.js +34 -0
- package/dist/api/websocket-hub.js +18 -7
- package/dist/config/json-config.js +393 -1
- package/dist/core/heartbeat.js +304 -8
- package/dist/core/lane-executor.js +18 -0
- package/dist/core/onboarding.js +2 -2
- package/dist/core/self-improve-cli.js +212 -0
- package/dist/core/simplified-onboarding.js +158 -16
- package/dist/index.js +61 -33
- package/dist/interfaces/dispatcher.js +4 -2
- package/dist/interfaces/whatsapp_handler.js +243 -2
- package/dist/services/auto-configurer.js +591 -0
- package/dist/services/device-pairing.js +378 -0
- package/dist/services/hooks.js +130 -0
- package/dist/services/job-scheduler.js +133 -1
- package/dist/services/learning-system.js +226 -0
- package/dist/services/self-healing.js +267 -0
- package/dist/services/skill-acquisition/intent-parser.js +115 -0
- package/dist/services/skill-acquisition/research-engine.js +319 -0
- package/dist/services/skill-builder.js +235 -0
- package/dist/services/sub-agent-service.js +215 -0
- package/dist/services/web-service.js +70 -0
- package/dist/services/webhook-service.js +127 -0
- package/dist/skills/builtin.js +827 -0
- package/dist/tools/agent-improvement.js +208 -0
- package/dist/types/skill-acquisition.js +4 -0
- package/package.json +3 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as readline from 'readline';
|
|
2
2
|
import { randomBytes } from 'node:crypto';
|
|
3
3
|
import { getConfigPath, readConfig, writeConfig, } from '../config/config-loader.js';
|
|
4
|
+
import { BUILTIN_WINDOWS_SKILLS } from '../config/json-config.js';
|
|
4
5
|
import { initializeWorkspace, getWorkspaceDir } from '../config/workspace.js';
|
|
5
6
|
import { ensureIdentityFiles } from '../config/identity-bootstrap.js';
|
|
6
7
|
import { createSession } from '../services/db.js';
|
|
@@ -107,8 +108,17 @@ async function runWhatsAppQRLogin() {
|
|
|
107
108
|
}
|
|
108
109
|
export async function runSimplifiedOnboarding() {
|
|
109
110
|
console.clear();
|
|
110
|
-
console.log(
|
|
111
|
-
|
|
111
|
+
console.log(`
|
|
112
|
+
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
|
|
113
|
+
██░▄▄▄░██░▄▄░██░▄▄▄██░▀██░██░▄▄▀██░████░▄▄▀██░███░██
|
|
114
|
+
██░███░██░▀▀░██░▄▄▄██░█░█░██░█████░████░▀▀░██░█░█░██
|
|
115
|
+
██░▀▀▀░██░█████░▀▀▀██░██▄░██░▀▀▄██░▀▀░█░██░██▄▀▄▀▄██
|
|
116
|
+
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
|
117
|
+
🦅 TWINCLAW 🦅
|
|
118
|
+
|
|
119
|
+
TwinClaw — Your Agentic AI Gateway
|
|
120
|
+
Multi-Modal Hooks | Proactive Memory | Voice-First
|
|
121
|
+
`);
|
|
112
122
|
console.log('═══════════════════════════════════════════════════════════\n');
|
|
113
123
|
await initializeWorkspace();
|
|
114
124
|
const configPath = getConfigPath();
|
|
@@ -161,15 +171,52 @@ export async function runSimplifiedOnboarding() {
|
|
|
161
171
|
console.log('\n═══════════════════════════════════════════════════════════');
|
|
162
172
|
console.log(' STEP 4: Telegram Setup');
|
|
163
173
|
console.log('═══════════════════════════════════════════════════════════\n');
|
|
164
|
-
console.log('
|
|
165
|
-
console.log('
|
|
166
|
-
console.log('
|
|
167
|
-
console.log('
|
|
168
|
-
|
|
169
|
-
|
|
174
|
+
console.log('┌────────────────────────────────────────────────────────────────┐');
|
|
175
|
+
console.log('│ How to get a Telegram Bot Token: │');
|
|
176
|
+
console.log('│ │');
|
|
177
|
+
console.log('│ 1. Open Telegram and chat with @BotFather │');
|
|
178
|
+
console.log('│ 2. Send /newbot (or /mybots) │');
|
|
179
|
+
console.log('│ 3. Follow instructions and copy the token │');
|
|
180
|
+
console.log('│ (looks like 123456:ABC-DEF...) │');
|
|
181
|
+
console.log('│ │');
|
|
182
|
+
console.log('│ Tip: You can also set TELEGRAM_BOT_TOKEN in your env. │');
|
|
183
|
+
console.log('│ Docs: https://docs.twinclaw.ai/telegram │');
|
|
184
|
+
console.log('└────────────────────────────────────────────────────────────────┘\n');
|
|
185
|
+
const telegramToken = await promptSecret('Enter Telegram bot token: ');
|
|
186
|
+
console.log('\n┌────────────────────────────────────────────────────────────────┐');
|
|
187
|
+
console.log('│ Telegram DM Access Policy: │');
|
|
188
|
+
console.log('│ │');
|
|
189
|
+
console.log('│ pairing - Unknown DMs get a pairing code (recommended) │');
|
|
190
|
+
console.log('│ allowlist - Only specific users can message │');
|
|
191
|
+
console.log('│ open - Anyone can message │');
|
|
192
|
+
console.log('│ │');
|
|
193
|
+
console.log('│ To approve: twinclaw pairing approve telegram <code> │');
|
|
194
|
+
console.log('│ Docs: https://docs.twinclaw.ai/pairing │');
|
|
195
|
+
console.log('└────────────────────────────────────────────────────────────────┘\n');
|
|
196
|
+
const dmPolicyChoice = await prompt('DM policy [allowlist]: ');
|
|
197
|
+
const dmPolicies = {
|
|
198
|
+
'1': 'pairing', 'pairing': 'pairing',
|
|
199
|
+
'2': 'allowlist', 'allowlist': 'allowlist',
|
|
200
|
+
'3': 'open', 'open': 'open'
|
|
201
|
+
};
|
|
202
|
+
const dmPolicy = dmPolicies[dmPolicyChoice] || 'allowlist';
|
|
203
|
+
let telegramUserId = null;
|
|
204
|
+
if (dmPolicy === 'allowlist') {
|
|
205
|
+
console.log('\n┌────────────────────────────────────────────────────────────────┐');
|
|
206
|
+
console.log('│ How to find your Telegram User ID: │');
|
|
207
|
+
console.log('│ │');
|
|
208
|
+
console.log('│ 1. DM your bot, then check twinclaw logs (safest) │');
|
|
209
|
+
console.log('│ 2. Call: https://api.telegram.org/bot<token>/getUpdates │');
|
|
210
|
+
console.log('│ 3. Third-party: DM @userinfobot or @getidsbot │');
|
|
211
|
+
console.log('│ │');
|
|
212
|
+
console.log('│ Docs: https://docs.twinclaw.ai/telegram │');
|
|
213
|
+
console.log('└────────────────────────────────────────────────────────────────┘\n');
|
|
214
|
+
const userIdInput = await prompt('Enter Telegram User ID (your account): ');
|
|
215
|
+
telegramUserId = userIdInput ? userIdInput : null;
|
|
216
|
+
}
|
|
170
217
|
config.messaging = config.messaging || {
|
|
171
|
-
telegram: { enabled: false, botToken: '', userId: null },
|
|
172
|
-
whatsapp: { enabled: false, phoneNumber: '' },
|
|
218
|
+
telegram: { enabled: false, botToken: '', userId: null, dmPolicy: 'allowlist', groupPolicy: 'allowlist', allowFrom: [] },
|
|
219
|
+
whatsapp: { enabled: false, phoneNumber: '', dmPolicy: 'allowlist', groupPolicy: 'allowlist', allowFrom: [] },
|
|
173
220
|
voice: { groqApiKey: '' },
|
|
174
221
|
inbound: { enabled: false, debounceMs: 0 },
|
|
175
222
|
streaming: {
|
|
@@ -185,19 +232,59 @@ export async function runSimplifiedOnboarding() {
|
|
|
185
232
|
enabled: !!telegramToken,
|
|
186
233
|
botToken: telegramToken,
|
|
187
234
|
userId: telegramUserId ? parseInt(telegramUserId, 10) : null,
|
|
235
|
+
dmPolicy,
|
|
236
|
+
groupPolicy: 'allowlist',
|
|
237
|
+
allowFrom: telegramUserId ? [telegramUserId] : [],
|
|
188
238
|
};
|
|
239
|
+
console.log('\n✅ Telegram configured!\n');
|
|
189
240
|
}
|
|
190
241
|
if (useWhatsApp) {
|
|
191
242
|
console.log('\n═══════════════════════════════════════════════════════════');
|
|
192
243
|
console.log(' STEP 5: WhatsApp Setup');
|
|
193
244
|
console.log('═══════════════════════════════════════════════════════════\n');
|
|
245
|
+
console.log('┌────────────────────────────────────────────────────────────────┐');
|
|
246
|
+
console.log('│ How to link WhatsApp: │');
|
|
247
|
+
console.log('│ │');
|
|
248
|
+
console.log('│ 1. Open WhatsApp on your phone │');
|
|
249
|
+
console.log('│ 2. Tap Menu (⋮) → Linked Devices │');
|
|
250
|
+
console.log('│ 3. Tap "Link a Device" and scan the QR code │');
|
|
251
|
+
console.log('│ │');
|
|
252
|
+
console.log('│ Note: WhatsApp session persists after setup. │');
|
|
253
|
+
console.log('│ Docs: https://docs.twinclaw.ai/whatsapp │');
|
|
254
|
+
console.log('└────────────────────────────────────────────────────────────────┘\n');
|
|
194
255
|
const startWhatsApp = await promptYesNo('Start WhatsApp QR code login now?', true);
|
|
195
256
|
if (startWhatsApp) {
|
|
257
|
+
console.log('\n');
|
|
196
258
|
const success = await runWhatsAppQRLogin();
|
|
197
259
|
if (success) {
|
|
260
|
+
console.log('\n┌────────────────────────────────────────────────────────────────┐');
|
|
261
|
+
console.log('│ WhatsApp DM Access Policy: │');
|
|
262
|
+
console.log('│ │');
|
|
263
|
+
console.log('│ allowlist - Only specific numbers can message (recommended) │');
|
|
264
|
+
console.log('│ open - Anyone can message │');
|
|
265
|
+
console.log('│ │');
|
|
266
|
+
console.log('│ To approve: twinclaw pairing approve whatsapp <code> │');
|
|
267
|
+
console.log('│ Docs: https://docs.twinclaw.ai/pairing │');
|
|
268
|
+
console.log('└────────────────────────────────────────────────────────────────┘\n');
|
|
269
|
+
const waDmPolicyChoice = await prompt('DM policy [allowlist]: ');
|
|
270
|
+
const waDmPolicies = {
|
|
271
|
+
'1': 'allowlist', 'allowlist': 'allowlist',
|
|
272
|
+
'2': 'open', 'open': 'open'
|
|
273
|
+
};
|
|
274
|
+
const waDmPolicy = waDmPolicies[waDmPolicyChoice] || 'allowlist';
|
|
275
|
+
let waAllowFrom = [];
|
|
276
|
+
if (waDmPolicy === 'allowlist') {
|
|
277
|
+
console.log('\nEnter phone numbers that can message TwinClaw:');
|
|
278
|
+
console.log(' Format: +1234567890 (E.164 format)');
|
|
279
|
+
console.log(' Separate multiple numbers with commas\n');
|
|
280
|
+
const allowFromInput = await prompt('Phone numbers: ');
|
|
281
|
+
waAllowFrom = allowFromInput
|
|
282
|
+
? allowFromInput.split(',').map(p => p.trim()).filter(p => p)
|
|
283
|
+
: [];
|
|
284
|
+
}
|
|
198
285
|
config.messaging = config.messaging || {
|
|
199
|
-
telegram: { enabled: false, botToken: '', userId: null },
|
|
200
|
-
whatsapp: { enabled: false, phoneNumber: '' },
|
|
286
|
+
telegram: { enabled: false, botToken: '', userId: null, dmPolicy: 'pairing', groupPolicy: 'allowlist', allowFrom: [] },
|
|
287
|
+
whatsapp: { enabled: false, phoneNumber: '', dmPolicy: 'allowlist', groupPolicy: 'allowlist', allowFrom: [] },
|
|
201
288
|
voice: { groqApiKey: '' },
|
|
202
289
|
inbound: { enabled: false, debounceMs: 0 },
|
|
203
290
|
streaming: {
|
|
@@ -212,29 +299,84 @@ export async function runSimplifiedOnboarding() {
|
|
|
212
299
|
config.messaging.whatsapp = {
|
|
213
300
|
enabled: true,
|
|
214
301
|
phoneNumber: '',
|
|
302
|
+
dmPolicy: waDmPolicy,
|
|
303
|
+
groupPolicy: 'allowlist',
|
|
304
|
+
allowFrom: waAllowFrom,
|
|
215
305
|
};
|
|
306
|
+
console.log('\n✅ WhatsApp configured!\n');
|
|
216
307
|
}
|
|
217
308
|
}
|
|
218
309
|
}
|
|
219
310
|
console.log('\n═══════════════════════════════════════════════════════════');
|
|
220
|
-
console.log(' STEP 6:
|
|
311
|
+
console.log(' STEP 6: Skills (Windows-Focused)');
|
|
312
|
+
console.log('═══════════════════════════════════════════════════════════\n');
|
|
313
|
+
console.log('Skills extend TwinClaw with additional capabilities.\n');
|
|
314
|
+
console.log('Available Windows Skills:');
|
|
315
|
+
console.log('─'.repeat(60));
|
|
316
|
+
const windowsSkills = BUILTIN_WINDOWS_SKILLS.filter(s => s.autoInstall || s.os === 'windows' || s.os === 'all');
|
|
317
|
+
const productivitySkills = windowsSkills.filter(s => s.category === 'productivity');
|
|
318
|
+
const systemSkills = windowsSkills.filter(s => s.category === 'system');
|
|
319
|
+
const utilSkills = windowsSkills.filter(s => s.category === 'utilities');
|
|
320
|
+
const devSkills = windowsSkills.filter(s => s.category === 'development');
|
|
321
|
+
console.log('\n📦 Productivity (auto-enabled):');
|
|
322
|
+
for (const skill of productivitySkills) {
|
|
323
|
+
console.log(` ✓ ${skill.name} - ${skill.description}`);
|
|
324
|
+
}
|
|
325
|
+
console.log('\n🖥️ System (Windows):');
|
|
326
|
+
for (const skill of systemSkills.slice(0, 8)) {
|
|
327
|
+
console.log(` ○ ${skill.name} - ${skill.description}`);
|
|
328
|
+
}
|
|
329
|
+
console.log('\n🔧 Development:');
|
|
330
|
+
for (const skill of devSkills) {
|
|
331
|
+
console.log(` ○ ${skill.name} - ${skill.description}`);
|
|
332
|
+
}
|
|
333
|
+
console.log('\n📱 Utilities:');
|
|
334
|
+
for (const skill of utilSkills.slice(0, 6)) {
|
|
335
|
+
console.log(` ○ ${skill.name} - ${skill.description}`);
|
|
336
|
+
}
|
|
337
|
+
console.log('\n' + '─'.repeat(60));
|
|
338
|
+
console.log(`Total: ${windowsSkills.length} skills available`);
|
|
339
|
+
console.log('\nSkills can be configured later with: twinclaw skills list');
|
|
340
|
+
await promptYesNo('\nContinue (skills can be configured later)?', true);
|
|
341
|
+
console.log('\n═══════════════════════════════════════════════════════════');
|
|
342
|
+
console.log(' STEP 7: 🤖 Self-Improving Agent');
|
|
343
|
+
console.log('═══════════════════════════════════════════════════════════\n');
|
|
344
|
+
console.log('TwinClaw can automatically improve itself!\n');
|
|
345
|
+
console.log('🧠 Auto-Learning: Learns from successes and failures');
|
|
346
|
+
console.log('💚 Self-Healing: Auto-detects and fixes issues');
|
|
347
|
+
console.log('🔧 Auto-Config: Connects to new services automatically');
|
|
348
|
+
console.log('🧱 Skill Builder: Creates new skills on demand\n');
|
|
349
|
+
console.log('┌────────────────────────────────────────────────────────────────┐');
|
|
350
|
+
console.log('│ Supported Integrations (auto-configure): │');
|
|
351
|
+
console.log('│ │');
|
|
352
|
+
console.log('│ Slack, Discord, Notion, GitHub, Jira, Linear, Spotify, │');
|
|
353
|
+
console.log('│ OpenWeather, Twilio, and more... │');
|
|
354
|
+
console.log('│ │');
|
|
355
|
+
console.log('│ Just ask: "Connect to Slack" or "Add GitHub integration" │');
|
|
356
|
+
console.log('└────────────────────────────────────────────────────────────────┘\n');
|
|
357
|
+
const enableSelfImprovement = await promptYesNo('Enable self-improving features?', true);
|
|
358
|
+
console.log('\n═══════════════════════════════════════════════════════════');
|
|
359
|
+
console.log(' STEP 8: Voice (Optional)');
|
|
221
360
|
console.log('═══════════════════════════════════════════════════════════\n');
|
|
222
361
|
const groqKey = await promptSecret('Groq API Key for voice/STT (optional, press Enter to skip): ');
|
|
223
362
|
config = {
|
|
224
363
|
...config,
|
|
225
364
|
runtime: {
|
|
226
365
|
apiSecret: apiSecret || randomBytes(24).toString('hex'),
|
|
227
|
-
apiPort: config.runtime?.apiPort ||
|
|
366
|
+
apiPort: config.runtime?.apiPort || 67420,
|
|
228
367
|
secretVaultRequired: config.runtime?.secretVaultRequired || [],
|
|
229
368
|
},
|
|
230
369
|
models: {
|
|
231
370
|
openRouterApiKey: openrouterKey || '',
|
|
232
371
|
modalApiKey: modalKey || '',
|
|
233
372
|
geminiApiKey: geminiKey || '',
|
|
373
|
+
definitions: [],
|
|
374
|
+
primaryModel: '',
|
|
375
|
+
fallbackModels: [],
|
|
234
376
|
},
|
|
235
377
|
messaging: config.messaging || {
|
|
236
|
-
telegram: { enabled: false, botToken: '', userId: null },
|
|
237
|
-
whatsapp: { enabled: false, phoneNumber: '' },
|
|
378
|
+
telegram: { enabled: false, botToken: '', userId: null, dmPolicy: 'allowlist', groupPolicy: 'allowlist', allowFrom: [] },
|
|
379
|
+
whatsapp: { enabled: false, phoneNumber: '', dmPolicy: 'allowlist', groupPolicy: 'allowlist', allowFrom: [] },
|
|
238
380
|
voice: { groqApiKey: '' },
|
|
239
381
|
inbound: { enabled: false, debounceMs: 0 },
|
|
240
382
|
streaming: {
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
3
|
const require = createRequire(import.meta.url);
|
|
4
|
-
import { handleOnboardCli, runOnboarding, runSetupWizard, startBasicREPL } from './core/onboarding.js';
|
|
4
|
+
import { handleOnboardCli, runOnboarding, runSetupWizard, runSimplifiedOnboarding, startBasicREPL } from './core/onboarding.js';
|
|
5
5
|
import { Gateway } from './core/gateway.js';
|
|
6
6
|
import { handleDoctorCli, handleHelpCli, handleUnknownCommand } from './core/cli.js';
|
|
7
|
+
import { handleSelfImproveCli } from './core/self-improve-cli.js';
|
|
7
8
|
import { HeartbeatService } from './core/heartbeat.js';
|
|
8
9
|
import { Dispatcher } from './interfaces/dispatcher.js';
|
|
9
10
|
import { TelegramHandler } from './interfaces/telegram_handler.js';
|
|
@@ -24,7 +25,7 @@ import { logThought } from './utils/logger.js';
|
|
|
24
25
|
import { PolicyEngine } from './services/policy-engine.js';
|
|
25
26
|
import { savePolicyAuditLog } from './services/db.js';
|
|
26
27
|
import { getSecretVaultService } from './services/secret-vault.js';
|
|
27
|
-
import { getConfigValue, checkAndMigrateWorkspace } from './config/config-loader.js';
|
|
28
|
+
import { getConfigValue, checkAndMigrateWorkspace, readConfig } from './config/config-loader.js';
|
|
28
29
|
import { getIdentityDir, getWorkspaceSubdir } from './config/workspace.js';
|
|
29
30
|
import { handleSecretVaultCli } from './core/secret-vault-cli.js';
|
|
30
31
|
import { handlePairingCli } from './core/pairing-cli.js';
|
|
@@ -33,6 +34,10 @@ import { handleGatewayCli } from './core/gateway-cli.js';
|
|
|
33
34
|
import { handleLogsCli } from './core/logs-cli.js';
|
|
34
35
|
import { getDmPairingService } from './services/dm-pairing.js';
|
|
35
36
|
import { randomUUID } from 'node:crypto';
|
|
37
|
+
import { initializeSelfHealer, getSelfHealingService } from './services/self-healing.js';
|
|
38
|
+
import { initializeSkillBuilder, getSkillBuilder } from './services/skill-builder.js';
|
|
39
|
+
import { initializeAutoConfigurer, getAutoConfigurer } from './services/auto-configurer.js';
|
|
40
|
+
import { initializeLearning, getLearningSystem } from './services/learning-system.js';
|
|
36
41
|
const secretVault = getSecretVaultService();
|
|
37
42
|
const pairingService = getDmPairingService();
|
|
38
43
|
function assertWindowsOnlyRuntime() {
|
|
@@ -47,6 +52,9 @@ assertWindowsOnlyRuntime();
|
|
|
47
52
|
if (handleHelpCli(process.argv.slice(2))) {
|
|
48
53
|
process.exit(process.exitCode ?? 0);
|
|
49
54
|
}
|
|
55
|
+
if (await handleSelfImproveCli(process.argv.slice(2))) {
|
|
56
|
+
process.exit(process.exitCode ?? 0);
|
|
57
|
+
}
|
|
50
58
|
if (handleDoctorCli(process.argv.slice(2))) {
|
|
51
59
|
process.exit(process.exitCode ?? 0);
|
|
52
60
|
}
|
|
@@ -100,11 +108,8 @@ async function tryAutoSetup() {
|
|
|
100
108
|
catch (error) {
|
|
101
109
|
console.log("\n[TwinClaw] Welcome! Critical configuration missing.");
|
|
102
110
|
console.log("Starting the interactive setup wizard to configure your agent.\n");
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const exitCode = result.status === 'cancelled' ? 130 : 1;
|
|
106
|
-
process.exit(exitCode);
|
|
107
|
-
}
|
|
111
|
+
await runSimplifiedOnboarding();
|
|
112
|
+
await runSetupWizard();
|
|
108
113
|
console.log("\n[TwinClaw] Setup complete. Initializing Gateway...\n");
|
|
109
114
|
}
|
|
110
115
|
}
|
|
@@ -143,6 +148,23 @@ fileWatcher.addTarget({
|
|
|
143
148
|
// ── MCP Skill Registry & Server Manager ──────────────────────────────────────
|
|
144
149
|
const skillRegistry = new SkillRegistry();
|
|
145
150
|
skillRegistry.registerMany(createBuiltinSkills());
|
|
151
|
+
// ── Self-Improving Services ───────────────────────────────────────────────────
|
|
152
|
+
const selfHealer = getSelfHealingService();
|
|
153
|
+
const skillBuilder = getSkillBuilder();
|
|
154
|
+
const autoConfigurer = getAutoConfigurer();
|
|
155
|
+
const learningSystem = getLearningSystem();
|
|
156
|
+
void (async () => {
|
|
157
|
+
try {
|
|
158
|
+
await initializeSelfHealer();
|
|
159
|
+
await initializeSkillBuilder();
|
|
160
|
+
await initializeAutoConfigurer();
|
|
161
|
+
await initializeLearning();
|
|
162
|
+
void logThought('[TwinClaw] Self-improving services initialized');
|
|
163
|
+
}
|
|
164
|
+
catch (err) {
|
|
165
|
+
console.warn('[TwinClaw] Self-improving services init warning:', err);
|
|
166
|
+
}
|
|
167
|
+
})();
|
|
146
168
|
const mcpManager = new McpServerManager(skillRegistry);
|
|
147
169
|
void (async () => {
|
|
148
170
|
try {
|
|
@@ -187,32 +209,41 @@ const telegramBotToken = secretVault.readSecret('TELEGRAM_BOT_TOKEN');
|
|
|
187
209
|
const telegramUserId = getConfigValue('TELEGRAM_USER_ID')?.trim();
|
|
188
210
|
const whatsappPhoneNumber = secretVault.readSecret('WHATSAPP_PHONE_NUMBER') ?? getConfigValue('WHATSAPP_PHONE_NUMBER');
|
|
189
211
|
const groqApiKey = secretVault.readSecret('GROQ_API_KEY');
|
|
212
|
+
const twinClawConfig = await readConfig();
|
|
190
213
|
let dispatcher = null;
|
|
191
214
|
if (telegramBotToken || whatsappPhoneNumber) {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
215
|
+
let telegramHandler;
|
|
216
|
+
let whatsappHandler;
|
|
217
|
+
let sttService;
|
|
218
|
+
let ttsService;
|
|
219
|
+
const telegramAllowFrom = [...(twinClawConfig.messaging.telegram?.allowFrom || [])];
|
|
220
|
+
const whatsappAllowFrom = [...(twinClawConfig.messaging.whatsapp?.allowFrom || [])];
|
|
221
|
+
if (telegramBotToken) {
|
|
222
|
+
telegramHandler = new TelegramHandler(telegramBotToken);
|
|
223
|
+
if (telegramUserId) {
|
|
224
|
+
const parsedTelegramUserId = Number(telegramUserId);
|
|
225
|
+
if (!Number.isInteger(parsedTelegramUserId) || parsedTelegramUserId <= 0) {
|
|
226
|
+
console.error('[TwinClaw] TELEGRAM_USER_ID must be a positive integer when provided.');
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
telegramAllowFrom.push(String(parsedTelegramUserId));
|
|
207
230
|
}
|
|
208
231
|
}
|
|
209
|
-
|
|
210
|
-
|
|
232
|
+
}
|
|
233
|
+
if (whatsappPhoneNumber) {
|
|
234
|
+
whatsappHandler = new WhatsAppHandler();
|
|
235
|
+
if (!whatsappAllowFrom.includes(whatsappPhoneNumber)) {
|
|
211
236
|
whatsappAllowFrom.push(whatsappPhoneNumber);
|
|
212
237
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
238
|
+
}
|
|
239
|
+
if (groqApiKey) {
|
|
240
|
+
sttService = new SttService(groqApiKey);
|
|
241
|
+
ttsService = new TtsService(groqApiKey);
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
console.warn('[TwinClaw] GROQ_API_KEY not set. Voice/STT features disabled.');
|
|
245
|
+
}
|
|
246
|
+
if (telegramHandler || whatsappHandler) {
|
|
216
247
|
const queueService = new QueueService(async (platform, chatId, text) => {
|
|
217
248
|
switch (platform) {
|
|
218
249
|
case 'telegram':
|
|
@@ -233,19 +264,16 @@ if (telegramBotToken || whatsappPhoneNumber) {
|
|
|
233
264
|
dispatcher = new Dispatcher(telegramHandler, whatsappHandler, sttService, ttsService, gateway, queueService, {
|
|
234
265
|
pairingService,
|
|
235
266
|
telegram: {
|
|
236
|
-
dmPolicy: '
|
|
267
|
+
dmPolicy: 'allowlist',
|
|
237
268
|
allowFrom: telegramAllowFrom,
|
|
238
269
|
},
|
|
239
270
|
whatsapp: {
|
|
240
|
-
dmPolicy: '
|
|
271
|
+
dmPolicy: 'allowlist',
|
|
241
272
|
allowFrom: whatsappAllowFrom,
|
|
242
273
|
},
|
|
243
274
|
});
|
|
244
275
|
void logThought('[TwinClaw] Messaging dispatcher and persistent queue initialized.');
|
|
245
276
|
}
|
|
246
|
-
else {
|
|
247
|
-
console.warn('[TwinClaw] Dispatcher requires GROQ_API_KEY.');
|
|
248
|
-
}
|
|
249
277
|
}
|
|
250
278
|
else {
|
|
251
279
|
console.log('[TwinClaw] Messaging dispatcher not initialized (missing Telegram AND WhatsApp configs).');
|
|
@@ -305,7 +333,7 @@ const runtimeEventProducer = new RuntimeEventProducer({
|
|
|
305
333
|
dispatcher: dispatcher ?? undefined,
|
|
306
334
|
modelRouter,
|
|
307
335
|
});
|
|
308
|
-
const apiPort = Number(getConfigValue('API_PORT')) ||
|
|
336
|
+
const apiPort = Number(getConfigValue('API_PORT')) || 67420;
|
|
309
337
|
startApiServer({
|
|
310
338
|
heartbeat,
|
|
311
339
|
skillRegistry,
|
|
@@ -5,7 +5,7 @@ import { InboundDebounceService } from '../services/inbound-debounce.js';
|
|
|
5
5
|
import { EmbeddedBlockChunker } from '../services/block-chunker.js';
|
|
6
6
|
import { getConfigValue } from '../config/config-loader.js';
|
|
7
7
|
const DEFAULT_ACCESS_CONFIG = {
|
|
8
|
-
dmPolicy: '
|
|
8
|
+
dmPolicy: 'allowlist',
|
|
9
9
|
allowFrom: [],
|
|
10
10
|
};
|
|
11
11
|
function buildPairingChallenge(channel, code) {
|
|
@@ -154,7 +154,9 @@ export class Dispatcher {
|
|
|
154
154
|
const filePath = message.audioFilePath;
|
|
155
155
|
let transcribedText;
|
|
156
156
|
try {
|
|
157
|
-
|
|
157
|
+
if (this.#stt) {
|
|
158
|
+
transcribedText = await this.#stt.transcribeFile(filePath);
|
|
159
|
+
}
|
|
158
160
|
}
|
|
159
161
|
finally {
|
|
160
162
|
fs.unlink(filePath, (unlinkErr) => {
|