uwonbot 1.1.2 → 1.1.3

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/bin/uwonbot.js CHANGED
@@ -14,7 +14,7 @@ showBanner();
14
14
  program
15
15
  .name('uwonbot')
16
16
  .description('Uwonbot AI Assistant — Your AI controls your computer')
17
- .version('1.1.2');
17
+ .version('1.1.3');
18
18
 
19
19
  program
20
20
  .command('login')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uwonbot",
3
- "version": "1.1.2",
3
+ "version": "1.1.3",
4
4
  "description": "Uwonbot AI Assistant CLI — Your AI controls your computer",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/agent.js CHANGED
@@ -4,8 +4,10 @@ import { promisify } from 'util';
4
4
  import chalk from 'chalk';
5
5
  import { getConfig } from './config.js';
6
6
  import ClapListener from './clapListener.js';
7
+ import { fetchAssistants, setIdToken } from './firebase-client.js';
7
8
 
8
9
  const execAsync = promisify(exec);
10
+ const WEB_APP_URL = 'https://chartapp-653e1.web.app';
9
11
  const platform = process.platform;
10
12
 
11
13
  let robot = null;
@@ -272,6 +274,84 @@ async function handleCommand(msg) {
272
274
  }
273
275
  }
274
276
 
277
+ async function openWebAssistant(assistantId) {
278
+ const url = `${WEB_APP_URL}/assistant/live?id=${assistantId}`;
279
+ try {
280
+ if (platform === 'darwin') {
281
+ await execAsync(`open -na "Google Chrome" --args --new-window "${url}" 2>/dev/null || open -na "Safari" --args "${url}" 2>/dev/null || open "${url}"`);
282
+ } else if (platform === 'win32') {
283
+ await execAsync(`start chrome --new-window "${url}" 2>nul || start "" "${url}"`);
284
+ } else {
285
+ await execAsync(`google-chrome --new-window "${url}" 2>/dev/null || xdg-open "${url}"`);
286
+ }
287
+ return true;
288
+ } catch {
289
+ try { await execAsync(`open "${url}"`); return true; } catch { return false; }
290
+ }
291
+ }
292
+
293
+ async function activateAllAssistants(assistants) {
294
+ const opened = [];
295
+ for (const a of assistants) {
296
+ const mode = a.activationMode || 'web';
297
+ if (mode === 'terminal') {
298
+ await openTerminalWithChat(a.name);
299
+ opened.push({ name: a.name, mode: 'terminal' });
300
+ } else if (mode === 'web') {
301
+ await openWebAssistant(a.id);
302
+ opened.push({ name: a.name, mode: 'web' });
303
+ } else {
304
+ await openTerminalWithChat(a.name);
305
+ await openWebAssistant(a.id);
306
+ opened.push({ name: a.name, mode: 'both' });
307
+ }
308
+ await new Promise(r => setTimeout(r, 500));
309
+ }
310
+ return opened;
311
+ }
312
+
313
+ async function loadUserAssistants(uid) {
314
+ try {
315
+ const assistants = await fetchAssistants(uid);
316
+ return assistants;
317
+ } catch {
318
+ return [];
319
+ }
320
+ }
321
+
322
+ async function listenForName(assistants, apiKey) {
323
+ if (!apiKey || assistants.length <= 1) return;
324
+ let VoiceInput;
325
+ try {
326
+ VoiceInput = (await import('./voiceInput.js')).default;
327
+ } catch { return; }
328
+
329
+ const vi = new VoiceInput(apiKey);
330
+ const names = assistants.map(a => a.name.toLowerCase());
331
+
332
+ console.log(chalk.cyan(' 🎙 비서 이름을 말하면 해당 비서만 유지됩니다...'));
333
+ console.log(chalk.gray(` 비서 목록: ${assistants.map(a => a.name).join(', ')}`));
334
+
335
+ const ok = await vi.start({
336
+ onListening: () => {},
337
+ onSpeechStart: () => {},
338
+ onSpeechEnd: () => {},
339
+ onTranscript: (text) => {
340
+ const lower = text.toLowerCase().trim();
341
+ const matched = names.find(n => lower.includes(n));
342
+ if (matched) {
343
+ const matchedAssistant = assistants.find(a => a.name.toLowerCase() === matched);
344
+ console.log(chalk.bold.green(` ✓ "${matchedAssistant.name}" 선택됨 — 나머지 비서를 종료합니다.`));
345
+ vi.stop();
346
+ }
347
+ },
348
+ });
349
+
350
+ if (!ok) {
351
+ console.log(chalk.gray(' (음성 인식을 사용할 수 없어 모든 비서가 활성 상태로 유지됩니다)'));
352
+ }
353
+ }
354
+
275
355
  export async function startAgent(port = 9876, options = {}) {
276
356
  console.log('');
277
357
  console.log(chalk.bold.cyan(' 🖥️ Uwonbot Agent'));
@@ -284,20 +364,61 @@ export async function startAgent(port = 9876, options = {}) {
284
364
  const config = getConfig();
285
365
  const uid = config.get('uid');
286
366
  const email = config.get('email');
367
+ const idToken = config.get('idToken');
287
368
 
288
369
  if (!uid) {
289
370
  console.log(chalk.red(' ✗ Not logged in. Run: uwonbot login'));
290
371
  process.exit(1);
291
372
  }
292
373
 
374
+ if (idToken) setIdToken(idToken);
375
+
293
376
  console.log(chalk.gray(` User: ${email}`));
294
377
  console.log(chalk.gray(` Port: ${port}`));
378
+
379
+ let userAssistants = [];
380
+ try {
381
+ userAssistants = await loadUserAssistants(uid);
382
+ if (userAssistants.length > 0) {
383
+ console.log(chalk.gray(` Assistants: ${userAssistants.map(a => a.name).join(', ')}`));
384
+ } else {
385
+ console.log(chalk.gray(' Assistants: (none — default Uwonbot will be used)'));
386
+ }
387
+ } catch {}
295
388
  console.log('');
296
389
 
297
390
  if (!options.noMic) {
298
- const clapListener = new ClapListener(() => {
299
- console.log(chalk.bold.cyan(' 🎯 Opening terminal...'));
300
- openTerminalWithChat();
391
+ const clapListener = new ClapListener(async () => {
392
+ console.log(chalk.bold.cyan(' 👏 박수 감지! 비서 활성화 중...'));
393
+
394
+ if (userAssistants.length === 0) {
395
+ console.log(chalk.gray(' → 기본 Uwonbot 실행'));
396
+ openTerminalWithChat('Uwonbot');
397
+ return;
398
+ }
399
+
400
+ if (userAssistants.length === 1) {
401
+ const a = userAssistants[0];
402
+ const mode = a.activationMode || 'web';
403
+ console.log(chalk.green(` → ${a.name} 실행 (${mode})`));
404
+ if (mode === 'terminal' || mode === 'both') await openTerminalWithChat(a.name);
405
+ if (mode === 'web' || mode === 'both') await openWebAssistant(a.id);
406
+ return;
407
+ }
408
+
409
+ console.log(chalk.cyan(` → ${userAssistants.length}개 비서 모두 활성화`));
410
+ const opened = await activateAllAssistants(userAssistants);
411
+ opened.forEach(o => {
412
+ console.log(chalk.gray(` ✓ ${o.name} (${o.mode})`));
413
+ });
414
+
415
+ const geminiKey = userAssistants.find(a => a.apiKey)?.apiKey;
416
+ if (geminiKey) {
417
+ await listenForName(userAssistants, geminiKey);
418
+ } else {
419
+ console.log(chalk.gray(' → 모든 비서가 활성 상태로 유지됩니다.'));
420
+ console.log(chalk.gray(' (비서 이름을 말해 선택하려면 API 키가 필요합니다)'));
421
+ }
301
422
  });
302
423
  await clapListener.start();
303
424
  console.log('');
@@ -318,7 +439,7 @@ export async function startAgent(port = 9876, options = {}) {
318
439
  console.log(chalk.yellow(' ○ Client disconnected'));
319
440
  });
320
441
 
321
- ws.send(JSON.stringify({ type: 'welcome', agent: 'uwonbot', version: '1.0.3', uid }));
442
+ ws.send(JSON.stringify({ type: 'welcome', agent: 'uwonbot', version: '1.1.2', uid }));
322
443
  });
323
444
 
324
445
  wss.on('error', (err) => {
@@ -331,9 +452,7 @@ export async function startAgent(port = 9876, options = {}) {
331
452
  });
332
453
 
333
454
  console.log(chalk.bold.green(` ✓ Agent running on ws://localhost:${port}`));
334
- console.log(chalk.gray(' Waiting for connections...'));
335
- console.log('');
336
- console.log(chalk.gray(' Press Ctrl+C to stop'));
455
+ console.log(chalk.gray(' Waiting for connections... (Ctrl+C to stop)'));
337
456
  console.log('');
338
457
 
339
458
  process.on('SIGINT', () => {