ask.js
1 import { cli, Strategy } from '@jackwener/opencli/registry'; 2 import { SelectorError } from '@jackwener/opencli/errors'; 3 export const askCommand = cli({ 4 site: 'chatwise', 5 name: 'ask', 6 description: 'Send a prompt and wait for the AI response (send + wait + read)', 7 domain: 'localhost', 8 strategy: Strategy.UI, 9 browser: true, 10 args: [ 11 { name: 'text', required: true, positional: true, help: 'Prompt to send' }, 12 { name: 'timeout', required: false, help: 'Max seconds to wait (default: 30)', default: '30' }, 13 ], 14 columns: ['Role', 'Text'], 15 func: async (page, kwargs) => { 16 const text = kwargs.text; 17 const timeout = parseInt(kwargs.timeout, 10) || 30; 18 // Snapshot content length 19 const beforeLen = await page.evaluate(` 20 (function() { 21 const msgs = document.querySelectorAll('[data-message-id], [class*="message"], [class*="bubble"]'); 22 return msgs.length; 23 })() 24 `); 25 // Send message 26 const injected = await page.evaluate(` 27 (function(text) { 28 let composer = document.querySelector('textarea'); 29 if (!composer) { 30 const editables = Array.from(document.querySelectorAll('[contenteditable="true"]')); 31 composer = editables.length > 0 ? editables[editables.length - 1] : null; 32 } 33 if (!composer) return false; 34 composer.focus(); 35 if (composer.tagName === 'TEXTAREA') { 36 const setter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, 'value').set; 37 setter.call(composer, text); 38 composer.dispatchEvent(new Event('input', { bubbles: true })); 39 } else { 40 document.execCommand('insertText', false, text); 41 } 42 return true; 43 })(${JSON.stringify(text)}) 44 `); 45 if (!injected) 46 throw new SelectorError('ChatWise input element'); 47 await page.wait(0.5); 48 await page.pressKey('Enter'); 49 // Poll for response 50 const pollInterval = 2; 51 const maxPolls = Math.ceil(timeout / pollInterval); 52 let response = ''; 53 for (let i = 0; i < maxPolls; i++) { 54 await page.wait(pollInterval); 55 const result = await page.evaluate(` 56 (function(prevLen) { 57 const msgs = document.querySelectorAll('[data-message-id], [class*="message"], [class*="bubble"]'); 58 if (msgs.length <= prevLen) return null; 59 const last = msgs[msgs.length - 1]; 60 const text = last.innerText || last.textContent; 61 return text ? text.trim() : null; 62 })(${beforeLen}) 63 `); 64 if (result) { 65 response = result; 66 break; 67 } 68 } 69 if (!response) { 70 return [ 71 { Role: 'User', Text: text }, 72 { Role: 'System', Text: `No response within ${timeout}s.` }, 73 ]; 74 } 75 return [ 76 { Role: 'User', Text: text }, 77 { Role: 'Assistant', Text: response }, 78 ]; 79 }, 80 });