UNPKG

grok-api-ts

Version:

TypeScript client for interacting with Grok AI with automated login and cookie management

532 lines 25.3 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.GrokAPI = void 0; const got_scraping_1 = require("got-scraping"); const tough_cookie_1 = require("tough-cookie"); const patchright_1 = require("patchright"); const fs = __importStar(require("fs")); const path = __importStar(require("path")); // A utility function for delay const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms)); class GrokAPI { constructor(cookieStrings, cookiesPath) { this.lastConversationId = 'new'; this.lastResponseId = ''; this.cookiesPath = cookiesPath || path.join(process.cwd(), 'grok-cookies.json'); if (cookieStrings && cookieStrings.length > 0) { this.cookieStrings = cookieStrings; } else { this.cookieStrings = this.loadCookiesFromFile() || []; if (this.cookieStrings.length === 0) { console.log('No cookies found. Please run the login method to authenticate.'); } } } loadCookiesFromFile() { try { if (fs.existsSync(this.cookiesPath)) { const cookiesData = fs.readFileSync(this.cookiesPath, 'utf-8'); return JSON.parse(cookiesData); } } catch (error) { console.error('Error loading cookies from file:', error); } return null; } saveCookiesToFile(cookies) { try { fs.writeFileSync(this.cookiesPath, JSON.stringify(cookies, null, 2), 'utf-8'); console.log('Cookies saved to:', this.cookiesPath); } catch (error) { console.error('Error saving cookies to file:', error); } } /** * Automates the login process using patchright * @param username The Grok username (email) * @param password The Grok password */ async login(username, password) { console.log('Starting automated login process...'); try { const browser = await patchright_1.chromium.launchPersistentContext(path.join(__dirname, '..', '.chrome-data'), { channel: "chrome", headless: false, viewport: null, }); const page = await browser.newPage(); await page.goto('https://accounts.x.ai/sign-in?redirect=grok-com', { waitUntil: 'domcontentloaded' }); console.log('Navigated to X.AI sign-in page'); await page.waitForSelector('button[type="submit"]', { timeout: 30000 }); await page.click('input[data-testid="email"]'); await page.fill('input[data-testid="email"]', username); await page.waitForSelector('input[type="password"]', { timeout: 30000 }); await page.fill('input[type="password"]', password); await page.click('button[type="submit"]'); console.log('Logging in and waiting for redirect to Grok...'); await page.waitForURL(url => url.hostname.includes('grok.com'), { timeout: 60000 }); await delay(3000); const cookies = await browser.cookies(); const essentialCookies = this.extractEssentialCookies(cookies); if (essentialCookies.length === 0) { console.error('Failed to get essential Grok cookies after login'); await browser.close(); return false; } this.cookieStrings = essentialCookies; this.saveCookiesToFile(essentialCookies); console.log('Extracted and saved cookies'); await browser.close(); return true; } catch (error) { console.error('Error during automated login:', error); return false; } } isAuthenticated() { return this.cookieStrings.length > 0; } /** * Ensures the user is authenticated before making API calls * @param username Optional username for login * @param password Optional password for login */ async ensureAuthenticated(username, password) { if (this.isAuthenticated()) { return true; } if (!username || !password) { throw new Error('Authentication required: No valid cookies found and no login credentials provided'); } return this.login(username, password); } async sendMessage(options) { if (!this.isAuthenticated()) { throw new Error('Authentication required: Please call login() method first or provide cookies'); } try { const { message, conversationId = 'new', parentResponseId = '', disableSearch = false, enableImageGeneration = true, customInstructions = "", forceConcise = false, imageAttachments = [], isReasoning = false } = options; const isNewConversation = conversationId === 'new'; const url = isNewConversation ? 'https://grok.com/rest/app-chat/conversations/new' : `https://grok.com/rest/app-chat/conversations/${conversationId}/responses`; console.log(`Sending message to ${isNewConversation ? 'new conversation' : 'existing conversation'}: ${conversationId}`); const response = await got_scraping_1.gotScraping.post(url, { headers: { 'accept': '*/*', 'accept-language': 'en-US,en;q=0.9', 'content-type': 'application/json', 'origin': 'https://grok.com', 'referer': 'https://grok.com/', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', }, cookieJar: this.setCookies(this.cookieStrings, 'https://grok.com'), json: { "temporary": false, "modelName": "grok-3", "message": message, "fileAttachments": [], "imageAttachments": imageAttachments, "disableSearch": disableSearch, "enableImageGeneration": enableImageGeneration, "returnImageBytes": false, "returnRawGrokInXaiRequest": false, "enableImageStreaming": true, "imageGenerationCount": 2, "forceConcise": forceConcise, "toolOverrides": {}, "enableSideBySide": false, "sendFinalMetadata": true, "customInstructions": customInstructions, "deepsearchPreset": "", "isReasoning": isReasoning, ...(isNewConversation ? {} : { "parentResponseId": parentResponseId }) } }); const parsedResponse = this.parseGrokResponse(response.body); if (parsedResponse.conversationId) { this.lastConversationId = parsedResponse.conversationId; } if (parsedResponse.responseId) { this.lastResponseId = parsedResponse.responseId; } return parsedResponse; } catch (error) { console.error('Error sending message to Grok:', error); if (error.response && (error.response.statusCode === 401 || error.response.statusCode === 403)) { console.error('Authentication error: Your session may have expired. Try logging in again.'); this.cookieStrings = []; } throw error; } } async continueConversation(message, options = {}) { if (!this.isAuthenticated()) { throw new Error('Authentication required: Please call login() method first or provide cookies'); } if (this.lastConversationId === 'new') { throw new Error('No active conversation to continue. Start a new conversation first.'); } console.log(`Continuing conversation ${this.lastConversationId} with parent response ${this.lastResponseId}`); try { return await this.sendMessage({ message, conversationId: this.lastConversationId, parentResponseId: this.lastResponseId, ...options }); } catch (error) { if (error.response && (error.response.statusCode === 401 || error.response.statusCode === 403)) { console.error('Failed to continue conversation: Your session may have expired.'); } throw error; } } getConversationInfo() { return { conversationId: this.lastConversationId, lastResponseId: this.lastResponseId }; } parseGrokResponse(responseBody) { const jsonLines = responseBody.trim().split('\n'); let fullMessage = ''; let responseId; let title; let metadata; let modelResponse; let conversationId; for (const line of jsonLines) { try { const parsedLine = JSON.parse(line); if (parsedLine.result && parsedLine.result.conversation && parsedLine.result.conversation.conversationId) { conversationId = parsedLine.result.conversation.conversationId; console.log(`Found conversation ID: ${conversationId}`); } if (parsedLine.result && parsedLine.result.response && parsedLine.result.response.token !== undefined) { fullMessage += parsedLine.result.response.token; responseId = parsedLine.result.response.responseId; } if (parsedLine.result && parsedLine.result.token !== undefined) { fullMessage += parsedLine.result.token || ''; responseId = parsedLine.result.responseId; } if (parsedLine.result && parsedLine.result.response && parsedLine.result.response.finalMetadata) { metadata = parsedLine.result.response.finalMetadata; } else if (parsedLine.result && parsedLine.result.finalMetadata) { metadata = parsedLine.result.finalMetadata; } if (parsedLine.result && parsedLine.result.response && parsedLine.result.response.modelResponse) { modelResponse = parsedLine.result.response.modelResponse; responseId = modelResponse.responseId; } else if (parsedLine.result && parsedLine.result.modelResponse) { modelResponse = parsedLine.result.modelResponse; responseId = modelResponse.responseId; } if (parsedLine.result && parsedLine.result.title) { title = parsedLine.result.title.newTitle; } if (parsedLine.result && parsedLine.result.response && parsedLine.result.response.userResponse && parsedLine.result.response.userResponse.responseId) { responseId = parsedLine.result.response.userResponse.responseId; } else if (parsedLine.result && parsedLine.result.userResponse && parsedLine.result.userResponse.responseId) { responseId = parsedLine.result.userResponse.responseId; } } catch (error) { console.error('Error parsing JSON line:', error); } } if (modelResponse?.message && !fullMessage) { fullMessage = modelResponse.message; } return { fullMessage, responseId, title, metadata, modelResponse, conversationId }; } setCookies(cookieStrings, url) { const cookieJar = new tough_cookie_1.CookieJar(); for (const cookieString of cookieStrings) { const [key, value] = cookieString.split('='); cookieJar.setCookieSync(`${key}=${value}`, url); } return cookieJar; } async sendMessageStream(options, callbacks) { if (!this.isAuthenticated()) { throw new Error('Authentication required: Please call login() method first or provide cookies'); } try { const { message, conversationId = 'new', parentResponseId = '', disableSearch = false, enableImageGeneration = true, customInstructions = "", forceConcise = false, imageAttachments = [], isReasoning = false } = options; const isNewConversation = conversationId === 'new'; const url = isNewConversation ? 'https://grok.com/rest/app-chat/conversations/new' : `https://grok.com/rest/app-chat/conversations/${conversationId}/responses`; console.log(`Streaming message to ${isNewConversation ? 'new conversation' : 'existing conversation'}: ${conversationId}`); // Initialize accumulated response data let fullMessage = ''; let responseId; let title; let metadata; let modelResponse; let conversationId_; let accumulator = ''; // Create a stream request const stream = got_scraping_1.gotScraping.stream.post(url, { headers: { 'accept': '*/*', 'accept-language': 'en-US,en;q=0.9', 'content-type': 'application/json', 'origin': 'https://grok.com', 'referer': 'https://grok.com/', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', }, cookieJar: this.setCookies(this.cookieStrings, 'https://grok.com'), json: { "temporary": false, "modelName": "grok-2", "message": message, "fileAttachments": [], "imageAttachments": imageAttachments, "disableSearch": disableSearch, "enableImageGeneration": enableImageGeneration, "returnImageBytes": false, "returnRawGrokInXaiRequest": false, "enableImageStreaming": true, "imageGenerationCount": 2, "forceConcise": forceConcise, "toolOverrides": {}, "enableSideBySide": false, "sendFinalMetadata": true, "customInstructions": customInstructions, "deepsearchPreset": "", "isReasoning": isReasoning, ...(isNewConversation ? {} : { "parentResponseId": parentResponseId }) } }); // Process the stream data stream.on('data', (chunk) => { const chunkStr = chunk.toString(); accumulator += chunkStr; // Process complete JSON lines let lineEnd = accumulator.indexOf('\n'); while (lineEnd !== -1) { const line = accumulator.substring(0, lineEnd); accumulator = accumulator.substring(lineEnd + 1); try { if (line.trim()) { const parsedLine = JSON.parse(line); // Handle conversation ID if (parsedLine.result?.conversation?.conversationId) { conversationId_ = parsedLine.result.conversation.conversationId; console.log(`Found conversation ID: ${conversationId_}`); } // Handle token and send it via callback let token; if (parsedLine.result?.response?.token !== undefined) { token = parsedLine.result.response.token; responseId = parsedLine.result.response.responseId; } else if (parsedLine.result?.token !== undefined) { token = parsedLine.result.token || ''; responseId = parsedLine.result.responseId; } if (token !== undefined) { fullMessage += token; if (callbacks.onToken) { callbacks.onToken(token); } } // Handle metadata if (parsedLine.result?.response?.finalMetadata) { metadata = parsedLine.result.response.finalMetadata; } else if (parsedLine.result?.finalMetadata) { metadata = parsedLine.result.finalMetadata; } // Handle model response if (parsedLine.result?.response?.modelResponse) { modelResponse = parsedLine.result.response.modelResponse; responseId = modelResponse.responseId; } else if (parsedLine.result?.modelResponse) { modelResponse = parsedLine.result.modelResponse; responseId = modelResponse.responseId; } // Handle title if (parsedLine.result?.title) { title = parsedLine.result.title.newTitle; } // Handle user response if (parsedLine.result?.response?.userResponse?.responseId) { responseId = parsedLine.result.response.userResponse.responseId; } else if (parsedLine.result?.userResponse?.responseId) { responseId = parsedLine.result.userResponse.responseId; } } } catch (error) { console.error('Error parsing JSON line:', error); } lineEnd = accumulator.indexOf('\n'); } }); // Handle the completion of the stream stream.on('end', () => { // Check if there's any data left in the accumulator if (accumulator.trim()) { try { const parsedLine = JSON.parse(accumulator); // Process any remaining tokens or data if (parsedLine.result?.conversation?.conversationId) { conversationId_ = parsedLine.result.conversation.conversationId; } let token; if (parsedLine.result?.response?.token !== undefined) { token = parsedLine.result.response.token; responseId = parsedLine.result.response.responseId; } else if (parsedLine.result?.token !== undefined) { token = parsedLine.result.token || ''; responseId = parsedLine.result.responseId; } if (token !== undefined) { fullMessage += token; if (callbacks.onToken) { callbacks.onToken(token); } } if (parsedLine.result?.response?.finalMetadata) { metadata = parsedLine.result.response.finalMetadata; } else if (parsedLine.result?.finalMetadata) { metadata = parsedLine.result.finalMetadata; } if (parsedLine.result?.response?.modelResponse) { modelResponse = parsedLine.result.response.modelResponse; responseId = modelResponse.responseId; } else if (parsedLine.result?.modelResponse) { modelResponse = parsedLine.result.modelResponse; responseId = modelResponse.responseId; } } catch (error) { console.error('Error parsing final JSON fragment:', error); } } // Use the model response message if we don't have a full message yet if (modelResponse?.message && !fullMessage) { fullMessage = modelResponse.message; } // Store the conversation ID and response ID if (conversationId_) { this.lastConversationId = conversationId_; } if (responseId) { this.lastResponseId = responseId; } // Create the final response object const parsedResponse = { fullMessage, responseId, title, metadata, modelResponse, conversationId: conversationId_ }; // Call the onComplete callback if (callbacks.onComplete) { callbacks.onComplete(parsedResponse); } }); // Handle errors stream.on('error', (error) => { console.error('Error streaming message from Grok:', error); if (error.response && (error.response.statusCode === 401 || error.response.statusCode === 403)) { console.error('Authentication error: Your session may have expired. Try logging in again.'); this.cookieStrings = []; } if (callbacks.onError) { callbacks.onError(error); } }); } catch (error) { console.error('Error setting up streaming request to Grok:', error); if (callbacks.onError) { callbacks.onError(error); } } } extractEssentialCookies(cookies) { const cookieStrings = cookies.map(cookie => `${cookie.name}=${cookie.value}`); return cookieStrings.filter(cookieString => { const name = cookieString.split('=')[0]; return [ // Grok-specific cookies 'x-anonuserid', 'x-challenge', 'x-signature', 'sso', 'sso-rw', // X.AI authentication cookies 'next-auth.session-token', 'next-auth.csrf-token', 'next-auth.callback-url', '__Secure-next-auth.session-token', '__Secure-next-auth.callback-url', '__Host-next-auth.csrf-token' ].includes(name); }); } } exports.GrokAPI = GrokAPI; //# sourceMappingURL=grokApi.js.map