Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 254 lines 7.5 kB view raw
1#!/usr/bin/env node 2// KidLisp utility commands - quick actions via CDP 3import http from 'http'; 4import WebSocket from 'ws'; 5 6// Try multiple CDP host/port combinations (same as artery.mjs) 7async function findWorkingCDPHost() { 8 const candidates = [ 9 { host: 'host.docker.internal', port: 9333 }, 10 { host: 'host.docker.internal', port: 9222 }, 11 { host: 'localhost', port: 9333 }, 12 { host: 'localhost', port: 9222 }, 13 { host: '172.17.0.1', port: 9224 }, 14 { host: '172.17.0.1', port: 9223 }, 15 { host: '172.17.0.1', port: 9222 }, 16 ]; 17 18 for (const { host, port } of candidates) { 19 try { 20 const works = await new Promise((resolve) => { 21 const req = http.get({ 22 hostname: host, 23 port: port, 24 path: '/json', 25 timeout: 1000, 26 headers: { 'Host': 'localhost' } 27 }, (res) => { 28 let data = ''; 29 res.on('data', (chunk) => data += chunk); 30 res.on('end', () => resolve(data.length > 0)); 31 }); 32 req.on('error', () => resolve(false)); 33 req.on('timeout', () => { req.destroy(); resolve(false); }); 34 }); 35 if (works) return { host, port }; 36 } catch (e) {} 37 } 38 39 throw new Error('CDP not available - is VS Code running with --remote-debugging-port?'); 40} 41 42async function getTargets() { 43 const { host, port } = await findWorkingCDPHost(); 44 return new Promise((resolve, reject) => { 45 http.get({ 46 hostname: host, 47 port: port, 48 path: '/json', 49 headers: { 'Host': 'localhost' } 50 }, (res) => { 51 let data = ''; 52 res.on('data', chunk => data += chunk); 53 res.on('end', () => { 54 const targets = JSON.parse(data); 55 // Attach the host/port for later use 56 resolve({ targets, host, port }); 57 }); 58 }).on('error', reject); 59 }); 60} 61 62async function connectToKidLisp() { 63 const { targets, host, port } = await getTargets(); 64 const kidlisp = targets.find(t => t.url && t.url.includes('kidlisp.com')); 65 66 if (!kidlisp) { 67 throw new Error('KidLisp window not found. Open it first with: artery K'); 68 } 69 70 let wsUrl = kidlisp.webSocketDebuggerUrl; 71 // Fix localhost if needed (some CDP configs return localhost instead of the actual host) 72 if (wsUrl.includes('localhost')) { 73 wsUrl = wsUrl.replace(/ws:\/\/localhost:(\d+)/, `ws://${host}:${port}`); 74 } 75 76 const ws = new WebSocket(wsUrl); 77 78 await new Promise((resolve, reject) => { 79 ws.on('open', resolve); 80 ws.on('error', reject); 81 setTimeout(() => reject(new Error('Connection timeout')), 5000); 82 }); 83 84 return ws; 85} 86 87let messageIdCounter = 100000; 88 89async function evaluate(ws, expression) { 90 return new Promise((resolve, reject) => { 91 const id = messageIdCounter++; 92 let timer; 93 94 const handler = (data) => { 95 const msg = JSON.parse(data); 96 if (msg.id === id) { 97 clearTimeout(timer); 98 ws.off('message', handler); 99 if (msg.result?.exceptionDetails) { 100 reject(new Error(msg.result.exceptionDetails.exception?.description || 'Error')); 101 } else { 102 resolve(msg.result?.result?.value); 103 } 104 } 105 }; 106 107 ws.on('message', handler); 108 ws.send(JSON.stringify({ 109 id, 110 method: 'Runtime.evaluate', 111 params: { expression, returnByValue: true } 112 })); 113 114 timer = setTimeout(() => { 115 ws.off('message', handler); 116 reject(new Error('Timeout waiting for evaluation result')); 117 }, 10000); 118 }); 119} 120 121// Commands 122const commands = { 123 async 'close-tabs'() { 124 const ws = await connectToKidLisp(); 125 const result = await evaluate(ws, ` 126 (function() { 127 const tabs = document.querySelectorAll('.tab-close'); 128 const count = tabs.length; 129 tabs.forEach(btn => btn.click()); 130 return { closedTabs: count }; 131 })() 132 `); 133 console.log(`✓ Closed ${result.closedTabs} tab(s)`); 134 ws.close(); 135 }, 136 137 async 'list-tabs'() { 138 const ws = await connectToKidLisp(); 139 const tabs = await evaluate(ws, ` 140 Array.from(document.querySelectorAll('.tab')).map(tab => ({ 141 name: tab.querySelector('.tab-name')?.textContent || 'unnamed', 142 active: tab.classList.contains('active') 143 })) 144 `); 145 if (tabs.length === 0) { 146 console.log('No tabs open'); 147 } else { 148 console.log('Open tabs:'); 149 tabs.forEach(t => console.log(` ${t.active ? '→' : ' '} ${t.name}`)); 150 } 151 ws.close(); 152 }, 153 154 async clear() { 155 const ws = await connectToKidLisp(); 156 await evaluate(ws, `monaco.editor.getEditors()[0].setValue('')`); 157 console.log('✓ Editor cleared'); 158 ws.close(); 159 }, 160 161 async stop() { 162 const ws = await connectToKidLisp(); 163 await evaluate(ws, `document.getElementById('stop-button')?.click()`); 164 console.log('✓ Stopped'); 165 ws.close(); 166 }, 167 168 async play() { 169 const ws = await connectToKidLisp(); 170 await evaluate(ws, `document.getElementById('send-button')?.click()`); 171 console.log('✓ Playing'); 172 ws.close(); 173 }, 174 175 async 'clear-console'() { 176 const ws = await connectToKidLisp(); 177 await evaluate(ws, `document.getElementById('console-output').innerHTML = ''`); 178 console.log('✓ Console cleared'); 179 ws.close(); 180 }, 181 182 async theme() { 183 const ws = await connectToKidLisp(); 184 await evaluate(ws, `document.getElementById('theme-toggle')?.click()`); 185 const theme = await evaluate(ws, `document.documentElement.getAttribute('data-theme')`); 186 console.log(`✓ Theme: ${theme}`); 187 ws.close(); 188 }, 189 190 // Card commands 191 async 'card-next'() { 192 const ws = await connectToKidLisp(); 193 await evaluate(ws, `document.getElementById('card-next-btn')?.click()`); 194 console.log('✓ Next card'); 195 ws.close(); 196 }, 197 198 async 'card-prev'() { 199 const ws = await connectToKidLisp(); 200 await evaluate(ws, `document.getElementById('card-prev-btn')?.click()`); 201 console.log('✓ Previous card'); 202 ws.close(); 203 }, 204 205 async 'card-flip'() { 206 const ws = await connectToKidLisp(); 207 await evaluate(ws, `document.getElementById('card-flip-btn')?.click()`); 208 console.log('✓ Card flipped'); 209 ws.close(); 210 }, 211 212 async 'card-spread'() { 213 const ws = await connectToKidLisp(); 214 await evaluate(ws, `document.getElementById('card-spread-btn')?.click()`); 215 console.log('✓ Spread toggled'); 216 ws.close(); 217 }, 218 219 async help() { 220 console.log('KidLisp Utilities'); 221 console.log(''); 222 console.log('Usage: node kidlisp-util.mjs <command>'); 223 console.log(''); 224 console.log('Commands:'); 225 console.log(' close-tabs Close all open tabs'); 226 console.log(' list-tabs List open tabs'); 227 console.log(' clear Clear the editor'); 228 console.log(' stop Stop playback'); 229 console.log(' play Start playback'); 230 console.log(' clear-console Clear the console'); 231 console.log(' theme Toggle theme'); 232 console.log(''); 233 console.log('Card commands:'); 234 console.log(' card-next Go to next card'); 235 console.log(' card-prev Go to previous card'); 236 console.log(' card-flip Flip the top card'); 237 console.log(' card-spread Toggle spread view'); 238 console.log(''); 239 console.log(' help Show this help'); 240 } 241}; 242 243// Main 244const cmd = process.argv[2] || 'help'; 245if (commands[cmd]) { 246 commands[cmd]().catch(e => { 247 console.error('Error:', e.message); 248 process.exit(1); 249 }); 250} else { 251 console.error(`Unknown command: ${cmd}`); 252 commands.help(); 253 process.exit(1); 254}