/ clis / chatwise / ask.js
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  });