const fs = require('fs') const express = require('express') const { promisify } = require('node:util') const { exec } = require('node:child_process') const puppeteer = require('puppeteer-core') const freeport = require('freeport') const ProxyChain = require('proxy-chain') const app = express() const freePortPromise = promisify(freeport) const execPromise = promisify(exec) const COOKIE_PATH = `${process.cwd()}/cookies.json` async function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)) } async function getChromiumPath() { if (process.env.PUPPETEER_EXECUTABLE_PATH) { return process.env.PUPPETEER_EXECUTABLE_PATH } const { stdout } = await execPromise('which chromium || which chromium-browser || which google-chrome') if (!stdout.trim()) throw new Error('Chromium not found') return stdout.trim() } async function waitForStableReply(page) { let previousText = '' let stableCount = 0 while (stableCount < 3) { await delay(1000) const currentText = await page.evaluate(() => { const blocks = document.querySelectorAll('.break-anywhere') if (!blocks.length) return '' const last = blocks[blocks.length - 1] const parts = Array.from( last.querySelectorAll('.whitespace-pre-wrap, pre') ) return parts.map(el => el.innerText.trim()).join('\n\n') }) if (currentText && currentText === previousText) { stableCount++ } else { stableCount = 0 } previousText = currentText } return previousText } async function runPi(message) { let browser let proxyServer try { const port = await freePortPromise() proxyServer = new ProxyChain.Server({ port }) await new Promise(resolve => proxyServer.listen(resolve)) const chromiumPath = await getChromiumPath() browser = await puppeteer.launch({ headless: true, executablePath: chromiumPath, args: [ '--no-sandbox', '--disable-dev-shm-usage', `--proxy-server=127.0.0.1:${port}` ] }) const page = await browser.newPage() if (fs.existsSync(COOKIE_PATH)) { const cookies = JSON.parse(fs.readFileSync(COOKIE_PATH, 'utf8')) await page.setCookie(...cookies) } await page.goto('https://pi.ai/talk', { waitUntil: 'networkidle2' }) await page.waitForSelector('#composer-field textarea') await page.type('#composer-field textarea', message, { delay: 5 }) await page.keyboard.press('Enter') await page.waitForSelector('.break-anywhere', { timeout: 30000 }) const aiReply = await waitForStableReply(page) const updatedCookies = await page.cookies() fs.writeFileSync(COOKIE_PATH, JSON.stringify(updatedCookies, null, 2)) return aiReply } finally { if (browser) await browser.close() if (proxyServer) await proxyServer.close(true) } } app.get('/chat', async (req, res) => { try { const message = req.query.message if (!message) { return res.status(400).json({ error: 'message query required' }) } const reply = await runPi(message) res.json({ success: true, reply }) } catch (err) { console.error(err) res.status(500).json({ success: false, error: err.message }) } }) const PORT = process.env.PORT || 7860 app.listen(PORT, '0.0.0.0', () => { console.log(`Running on ${PORT}`) })