grok-api-ts
Version:
TypeScript client for interacting with Grok AI with automated login and cookie management
532 lines • 25.3 kB
JavaScript
;
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