| const os = require('os'); |
| const zlib = require('zlib'); |
| const crypto = require('crypto'); |
| const { v4: uuidv4 } = require('uuid'); |
| const $root = require('../proto/message.js'); |
|
|
| function generateCursorBody(messages, modelName) { |
|
|
| const instruction = messages |
| .filter(msg => msg.role === 'system') |
| .map(msg => msg.content) |
| .join('\n') |
|
|
| const formattedMessages = messages |
| .filter(msg => msg.role !== 'system') |
| .map(msg => ({ |
| content: msg.content, |
| role: msg.role === 'user' ? 1 : 2, |
| messageId: uuidv4(), |
| ...(msg.role === 'user' ? { chatModeEnum: 1 } : {}) |
| |
| })); |
|
|
| const messageIds = formattedMessages.map(msg => { |
| const { role, messageId, summaryId } = msg; |
| return summaryId ? { role, messageId, summaryId } : { role, messageId }; |
| }); |
|
|
| const body = { |
| request:{ |
| messages: formattedMessages, |
| unknown2: 1, |
| instruction: { |
| instruction: instruction |
| }, |
| unknown4: 1, |
| model: { |
| name: modelName, |
| empty: '', |
| }, |
| webTool: "", |
| unknown13: 1, |
| cursorSetting: { |
| name: "cursor\\aisettings", |
| unknown3: "", |
| unknown6: { |
| unknwon1: "", |
| unknown2: "" |
| }, |
| unknown8: 1, |
| unknown9: 1 |
| }, |
| unknown19: 1, |
| |
| conversationId: uuidv4(), |
| metadata: { |
| os: "win32", |
| arch: "x64", |
| version: "10.0.22631", |
| path: "C:\\Program Files\\PowerShell\\7\\pwsh.exe", |
| timestamp: new Date().toISOString(), |
| }, |
| unknown27: 0, |
| |
| messageIds: messageIds, |
| largeContext: 0, |
| unknown38: 0, |
| chatModeEnum: 1, |
| unknown47: "", |
| unknown48: 0, |
| unknown49: 0, |
| unknown51: 0, |
| unknown53: 1, |
| chatMode: "Ask" |
| } |
| }; |
|
|
| const errMsg = $root.StreamUnifiedChatWithToolsRequest.verify(body); |
| if (errMsg) throw Error(errMsg); |
| const instance = $root.StreamUnifiedChatWithToolsRequest.create(body); |
| let buffer = $root.StreamUnifiedChatWithToolsRequest.encode(instance).finish(); |
| let magicNumber = 0x00 |
| if (formattedMessages.length >= 3){ |
| buffer = zlib.gzipSync(buffer) |
| magicNumber = 0x01 |
| } |
|
|
| const finalBody = Buffer.concat([ |
| Buffer.from([magicNumber]), |
| Buffer.from(buffer.length.toString(16).padStart(8, '0'), 'hex'), |
| buffer |
| ]) |
|
|
| return finalBody |
| } |
|
|
| function chunkToUtf8String(chunk) { |
| const results = [] |
| const errorResults = { hasError: false, errorMessage: '' } |
| const buffer = Buffer.from(chunk, 'hex'); |
| |
|
|
| try { |
| for(let i = 0; i < buffer.length; i++){ |
| const magicNumber = parseInt(buffer.subarray(i, i + 1).toString('hex'), 16) |
| const dataLength = parseInt(buffer.subarray(i + 1, i + 5).toString('hex'), 16) |
| const data = buffer.subarray(i + 5, i + 5 + dataLength) |
| |
|
|
| if (magicNumber == 0 || magicNumber == 1) { |
| const gunzipData = magicNumber == 0 ? data : zlib.gunzipSync(data) |
| const response = $root.StreamUnifiedChatWithToolsResponse.decode(gunzipData); |
| const thinking = response?.message?.thinking?.content |
| if (thinking !== undefined){ |
| results.push(thinking); |
| |
| } |
|
|
| const content = response?.message?.content |
| if (content !== undefined){ |
| results.push(content) |
| |
| } |
| |
| } |
| else if (magicNumber == 2 || magicNumber == 3) { |
| |
| const gunzipData = magicNumber == 2 ? data : zlib.gunzipSync(data) |
| const utf8 = gunzipData.toString('utf-8') |
| const message = JSON.parse(utf8) |
|
|
| if (message != null && (typeof message !== 'object' || |
| (Array.isArray(message) ? message.length > 0 : Object.keys(message).length > 0))){ |
| |
| console.error(utf8) |
| |
| |
| if (message && message.error) { |
| errorResults.hasError = true; |
| errorResults.errorMessage = utf8; |
| } |
| } |
| } |
| else { |
| |
| } |
|
|
| i += 5 + dataLength - 1 |
| } |
| } catch (err) { |
| console.log('Error parsing chunk response:', err) |
| } |
|
|
| |
| if (errorResults.hasError) { |
| return { error: errorResults.errorMessage }; |
| } |
|
|
| return results.join('') |
| } |
|
|
| function generateHashed64Hex(input, salt = '') { |
| const hash = crypto.createHash('sha256'); |
| hash.update(input + salt); |
| return hash.digest('hex'); |
| } |
|
|
| function obfuscateBytes(byteArray) { |
| let t = 165; |
| for (let r = 0; r < byteArray.length; r++) { |
| byteArray[r] = (byteArray[r] ^ t) + (r % 256); |
| t = byteArray[r]; |
| } |
| return byteArray; |
| } |
|
|
| function generateCursorChecksum(token) { |
| const machineId = generateHashed64Hex(token, 'machineId'); |
| const macMachineId = generateHashed64Hex(token, 'macMachineId'); |
|
|
| const timestamp = Math.floor(Date.now() / 1e6); |
| const byteArray = new Uint8Array([ |
| (timestamp >> 40) & 255, |
| (timestamp >> 32) & 255, |
| (timestamp >> 24) & 255, |
| (timestamp >> 16) & 255, |
| (timestamp >> 8) & 255, |
| 255 & timestamp, |
| ]); |
|
|
| const obfuscatedBytes = obfuscateBytes(byteArray); |
| const encodedChecksum = Buffer.from(obfuscatedBytes).toString('base64'); |
|
|
| return `${encodedChecksum}${machineId}/${macMachineId}`; |
| } |
|
|
| module.exports = { |
| generateCursorBody, |
| chunkToUtf8String, |
| generateHashed64Hex, |
| generateCursorChecksum |
| }; |
|
|