ps-claw 1.1.1 β†’ 1.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ps-claw",
3
- "version": "1.1.1",
3
+ "version": "1.2.0",
4
4
  "description": "PS Claw - AI Agent Gateway with multi-provider support, web UI, and CLI. Lightweight fork of OpenClaw.",
5
5
  "keywords": [
6
6
  "ai",
@@ -251,7 +251,7 @@
251
251
  "postinstall": "echo 'Skipping postinstall'",
252
252
  "postpack": "echo 'Skipping postpack'",
253
253
  "preinstall": "echo 'Skipping preinstall'",
254
- "prepack": "echo 'Skipping prepack \u2014 dist/ already included in files'",
254
+ "prepack": "echo 'Skipping prepack β€” dist/ already included in files'",
255
255
  "prepare": "echo 'Skipping prepare'",
256
256
  "prepush:ci": "bash scripts/prepush-ci.sh",
257
257
  "probe:anthropic:prompt": "node --import tsx scripts/anthropic-prompt-probe.ts",
@@ -596,4 +596,4 @@
596
596
  "node": ">=22.19.0"
597
597
  },
598
598
  "packageManager": "pnpm@11.2.2+sha512.36e6621fad506178936455e70247b8808ef4ec25797a9f437a93281a020484e2607f6a469a22e982987c3dbb8866e3071514ab10a4a1749e06edcd1ec118436f"
599
- }
599
+ }
@@ -192,6 +192,22 @@ body{font-family:'Syne',sans-serif;background:var(--bg);color:var(--txt);display
192
192
  .bbl th,.bbl td{border:1px solid var(--border2);padding:8px 12px;text-align:left}
193
193
  .bbl th{background:var(--surface3);font-weight:700}
194
194
 
195
+ /* action tags */
196
+ .act{padding:4px 5px 4px 10px;margin:6px 0 2px;border-radius:8px;font-size:12px;font-weight:700;font-family:'JetBrains Mono',monospace;display:flex;align-items:center;gap:7px;line-height:1.3;word-break:break-word}
197
+ .act-cmd{background:rgba(59,130,246,.12);color:#3b82f6;border-left:3px solid #3b82f6}
198
+ .act-click{background:rgba(139,92,246,.12);color:#8b5cf6;border-left:3px solid #8b5cf6}
199
+ .act-app{background:rgba(16,185,129,.12);color:#10b981;border-left:3px solid #10b981}
200
+ .act-file{background:rgba(245,158,11,.12);color:#f59e0b;border-left:3px solid #f59e0b}
201
+ .act-err{background:rgba(239,68,68,.1);color:#ef4444;border-left:3px solid #ef4444}
202
+ .act-ok{background:rgba(16,185,129,.1);color:#10b981;border-left:3px solid #10b981}
203
+ .act::before{font-size:13px}
204
+ .act-cmd::before{content:'πŸ’»'}
205
+ .act-click::before{content:'πŸ–±οΈ'}
206
+ .act-app::before{content:'πŸ“‚'}
207
+ .act-file::before{content:'πŸ“„'}
208
+ .act-err::before{content:'❌'}
209
+ .act-ok::before{content:'βœ…'}
210
+
195
211
  /* typing */
196
212
  .typing{display:flex;gap:4px;align-items:center;padding:14px 16px}
197
213
  .typing span{width:6px;height:6px;border-radius:50%;background:var(--acc);animation:pulse 1.2s infinite}
@@ -334,7 +350,112 @@ body{font-family:'Syne',sans-serif;background:var(--bg);color:var(--txt);display
334
350
  #sidebar.open{transform:none}
335
351
  #menu-btn{display:flex}
336
352
  .model-grid{grid-template-columns:1fr 1fr}
353
+ .store-grid{grid-template-columns:1fr 1fr !important}
354
+ #tabs{overflow-x:auto}
355
+ }
356
+
357
+ /* ── STORE (Play Store-like) ── */
358
+ .store-banner{
359
+ background:linear-gradient(135deg,var(--acc3) 0%,var(--acc) 50%,#9b8fff 100%);
360
+ border-radius:var(--rad);padding:28px 32px;margin-bottom:20px;
361
+ display:flex;align-items:center;gap:24px;color:#fff;
362
+ box-shadow:0 8px 32px rgba(124,106,247,.3);position:relative;overflow:hidden
363
+ }
364
+ .store-banner::after{
365
+ content:'🦞';position:absolute;right:24px;top:50%;transform:translateY(-50%);
366
+ font-size:90px;opacity:.18
367
+ }
368
+ .store-banner h2{font-size:24px;font-weight:800;margin-bottom:6px;letter-spacing:-.5px}
369
+ .store-banner p{font-size:13px;opacity:.9;max-width:560px;line-height:1.6}
370
+ .store-toolbar{display:flex;flex-direction:column;gap:12px;margin-bottom:20px}
371
+ #store-search{
372
+ width:100%;padding:11px 16px;background:var(--surface2);border:1px solid var(--border);
373
+ border-radius:var(--rad-sm);color:var(--txt);font-size:14px;outline:none;
374
+ font-family:'Syne',sans-serif;transition:border-color .15s
375
+ }
376
+ #store-search:focus{border-color:var(--acc)}
377
+ .store-cats{display:flex;gap:8px;flex-wrap:wrap}
378
+ .store-cat{
379
+ padding:6px 14px;border-radius:20px;border:1px solid var(--border);
380
+ background:transparent;color:var(--txt3);cursor:pointer;font-size:12px;font-weight:700;
381
+ transition:all .15s;font-family:'Syne',sans-serif
382
+ }
383
+ .store-cat:hover{color:var(--txt2);border-color:var(--border2)}
384
+ .store-cat.on{background:var(--acc);border-color:var(--acc);color:#fff}
385
+ .store-section-title{
386
+ font-size:16px;font-weight:800;margin:20px 0 14px;letter-spacing:-.3px;
387
+ display:flex;align-items:center;gap:8px
388
+ }
389
+ .store-grid{
390
+ display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));
391
+ gap:14px;margin-bottom:24px
392
+ }
393
+ .store-card{
394
+ background:var(--surface2);border:1px solid var(--border);
395
+ border-radius:var(--rad);padding:16px;cursor:pointer;
396
+ transition:all .2s;display:flex;flex-direction:column;gap:8px;
397
+ position:relative;overflow:hidden
398
+ }
399
+ .store-card:hover{border-color:var(--border2);transform:translateY(-2px);box-shadow:0 6px 20px rgba(0,0,0,.3)}
400
+ .store-card.installed{border-color:rgba(34,201,138,.4)}
401
+ .store-card-head{display:flex;align-items:center;gap:10px}
402
+ .store-icon{
403
+ width:42px;height:42px;border-radius:11px;flex-shrink:0;
404
+ background:linear-gradient(135deg,var(--surface3),var(--surface));
405
+ display:flex;align-items:center;justify-content:center;font-size:22px;
406
+ border:1px solid var(--border2)
407
+ }
408
+ .store-card.featured .store-icon{background:linear-gradient(135deg,var(--acc3),var(--acc));border-color:transparent}
409
+ .store-name{font-size:14px;font-weight:700;line-height:1.2}
410
+ .store-cat-tag{font-size:10px;color:var(--txt3);text-transform:uppercase;letter-spacing:.5px;font-weight:700}
411
+ .store-desc{font-size:12px;color:var(--txt2);line-height:1.55;flex:1;min-height:36px}
412
+ .store-meta{display:flex;align-items:center;justify-content:space-between;font-size:11px;color:var(--txt3);margin-top:4px}
413
+ .store-rating{display:flex;align-items:center;gap:3px;color:var(--yellow);font-weight:700}
414
+ .store-installed-badge{
415
+ position:absolute;top:10px;right:10px;
416
+ background:rgba(34,201,138,.15);color:var(--green);
417
+ font-size:10px;font-weight:700;padding:3px 8px;border-radius:10px
418
+ }
419
+ .store-actions{display:flex;gap:6px;margin-top:6px}
420
+ .store-actions .btn{flex:1}
421
+ .store-empty{text-align:center;padding:40px 20px;color:var(--txt3)}
422
+ .store-empty-icon{font-size:48px;margin-bottom:12px}
423
+
424
+ /* ── QUICKSTART MODAL ── */
425
+ #qs-bg{
426
+ display:none;position:fixed;inset:0;background:rgba(0,0,0,.8);z-index:300;
427
+ align-items:center;justify-content:center;backdrop-filter:blur(8px)
337
428
  }
429
+ #qs-bg.open{display:flex}
430
+ #qs-modal{
431
+ background:var(--surface);border:1px solid var(--border2);border-radius:var(--rad);
432
+ padding:32px;width:560px;max-width:95vw;max-height:90vh;overflow-y:auto;
433
+ box-shadow:0 24px 80px rgba(0,0,0,.6)
434
+ }
435
+ #qs-modal h2{font-size:22px;font-weight:800;margin-bottom:8px;letter-spacing:-.4px}
436
+ #qs-modal .qs-sub{font-size:13px;color:var(--txt2);margin-bottom:24px;line-height:1.6}
437
+ .qs-step{font-size:11px;color:var(--acc2);font-weight:700;text-transform:uppercase;letter-spacing:.8px;margin-bottom:6px}
438
+ .qs-provs{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:20px}
439
+ .qs-prov{
440
+ padding:14px;background:var(--surface2);border:1.5px solid var(--border);
441
+ border-radius:var(--rad-sm);cursor:pointer;transition:all .15s;text-align:left;
442
+ font-family:'Syne',sans-serif;color:var(--txt);font-size:13px;font-weight:600
443
+ }
444
+ .qs-prov:hover{border-color:var(--border2)}
445
+ .qs-prov.sel{border-color:var(--acc);background:rgba(124,106,247,.08)}
446
+ .qs-prov .qs-prov-name{display:block;font-size:14px;margin-bottom:3px}
447
+ .qs-prov .qs-prov-desc{font-size:11px;color:var(--txt3);font-weight:500}
448
+ .qs-final{background:var(--surface2);border:1px solid var(--border);border-radius:var(--rad-sm);padding:16px;margin-top:14px}
449
+ .qs-final-row{display:flex;justify-content:space-between;font-size:13px;padding:6px 0;border-bottom:1px solid var(--border)}
450
+ .qs-final-row:last-child{border:none}
451
+
452
+ /* ── BROWSER USE ── */
453
+ .bu-status-ok{background:rgba(34,201,138,.08);border:1px solid rgba(34,201,138,.3);color:var(--green);padding:12px 16px;border-radius:var(--rad-sm);font-size:13px;font-weight:600}
454
+ .bu-status-warn{background:rgba(240,160,72,.08);border:1px solid rgba(240,160,72,.3);color:var(--orange);padding:12px 16px;border-radius:var(--rad-sm);font-size:13px;font-weight:600}
455
+ .bu-status-err{background:rgba(240,90,90,.08);border:1px solid rgba(240,90,90,.3);color:var(--red);padding:12px 16px;border-radius:var(--rad-sm);font-size:13px;font-weight:600}
456
+ .bu-result-box{background:var(--surface);border:1px solid var(--border);border-radius:var(--rad-sm);padding:14px;font-family:'JetBrains Mono',monospace;font-size:12px;white-space:pre-wrap;max-height:400px;overflow-y:auto;color:var(--txt);line-height:1.6}
457
+ .bu-result-err{background:rgba(240,90,90,.08);border:1px solid rgba(240,90,90,.3);color:var(--red)}
458
+
338
459
  </style>
339
460
  </head>
340
461
  <body>
@@ -380,6 +501,8 @@ body{font-family:'Syne',sans-serif;background:var(--bg);color:var(--txt);display
380
501
  <div class="tab active" data-t="chat" onclick="goTab('chat')">πŸ’¬ Chat</div>
381
502
  <div class="tab" data-t="keys" onclick="goTab('keys')">πŸ”‘ API Keys</div>
382
503
  <div class="tab" data-t="models" onclick="goTab('models')">πŸ€– Modelos</div>
504
+ <div class="tab" data-t="store" onclick="goTab('store')">πŸ›’ Loja</div>
505
+ <div class="tab" data-t="browser" onclick="goTab('browser')">🌐 Browser</div>
383
506
  <div class="tab" data-t="gateways" onclick="goTab('gateways')">πŸ”Œ Gateways</div>
384
507
  <div class="tab" data-t="settings" onclick="goTab('settings')">βš™οΈ Config</div>
385
508
  </div>
@@ -556,7 +679,7 @@ body{font-family:'Syne',sans-serif;background:var(--bg);color:var(--txt);display
556
679
  <h3>πŸ’¬ Comportamento</h3>
557
680
  <div class="field">
558
681
  <label>System prompt</label>
559
- <textarea id="cfg-sys" placeholder="VocΓͺ Γ© um assistente de IA ΓΊtil e direto." oninput="saveCfg()"></textarea>
682
+ <textarea id="cfg-sys" placeholder="VocΓͺ Γ© a PS Claw, assistente de IA com acesso a ferramentas e aΓ§Γ΅es executΓ‘veis no computador do usuΓ‘rio. VocΓͺ pode: executar comandos no terminal/shell do sistema, dar cliques na tela e controlar o mouse, abrir aplicativos e programas, manipular arquivos e pastas. Sempre informe o que estΓ‘ executando e o resultado obtido. Seja direta, ΓΊtil e proativa." oninput="saveCfg()"></textarea>
560
683
  </div>
561
684
  <div class="row">
562
685
  <div class="field">
@@ -579,6 +702,80 @@ body{font-family:'Syne',sans-serif;background:var(--bg);color:var(--txt);display
579
702
  </div>
580
703
  </div>
581
704
  </div>
705
+
706
+ <!-- STORE (Play Store-like) -->
707
+ <div class="page" id="store-page">
708
+ <div class="page-inner" style="max-width:1100px">
709
+ <div class="sp-header">
710
+ <div class="sp-title">πŸ›’ Loja PS Claw</div>
711
+ <div class="sp-sub">CatΓ‘logo do <strong style="color:var(--acc2)">Claw Hub</strong> β€” instale plugins, MCPs, skills e integraΓ§Γ΅es com 1 clique. Tudo baixado direto do dashboard.</div>
712
+ </div>
713
+
714
+ <!-- banner de destaque -->
715
+ <div id="store-banner" class="store-banner"></div>
716
+
717
+ <!-- search + filtros -->
718
+ <div class="store-toolbar">
719
+ <input id="store-search" type="text" placeholder="πŸ”Ž Buscar plugins..." oninput="renderStore()"/>
720
+ <div id="store-cats" class="store-cats"></div>
721
+ </div>
722
+
723
+ <!-- seΓ§Γ£o: Em Destaque -->
724
+ <h3 class="store-section-title">⭐ Em Destaque</h3>
725
+ <div id="store-featured" class="store-grid"></div>
726
+
727
+ <!-- seΓ§Γ£o: Todos -->
728
+ <h3 class="store-section-title">πŸ“¦ Todos os Plugins</h3>
729
+ <div id="store-all" class="store-grid"></div>
730
+ </div>
731
+ </div>
732
+
733
+ <!-- BROWSER USE -->
734
+ <div class="page" id="browser-page">
735
+ <div class="page-inner" style="max-width:900px">
736
+ <div class="sp-header">
737
+ <div class="sp-title">🌐 Browser Use</div>
738
+ <div class="sp-sub">Controle um navegador por IA: diga o que quer fazer e a IA abre o site, clica, preenche formulΓ‘rios e extrai dados automaticamente.</div>
739
+ </div>
740
+
741
+ <div class="card" id="bu-status-card">
742
+ <h3>πŸ“‹ Status da IntegraΓ§Γ£o</h3>
743
+ <div id="bu-status-content"><p style="color:var(--txt2)">Verificando...</p></div>
744
+ <div style="margin-top:14px;display:flex;gap:10px;flex-wrap:wrap">
745
+ <button class="btn btn-primary" onclick="buInstall()">⬇️ Instalar Browser Use</button>
746
+ <button class="btn btn-ghost" onclick="buStatus()">πŸ”„ Re-verificar</button>
747
+ <a class="btn btn-ghost" href="https://github.com/browser-use/browser-use" target="_blank" style="text-decoration:none">πŸ“– DocumentaΓ§Γ£o</a>
748
+ </div>
749
+ </div>
750
+
751
+ <div class="card">
752
+ <h3>πŸ€– Rodar Tarefa no Browser</h3>
753
+ <div class="field">
754
+ <label>URL inicial (opcional)</label>
755
+ <input type="text" id="bu-url" placeholder="https://exemplo.com"/>
756
+ </div>
757
+ <div class="field">
758
+ <label>Tarefa em linguagem natural</label>
759
+ <textarea id="bu-task" placeholder="Ex: Acesse o site, faΓ§a login com user admin e senha 1234, depois baixe o relatΓ³rio de vendas do mΓͺs passado." style="min-height:100px"></textarea>
760
+ </div>
761
+ <div style="display:flex;gap:10px;flex-wrap:wrap;align-items:center">
762
+ <button class="btn btn-primary" onclick="buRun()">▢️ Executar Tarefa</button>
763
+ <span id="bu-run-status" style="font-size:12px;color:var(--txt3)"></span>
764
+ </div>
765
+ <div id="bu-result" style="margin-top:14px"></div>
766
+ </div>
767
+
768
+ <div class="card">
769
+ <h3>πŸ’‘ Exemplos de Tarefas</h3>
770
+ <div class="chips" style="justify-content:flex-start">
771
+ <div class="chip" onclick="document.getElementById('bu-url').value='https://news.ycombinator.com';document.getElementById('bu-task').value='Liste as 5 notΓ­cias mais populares da pΓ‘gina inicial com tΓ­tulo e link.'">πŸ“‹ Extrair notΓ­cias</div>
772
+ <div class="chip" onclick="document.getElementById('bu-url').value='https://google.com';document.getElementById('bu-task').value='Pesquise por \"PS Claw AI agent\" e liste os 3 primeiros resultados com tΓ­tulo e URL.'">πŸ”Ž Pesquisar no Google</div>
773
+ <div class="chip" onclick="document.getElementById('bu-url').value='';document.getElementById('bu-task').value='Acesse amazon.com e encontre os 5 livros mais vendidos de tecnologia esta semana.'">πŸ“š Top produtos</div>
774
+ <div class="chip" onclick="document.getElementById('bu-url').value='https://github.com';document.getElementById('bu-task').value='VΓ‘ em trending, liste os 5 repositΓ³rios mais populares do dia com nome, linguagem e estrelas.'">πŸ“Š GitHub Trending</div>
775
+ </div>
776
+ </div>
777
+ </div>
778
+ </div>
582
779
  </div><!-- /main -->
583
780
 
584
781
  <!-- MODAL ADD GATEWAY -->
@@ -595,6 +792,64 @@ body{font-family:'Syne',sans-serif;background:var(--bg);color:var(--txt);display
595
792
  </div>
596
793
  </div>
597
794
 
795
+ <!-- QUICKSTART MODAL (primeiro uso) -->
796
+ <div id="qs-bg">
797
+ <div id="qs-modal">
798
+ <div style="text-align:center;margin-bottom:18px">
799
+ <div style="font-size:48px;margin-bottom:6px">🦞</div>
800
+ <h2>Bem-vindo ao PS Claw!</h2>
801
+ <div class="qs-sub">Vamos configurar tudo em menos de 1 minuto. VocΓͺ sΓ³ precisa de <strong style="color:var(--acc2)">uma</strong> API key.</div>
802
+ </div>
803
+
804
+ <div class="qs-step">Passo 1 β€” Escolha o provedor</div>
805
+ <div class="qs-provs" id="qs-provs">
806
+ <button class="qs-prov sel" data-p="google" onclick="qsSelProv('google')">
807
+ <span class="qs-prov-name">πŸ”΅ Google Gemini</span>
808
+ <span class="qs-prov-desc">GrΓ‘tis para sempre Β· recomendado</span>
809
+ </button>
810
+ <button class="qs-prov" data-p="anthropic" onclick="qsSelProv('anthropic')">
811
+ <span class="qs-prov-name">🟠 Anthropic Claude</span>
812
+ <span class="qs-prov-desc">US$5 de crΓ©dito inicial</span>
813
+ </button>
814
+ <button class="qs-prov" data-p="openai" onclick="qsSelProv('openai')">
815
+ <span class="qs-prov-name">🟒 OpenAI GPT-4o</span>
816
+ <span class="qs-prov-desc">US$5 de crΓ©dito inicial</span>
817
+ </button>
818
+ <button class="qs-prov" data-p="mistral" onclick="qsSelProv('mistral')">
819
+ <span class="qs-prov-name">🟣 Mistral</span>
820
+ <span class="qs-prov-desc">Trial de crΓ©dito Β· Europa</span>
821
+ </button>
822
+ </div>
823
+
824
+ <div class="qs-step">Passo 2 β€” Cole sua API Key</div>
825
+ <div class="field">
826
+ <label>API Key</label>
827
+ <input type="password" id="qs-key" placeholder="Cole aqui..." oninput="qsClearTest()"/>
828
+ <div style="font-size:11px;color:var(--txt3);margin-top:6px" id="qs-key-hint">
829
+ Obtenha grΓ‘tis em: <a href="https://aistudio.google.com/apikey" target="_blank" style="color:var(--acc2)">aistudio.google.com/apikey</a>
830
+ </div>
831
+ </div>
832
+ <button class="btn btn-ghost btn-sm" style="margin-top:8px" onclick="qsTestKey()">πŸ” Testar chave</button>
833
+ <span id="qs-test-result" style="font-size:12px;margin-left:10px"></span>
834
+
835
+ <div class="qs-step" style="margin-top:18px">Passo 3 β€” Modelo padrΓ£o</div>
836
+ <div class="field">
837
+ <select id="qs-model"></select>
838
+ </div>
839
+
840
+ <div class="qs-step" style="margin-top:18px">Passo 4 β€” Perfil (opcional)</div>
841
+ <div class="row">
842
+ <div class="field"><label>Seu nome</label><input type="text" id="qs-name" placeholder="VocΓͺ"/></div>
843
+ <div class="field"><label>Nome do agente</label><input type="text" id="qs-agent" placeholder="PS Claw"/></div>
844
+ </div>
845
+
846
+ <div class="modal-acts" style="margin-top:24px">
847
+ <button class="btn btn-ghost" onclick="closeQs()">Pular por agora</button>
848
+ <button class="btn btn-primary" onclick="qsFinish()">βœ… Finalizar e conversar</button>
849
+ </div>
850
+ </div>
851
+ </div>
852
+
598
853
  <div id="toast"></div>
599
854
 
600
855
  <script>
@@ -653,11 +908,13 @@ function goTab(t){
653
908
  document.querySelectorAll('.tab').forEach(e=>e.classList.toggle('active',e.dataset.t===t));
654
909
  document.querySelectorAll('.page').forEach(e=>e.classList.remove('active'));
655
910
  document.getElementById(t+'-page').classList.add('active');
656
- document.getElementById('tb-title').textContent={chat:'Chat',keys:'API Keys',models:'Modelos',gateways:'Gateways',settings:'ConfiguraΓ§Γ΅es'}[t]||t;
911
+ document.getElementById('tb-title').textContent={chat:'Chat',keys:'API Keys',models:'Modelos',store:'Loja',browser:'Browser Use',gateways:'Gateways',settings:'ConfiguraΓ§Γ΅es'}[t]||t;
657
912
  if(t==='models') renderModels();
658
913
  if(t==='gateways') renderGws();
659
914
  if(t==='keys') loadKeys();
660
915
  if(t==='settings') loadCfg();
916
+ if(t==='store') loadStore();
917
+ if(t==='browser') buStatus();
661
918
  }
662
919
 
663
920
  // ─── CHAT ─────────────────────────────────────────────────────────────────
@@ -683,6 +940,12 @@ function renderChatList(){
683
940
  <button class="ci-del" onclick="deleteChat('${c.id}',event)">βœ•</button>
684
941
  </div>`).join('');
685
942
  }
943
+ function parseMsg(text){
944
+ var lines=text.split('\n');var parts=[];var buf=[];
945
+ var flush=function(){if(!buf.length)return;var raw=buf.join('\n').trim();buf.length=0;if(!raw)return;var cls='act-cmd';var lo=raw.toLowerCase();if(/\b(click|clique|mouse|botao|botΓ£o|right.?click|duplo|dblclick|scroll|arrastar|drag)\b/.test(lo))cls='act-click';else if(/\b(abrir|open|launch|start|iniciar|executar app|\.exe|notepad|calc|chrome|firefox|edge|explorer)\b/.test(lo))cls='act-app';else if(/\b(arquivo|file|criar|deletar|remover|delete|salvar|save|mover|move|copiar|copy|mkdir)\b/.test(lo))cls='act-file';else if(/^(erro|error|falha|failed|exception|traceback|fatal)/i.test(lo))cls='act-err';else if(/^(ok|sucesso|success|concluΓ­do|concluido|feito|done|pronto)/i.test(lo))cls='act-ok';parts.push({type:'action',text:raw,cls});};
946
+ for(var i=0;i<lines.length;i++){var trimmed=lines[i].trim();if(!trimmed||trimmed.charAt(0)=='-'||trimmed.charAt(0)=='*'||trimmed.charAt(0)=='#'||trimmed.indexOf('\`\`\`')===0){buf.push(lines[i]);continue;}var isAct=/^(> |\$ |❯|β–Ά|\$|PS |C:|D:)/.test(trimmed)||/^(Executando|Running |Abrindo|Opening |Exec)/.test(trimmed)||/^(npm |pip |python |node |git |ls |cd |mkdir |rm |cat |open |start |launch |echo |mv |cp |touch |chmod |docker |kubectl |make )/i.test(trimmed);if(isAct){flush();buf.push(lines[i]);}else buf.push(lines[i]);}flush();if(!parts.length&&text.trim())parts.push({type:'text',text});return parts;
947
+ }
948
+
686
949
  function renderMsgs(){
687
950
  const box=document.getElementById('messages');
688
951
  if(!curChat||!curChat.msgs.length){
@@ -699,13 +962,7 @@ function renderMsgs(){
699
962
  </div>`;
700
963
  return;
701
964
  }
702
- box.innerHTML=curChat.msgs.map(m=>`
703
- <div class="msg-row ${m.role==='user'?'user':''}">
704
- <div class="av ${m.role==='user'?'user':'ai'}">${m.role==='user'?(S.cfg.name||'V')[0].toUpperCase():'🦞'}</div>
705
- <div class="bbl ${m.role==='user'?'user':'ai'}">${fmt(m.content)}</div>
706
- </div>`).join('');
707
- box.scrollTop=box.scrollHeight;
708
- }
965
+ box.innerHTML=curChat.msgs.map(function(m){var body="";if(m.role==="user"){body="<div class=\"bbl user\">"+fmt(m.content)+"</div>";}else{var parts=parseMsg(m.content||"");body=parts.map(function(p){if(p.type==="text")return fmt(p.text);return "<div class=\"act "+p.cls+"\">"+fmt(p.text)+"</div>";}).join("");body="<div class=\"bbl ai\">"+body+"</div>";}return "<div class=\"msg-row "+(m.role==="user"?"user":"")+"\">\n <div class=\\"av \"+(m.role==="user"?"user":"ai")+"\">"+(m.role==="user"?(S.cfg.name||"V")[0].toUpperCase():"🦞")+"</div>\n "+body+"</div>";}).join("");box.scrollTop=box.scrollHeight;}
709
966
  function addTyping(){
710
967
  const box=document.getElementById('messages');
711
968
  document.getElementById('welcome')?.remove();
@@ -756,7 +1013,7 @@ async function sendMsg(){
756
1013
  async function callApi(model, text, key){
757
1014
  const history = curChat.msgs.slice(0,-1).map(m=>({role:m.role,content:m.content}));
758
1015
  const messages = [...history, {role:'user',content:text}];
759
- const sys = S.cfg.sys || 'VocΓͺ Γ© um assistente de IA ΓΊtil e direto.';
1016
+ const sys = S.cfg.sys || 'VocΓͺ Γ© a PS Claw, uma assistente de IA inteligente e prestativa. VocΓͺ tem acesso a ferramentas poderosas e pode executar aΓ§Γ΅es no computador do usuΓ‘rio, incluindo: executar comandos no terminal/shell do sistema, dar cliques na tela e controlar o mouse, abrir aplicativos e programas, e manipular arquivos. Sempre que executar uma dessas aΓ§Γ΅es, explique o que estΓ‘ fazendo e informe o resultado. Seja direta, ΓΊtil e proativa.';
760
1017
  const temp = parseFloat(S.cfg.temp)||0.7;
761
1018
  const maxTok = parseInt(S.cfg.tok)||4096;
762
1019
 
@@ -1032,11 +1289,264 @@ function autoResize(el){el.style.height='auto';el.style.height=Math.min(el.scrol
1032
1289
  function sendQ(t){document.getElementById('msg-in').value=t;goTab('chat');sendMsg();}
1033
1290
  function toggleSb(){document.getElementById('sidebar').classList.toggle('open');}
1034
1291
 
1292
+ // ─── QUICKSTART (primeiro uso) ─────────────────────────────────────────────
1293
+ let qsProv = 'google';
1294
+ const QS_LINKS = {
1295
+ google:'https://aistudio.google.com/apikey',
1296
+ anthropic:'https://console.anthropic.com/api-keys',
1297
+ openai:'https://platform.openai.com/api-keys',
1298
+ mistral:'https://console.mistral.ai/api-keys',
1299
+ };
1300
+ const QS_PH = {
1301
+ google:'AIzaSy...',
1302
+ anthropic:'sk-ant-api03-...',
1303
+ openai:'sk-proj-...',
1304
+ mistral:'...',
1305
+ };
1306
+ function qsSelProv(p){
1307
+ qsProv = p;
1308
+ document.querySelectorAll('.qs-prov').forEach(b => b.classList.toggle('sel', b.dataset.p === p));
1309
+ // atualiza hint
1310
+ document.getElementById('qs-key-hint').innerHTML =
1311
+ `Obtenha em: <a href="${QS_LINKS[p]}" target="_blank" style="color:var(--acc2)">${QS_LINKS[p].replace('https://','')}</a>`;
1312
+ document.getElementById('qs-key').placeholder = QS_PH[p];
1313
+ qsClearTest();
1314
+ // atualiza lista de modelos
1315
+ const sel = document.getElementById('qs-model');
1316
+ const models = MODELS.filter(m => m.p === p);
1317
+ sel.innerHTML = models.map(m => `<option value="${m.id}">${m.name} β€” ${m.desc}</option>`).join('');
1318
+ }
1319
+ function qsClearTest(){
1320
+ const r = document.getElementById('qs-test-result');
1321
+ r.textContent = '';
1322
+ r.style.color = 'var(--txt3)';
1323
+ }
1324
+ async function qsTestKey(){
1325
+ const key = document.getElementById('qs-key').value.trim();
1326
+ const r = document.getElementById('qs-test-result');
1327
+ if (!key){ r.textContent='⚠️ Cole a chave primeiro'; r.style.color='var(--orange)'; return; }
1328
+ r.textContent = 'πŸ” Testando...'; r.style.color = 'var(--txt3)';
1329
+ try {
1330
+ let ok = false;
1331
+ if (qsProv === 'anthropic') {
1332
+ const x = await fetch(px('https://api.anthropic.com/v1/models'), { headers: { 'x-api-key': key, 'anthropic-version': '2023-06-01' } });
1333
+ ok = x.ok;
1334
+ } else if (qsProv === 'openai') {
1335
+ const x = await fetch(px('https://api.openai.com/v1/models'), { headers: { Authorization: 'Bearer ' + key } });
1336
+ ok = x.ok;
1337
+ } else if (qsProv === 'google') {
1338
+ const x = await fetch(px(`https://generativelanguage.googleapis.com/v1beta/models?key=${key}`));
1339
+ ok = x.ok;
1340
+ } else if (qsProv === 'mistral') {
1341
+ const x = await fetch(px('https://api.mistral.ai/v1/models'), { headers: { Authorization: 'Bearer ' + key } });
1342
+ ok = x.ok;
1343
+ }
1344
+ if (ok) { r.textContent = 'βœ… Chave vΓ‘lida!'; r.style.color = 'var(--green)'; }
1345
+ else { r.textContent = '❌ Chave invÑlida ou erro'; r.style.color = 'var(--red)'; }
1346
+ } catch (e) {
1347
+ r.textContent = '❌ ' + e.message; r.style.color = 'var(--red)';
1348
+ }
1349
+ }
1350
+ function openQs(){ document.getElementById('qs-bg').classList.add('open'); qsSelProv('google'); }
1351
+ function closeQs(){ document.getElementById('qs-bg').classList.remove('open'); }
1352
+ function qsFinish(){
1353
+ const key = document.getElementById('qs-key').value.trim();
1354
+ if (!key) { toast('⚠️ Cole sua API key'); return; }
1355
+ const model = document.getElementById('qs-model').value;
1356
+ const name = document.getElementById('qs-name').value.trim() || 'VocΓͺ';
1357
+ const agent = document.getElementById('qs-agent').value.trim() || 'PS Claw';
1358
+ S.keys[qsProv] = key;
1359
+ S.model = model;
1360
+ S.cfg.name = name;
1361
+ S.cfg.agent = agent;
1362
+ S.onboarded = true;
1363
+ save();
1364
+ updateStatus();
1365
+ const m = MODELS.find(x => x.id === model);
1366
+ if (m) document.getElementById('model-name-badge').textContent = m.name;
1367
+ closeQs();
1368
+ toast('πŸŽ‰ PS Claw configurado! Pode conversar.');
1369
+ goTab('chat');
1370
+ }
1371
+
1372
+ // ─── STORE (Claw Hub catalog) ──────────────────────────────────────────────
1373
+ let storeData = null;
1374
+ let storeCatFilter = 'all';
1375
+ let storeBusy = {};
1376
+
1377
+ async function loadStore(){
1378
+ try {
1379
+ const r = await fetch('/api/store');
1380
+ storeData = await r.json();
1381
+ } catch {
1382
+ storeData = { plugins: [], featured: [], categories: [] };
1383
+ }
1384
+ renderStore();
1385
+ }
1386
+
1387
+ function renderStore(){
1388
+ if (!storeData) return;
1389
+ // banner
1390
+ const banner = document.getElementById('store-banner');
1391
+ const instCount = storeData.plugins.filter(p => p.installed).length;
1392
+ banner.innerHTML = `
1393
+ <div style="position:relative;z-index:1;flex:1">
1394
+ <h2>πŸ›’ Claw Hub β€” Loja de Plugins</h2>
1395
+ <p>${storeData.plugins.length} plugins disponΓ­veis Β· ${storeData.categories.length} categorias Β· ${instCount} instalados.<br>
1396
+ Instale MCPs, integraΓ§Γ΅es e skills com 1 clique β€” tudo baixado direto do dashboard.</p>
1397
+ </div>`;
1398
+ // categorias
1399
+ const cats = document.getElementById('store-cats');
1400
+ cats.innerHTML = `<button class="store-cat ${storeCatFilter==='all'?'on':''}" onclick="storeSetCat('all')">Todos</button>` +
1401
+ storeData.categories.map(c => `<button class="store-cat ${storeCatFilter===c?'on':''}" onclick="storeSetCat('${c}')">${h(c)}</button>`).join('');
1402
+
1403
+ const search = (document.getElementById('store-search')?.value || '').toLowerCase();
1404
+ const filterFn = p => {
1405
+ if (storeCatFilter !== 'all' && p.category !== storeCatFilter) return false;
1406
+ if (search && !(p.name.toLowerCase().includes(search) || p.desc.toLowerCase().includes(search) || p.category.toLowerCase().includes(search))) return false;
1407
+ return true;
1408
+ };
1409
+
1410
+ // featured
1411
+ const featured = storeData.featured.filter(filterFn);
1412
+ document.getElementById('store-featured').innerHTML = featured.length
1413
+ ? featured.map(p => storeCardHTML(p, true)).join('')
1414
+ : `<div class="store-empty" style="grid-column:1/-1"><div class="store-empty-icon">πŸ”</div>Nenhum plugin em destaque corresponde Γ  busca.</div>`;
1415
+
1416
+ // all
1417
+ const all = storeData.plugins.filter(filterFn);
1418
+ document.getElementById('store-all').innerHTML = all.length
1419
+ ? all.map(p => storeCardHTML(p, false)).join('')
1420
+ : `<div class="store-empty" style="grid-column:1/-1"><div class="store-empty-icon">πŸ“¦</div>Nenhum plugin encontrado.</div>`;
1421
+ }
1422
+
1423
+ function storeSetCat(c){ storeCatFilter = c; renderStore(); }
1424
+
1425
+ function storeCardHTML(p, featured){
1426
+ return `
1427
+ <div class="store-card ${featured?'featured':''} ${p.installed?'installed':''}">
1428
+ ${p.installed ? '<span class="store-installed-badge">βœ“ instalado</span>' : ''}
1429
+ <div class="store-card-head">
1430
+ <div class="store-icon">${p.icon || 'πŸ“¦'}</div>
1431
+ <div style="flex:1;min-width:0">
1432
+ <div class="store-name">${h(p.name)}</div>
1433
+ <div class="store-cat-tag">${h(p.category)}</div>
1434
+ </div>
1435
+ </div>
1436
+ <div class="store-desc">${h(p.desc)}</div>
1437
+ <div class="store-meta">
1438
+ <span class="store-rating">β˜… ${p.rating || 'β€”'}</span>
1439
+ <span>${h(p.downloads || '')} downloads</span>
1440
+ </div>
1441
+ <div class="store-actions">
1442
+ ${p.installed
1443
+ ? `<button class="btn btn-ghost btn-sm" onclick="storeUninst('${p.id}')">πŸ—‘οΈ Remover</button>`
1444
+ : `<button class="btn btn-primary btn-sm" id="store-btn-${p.id}" onclick="storeInstall('${p.id}')">⬇️ Instalar</button>`}
1445
+ <a class="btn btn-ghost btn-sm" href="${p.homepage || '#'}" target="_blank" style="text-decoration:none;text-align:center">πŸ”—</a>
1446
+ </div>
1447
+ </div>`;
1448
+ }
1449
+
1450
+ async function storeInstall(id){
1451
+ const btn = document.getElementById('store-btn-' + id);
1452
+ if (btn) { btn.disabled = true; btn.textContent = '⏳ Instalando...'; }
1453
+ toast('⬇️ Instalando plugin... pode levar alguns segundos', 4000);
1454
+ try {
1455
+ const r = await fetch('/api/store/install', {
1456
+ method: 'POST',
1457
+ headers: { 'Content-Type': 'application/json' },
1458
+ body: JSON.stringify({ id }),
1459
+ });
1460
+ const data = await r.json();
1461
+ if (data.ok) {
1462
+ toast('βœ… ' + data.message);
1463
+ await loadStore();
1464
+ } else {
1465
+ toast('❌ ' + (data.message || 'Falha'));
1466
+ if (btn) { btn.disabled = false; btn.textContent = '⬇️ Instalar'; }
1467
+ }
1468
+ } catch (e) {
1469
+ toast('❌ Erro: ' + e.message);
1470
+ if (btn) { btn.disabled = false; btn.textContent = '⬇️ Instalar'; }
1471
+ }
1472
+ }
1473
+
1474
+ async function storeUninst(id){
1475
+ if (!confirm('Remover plugin?')) return;
1476
+ try {
1477
+ const r = await fetch('/api/store/uninstall', {
1478
+ method: 'POST',
1479
+ headers: { 'Content-Type': 'application/json' },
1480
+ body: JSON.stringify({ id }),
1481
+ });
1482
+ const data = await r.json();
1483
+ toast(data.ok ? 'πŸ—‘οΈ ' + data.message : '❌ ' + data.message);
1484
+ await loadStore();
1485
+ } catch (e) { toast('❌ ' + e.message); }
1486
+ }
1487
+
1488
+ // ─── BROWSER USE ───────────────────────────────────────────────────────────
1489
+ async function buStatus(){
1490
+ const el = document.getElementById('bu-status-content');
1491
+ el.innerHTML = '<p style="color:var(--txt2)">Verificando...</p>';
1492
+ try {
1493
+ const r = await fetch('/api/browser-use/status');
1494
+ const d = await r.json();
1495
+ if (d.ready) {
1496
+ el.innerHTML = `<div class="bu-status-ok">βœ… ${d.message}</div>`;
1497
+ } else if (d.installed === false) {
1498
+ el.innerHTML = `<div class="bu-status-warn">⚠️ ${d.message}</div>`;
1499
+ } else {
1500
+ el.innerHTML = `<div class="bu-status-err">❌ ${d.message}</div>`;
1501
+ }
1502
+ } catch (e) {
1503
+ el.innerHTML = `<div class="bu-status-err">❌ Erro ao verificar: ${e.message}</div>`;
1504
+ }
1505
+ }
1506
+
1507
+ async function buInstall(){
1508
+ toast('⬇️ Instalando browser-use + playwright (pode levar 1-2 min)...', 5000);
1509
+ try {
1510
+ const r = await fetch('/api/browser-use/install', { method: 'POST' });
1511
+ const d = await r.json();
1512
+ toast(d.ok ? 'βœ… ' + d.message : '❌ ' + d.message, 5000);
1513
+ buStatus();
1514
+ } catch (e) { toast('❌ ' + e.message); }
1515
+ }
1516
+
1517
+ async function buRun(){
1518
+ const task = document.getElementById('bu-task').value.trim();
1519
+ const url = document.getElementById('bu-url').value.trim();
1520
+ const statusEl = document.getElementById('bu-run-status');
1521
+ const resultEl = document.getElementById('bu-result');
1522
+ if (!task) { toast('⚠️ Descreva a tarefa'); return; }
1523
+ statusEl.textContent = '⏳ Executando... pode levar 1-2 minutos';
1524
+ resultEl.innerHTML = '<div class="bu-result-box">⏳ Aguardando IA terminar a tarefa no browser...</div>';
1525
+ try {
1526
+ const r = await fetch('/api/browser-use/run', {
1527
+ method: 'POST',
1528
+ headers: { 'Content-Type': 'application/json' },
1529
+ body: JSON.stringify({ task, url }),
1530
+ });
1531
+ const d = await r.json();
1532
+ statusEl.textContent = d.ok ? 'βœ… ConcluΓ­do' : '❌ Falhou';
1533
+ resultEl.innerHTML = `<div class="bu-result-box ${d.ok?'':'bu-result-err'}">${h(d.message || 'Sem resposta')}</div>`;
1534
+ } catch (e) {
1535
+ statusEl.textContent = '❌ Erro';
1536
+ resultEl.innerHTML = `<div class="bu-result-box bu-result-err">❌ ${h(e.message)}</div>`;
1537
+ }
1538
+ }
1539
+
1035
1540
  // ─── BOOT ─────────────────────────────────────────────────────────────────
1036
1541
  renderChatList();renderMsgs();updateStatus();
1037
1542
  const bm=MODELS.find(m=>m.id===S.model);
1038
1543
  if(bm)document.getElementById('model-name-badge').textContent=bm.name;
1039
1544
  loadKeys();
1545
+
1546
+ // Quickstart no primeiro uso (web)
1547
+ if (!S.onboarded && !S.model && !Object.values(S.keys||{}).some(Boolean)) {
1548
+ setTimeout(() => openQs(), 500);
1549
+ }
1040
1550
  </script>
1041
1551
  </body>
1042
1552
  </html>