Files
cursor-api/tools/set-token/src/main.rs
2025-02-24 08:51:57 +08:00

539 lines
17 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use rusqlite::{Connection, Result};
use serde_json::{Value, from_str, to_string_pretty};
use std::env;
use std::fs;
use std::io::{self, Write};
use std::path::PathBuf;
fn get_cursor_path() -> PathBuf {
let home = if cfg!(windows) {
env::var("USERPROFILE").unwrap_or_else(|_| env::var("HOME").unwrap())
} else {
env::var("HOME").unwrap()
};
let base_path = PathBuf::from(home);
if cfg!(windows) {
base_path.join("AppData\\Roaming\\Cursor")
} else if cfg!(target_os = "macos") {
base_path.join("Library/Application Support/Cursor")
} else {
base_path.join(".config/Cursor")
}
}
fn update_sqlite_tokens(
refresh_token: &str,
access_token: &str,
email: &str,
signup_type: &str,
membership_type: &str,
) -> Result<()> {
let db_path = get_cursor_path().join("User/globalStorage/state.vscdb");
let conn = Connection::open(db_path)?;
// 获取原始值
let mut stmt = conn.prepare(
"SELECT key, value FROM ItemTable WHERE key IN (
'cursorAuth/refreshToken',
'cursorAuth/accessToken',
'cursorAuth/cachedEmail',
'cursorAuth/cachedSignUpType',
'cursorAuth/stripeMembershipType'
)",
)?;
println!("\n原始值:");
let rows = stmt.query_map([], |row| {
Ok((row.get::<_, String>(0)?, row.get::<_, String>(1)?))
})?;
for row in rows {
let (key, value) = row?;
println!("{}: {}", key, value);
}
// 更新值
conn.execute(
"UPDATE ItemTable SET value = ? WHERE key = 'cursorAuth/refreshToken'",
[refresh_token],
)?;
conn.execute(
"UPDATE ItemTable SET value = ? WHERE key = 'cursorAuth/accessToken'",
[access_token],
)?;
conn.execute(
"UPDATE ItemTable SET value = ? WHERE key = 'cursorAuth/cachedEmail'",
[email],
)?;
conn.execute(
"UPDATE ItemTable SET value = ? WHERE key = 'cursorAuth/cachedSignUpType'",
[signup_type],
)?;
conn.execute(
"UPDATE ItemTable SET value = ? WHERE key = 'cursorAuth/stripeMembershipType'",
[membership_type],
)?;
println!("\n更新后的值:");
let mut stmt = conn.prepare(
"SELECT key, value FROM ItemTable WHERE key IN (
'cursorAuth/refreshToken',
'cursorAuth/accessToken',
'cursorAuth/cachedEmail',
'cursorAuth/cachedSignUpType',
'cursorAuth/stripeMembershipType'
)",
)?;
let rows = stmt.query_map([], |row| {
Ok((row.get::<_, String>(0)?, row.get::<_, String>(1)?))
})?;
for row in rows {
let (key, value) = row?;
println!("{}: {}", key, value);
}
Ok(())
}
fn update_storage_json(machine_ids: &[String; 4]) -> io::Result<()> {
let storage_path = get_cursor_path().join("User/globalStorage/storage.json");
let content = fs::read_to_string(&storage_path)?;
let mut json: Value = from_str(&content)?;
if let Value::Object(ref mut map) = json {
map.insert(
"telemetry.macMachineId".to_string(),
Value::String(machine_ids[0].clone()),
);
map.insert(
"telemetry.sqmId".to_string(),
Value::String(machine_ids[1].clone()),
);
map.insert(
"telemetry.machineId".to_string(),
Value::String(machine_ids[2].clone()),
);
map.insert(
"telemetry.devDeviceId".to_string(),
Value::String(machine_ids[3].clone()),
);
}
fs::write(storage_path, to_string_pretty(&json)?)?;
Ok(())
}
fn is_valid_jwt(token: &str) -> bool {
let parts: Vec<&str> = token.split('.').collect();
if parts.len() != 3 {
println!("警告: Token 格式不正确应该包含3个由'.'分隔的部分");
return false;
}
// 检查是否以 "ey" 开头
if !token.starts_with("ey") {
println!("警告: Token 应该以'ey'开头");
return false;
}
true
}
fn is_valid_sha256(id: &str) -> bool {
// SHA256 哈希是64个十六进制字符
if id.len() != 64 {
println!("警告: ID 长度应为64个字符");
return false;
}
// 检查是否都是有效的十六进制字符
if !id.chars().all(|c| c.is_ascii_hexdigit()) {
println!("警告: ID 应只包含十六进制字符(0-9, a-f)");
return false;
}
true
}
fn is_valid_sqm_id(id: &str) -> bool {
// 格式应为 {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} (大写)
if id.len() != 38 {
println!("警告: SQM ID 格式不正确");
return false;
}
if !id.starts_with('{') || !id.ends_with('}') {
println!("警告: SQM ID 应该被花括号包围");
return false;
}
let uuid = &id[1..37];
if !uuid
.chars()
.all(|c| c.is_ascii_uppercase() || c.is_ascii_digit() || c == '-')
{
println!("警告: UUID 部分应为大写字母、数字和连字符");
return false;
}
true
}
fn is_valid_device_id(id: &str) -> bool {
// 格式应为 xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
if id.len() != 36 {
println!("警告: Device ID 格式不正确");
return false;
}
if !id
.chars()
.all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '-')
{
println!("警告: Device ID 应为小写字母、数字和连字符");
return false;
}
true
}
fn is_valid_email(email: &str) -> bool {
if !email.contains('@') || !email.contains('.') {
println!("警告: 邮箱格式不正确");
return false;
}
let parts: Vec<&str> = email.split('@').collect();
if parts.len() != 2 || parts[0].is_empty() || parts[1].is_empty() {
println!("警告: 邮箱格式不正确");
return false;
}
true
}
fn is_valid_uuid(uuid: &str) -> bool {
// UUID格式应为: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
if uuid.len() != 36 {
println!("警告: UUID 格式不正确");
return false;
}
let parts: Vec<&str> = uuid.split('-').collect();
if parts.len() != 5
|| parts[0].len() != 8
|| parts[1].len() != 4
|| parts[2].len() != 4
|| parts[3].len() != 4
|| parts[4].len() != 12
{
println!("警告: UUID 格式不正确");
return false;
}
if !uuid.chars().all(|c| c.is_ascii_hexdigit() || c == '-') {
println!("警告: UUID 应只包含十六进制字符(0-9, a-f)和连字符");
return false;
}
true
}
fn create_uuid_launcher(uuid: &str) -> io::Result<()> {
let tools_dir = get_cursor_path().join("tools/set-token");
fs::create_dir_all(&tools_dir)?;
// 创建 inject.js
let inject_js = format!(
r#"// 保存原始 require
const originalRequire = module.constructor.prototype.require;
// 重写 require 函数
module.constructor.prototype.require = function(path) {{
const result = originalRequire.apply(this, arguments);
// 检测目标模块
if (path.includes('main.js')) {{
// 保存原始函数
const originalModule = result;
// 创建代理对象
return new Proxy(originalModule, {{
get(target, prop) {{
// 拦截 execSync 调用
if (prop === 'execSync') {{
return function() {{
// 返回自定义的 UUID
const platform = process.platform;
switch (platform) {{
case 'darwin':
return 'IOPlatformUUID="{}"';
case 'win32':
return ' HARDWARE\\DESCRIPTION\\System\\BIOS SystemProductID REG_SZ {}';
case 'linux':
case 'freebsd':
return '{}';
default:
throw new Error(`Unsupported platform: ${{platform}}`);
}}
}};
}}
return target[prop];
}}
}});
}}
return result;
}};"#,
uuid, uuid, uuid
);
// 写入 inject.js
fs::write(tools_dir.join("inject.js"), inject_js)?;
if cfg!(windows) {
// 创建 Windows CMD 脚本
let cmd_script = format!(
"@echo off\r\n\
set NODE_OPTIONS=--require \"%~dp0inject.js\"\r\n\
start \"\" \"%LOCALAPPDATA%\\Programs\\Cursor\\Cursor.exe\""
);
fs::write(tools_dir.join("start-cursor.cmd"), cmd_script)?;
// 创建 Windows PowerShell 脚本
let ps_script = format!(
"$env:NODE_OPTIONS = \"--require `\"$PSScriptRoot\\inject.js`\"\"\r\n\
Start-Process -FilePath \"$env:LOCALAPPDATA\\Programs\\Cursor\\Cursor.exe\""
);
fs::write(tools_dir.join("start-cursor.ps1"), ps_script)?;
} else {
// 创建 Shell 脚本
let shell_script = format!(
"#!/bin/bash\n\
SCRIPT_DIR=\"$(cd \"$(dirname \"${{BASH_SOURCE[0]}}\")\" && pwd)\"\n\
export NODE_OPTIONS=\"--require $SCRIPT_DIR/inject.js\"\n\
if [[ \"$OSTYPE\" == \"darwin\"* ]]; then\n\
open -a Cursor\n\
else\n\
cursor # Linux根据实际安装路径调整\n\
fi"
);
let script_path = tools_dir.join("start-cursor.sh");
fs::write(&script_path, shell_script)?;
// 在类Unix系统上设置可执行权限
#[cfg(not(windows))]
{
use std::os::unix::fs::PermissionsExt;
let mut perms = fs::metadata(&script_path)?.permissions();
perms.set_mode(0o755);
fs::set_permissions(&script_path, perms)?;
}
}
println!("\n注入脚本已创建在: {}", tools_dir.display());
println!("\n使用方法:");
if cfg!(windows) {
println!(
"方法1: 双击运行 {}",
tools_dir.join("start-cursor.cmd").display()
);
println!(
"方法2: 在 PowerShell 中运行 {}",
tools_dir.join("start-cursor.ps1").display()
);
} else {
println!(
"在终端中运行: {}",
tools_dir.join("start-cursor.sh").display()
);
}
println!("\n注意:每次启动 Cursor 时都需要使用这个脚本。");
Ok(())
}
fn main() {
loop {
println!("\n请选择操作:");
println!("0. 退出");
println!("1. 更新 Token");
println!("2. 更新设备 ID");
println!("3. 创建自定义UUID启动脚本");
print!("请输入选项 (0-3): ");
io::stdout().flush().unwrap();
let mut choice = String::new();
io::stdin().read_line(&mut choice).unwrap();
match choice.trim() {
"0" => break,
"1" => {
let mut refresh_token = String::new();
loop {
print!("请输入 Refresh Token: ");
io::stdout().flush().unwrap();
refresh_token.clear();
io::stdin().read_line(&mut refresh_token).unwrap();
refresh_token = refresh_token.trim().to_string();
if is_valid_jwt(&refresh_token) {
break;
}
println!("请重新输入正确格式的 Token");
}
print!("Access Token 是否与 Refresh Token 相同? (y/n): ");
io::stdout().flush().unwrap();
let mut same = String::new();
io::stdin().read_line(&mut same).unwrap();
let access_token = if same.trim().eq_ignore_ascii_case("y") {
refresh_token.clone()
} else {
let mut access_token = String::new();
loop {
print!("请输入 Access Token: ");
io::stdout().flush().unwrap();
access_token.clear();
io::stdin().read_line(&mut access_token).unwrap();
access_token = access_token.trim().to_string();
if is_valid_jwt(&access_token) {
break;
}
println!("请重新输入正确格式的 Token");
}
access_token
};
let mut email = String::new();
loop {
print!("请输入邮箱: ");
io::stdout().flush().unwrap();
email.clear();
io::stdin().read_line(&mut email).unwrap();
email = email.trim().to_string();
if is_valid_email(&email) {
break;
}
println!("请重新输入正确格式的邮箱");
}
let mut signup_type = String::new();
loop {
println!("\n可选的注册类型:");
println!("1. Auth_0");
println!("2. Github");
println!("3. Google");
println!("4. unknown");
println!("(WorkOS - 仅供展示,不可选择)");
print!("请选择注册类型 (1-4): ");
io::stdout().flush().unwrap();
signup_type.clear();
io::stdin().read_line(&mut signup_type).unwrap();
let signup_type_str = match signup_type.trim() {
"1" => "Auth_0",
"2" => "Github",
"3" => "Google",
"4" => "unknown",
_ => continue,
}
.to_string();
signup_type = signup_type_str;
break;
}
let mut membership_type = String::new();
loop {
println!("\n可选的会员类型:");
println!("1. free");
println!("2. pro");
println!("3. enterprise");
println!("4. free_trial");
print!("请选择会员类型 (1-4): ");
io::stdout().flush().unwrap();
membership_type.clear();
io::stdin().read_line(&mut membership_type).unwrap();
let membership_type_str = match membership_type.trim() {
"1" => "free",
"2" => "pro",
"3" => "enterprise",
"4" => "free_trial",
_ => continue,
}
.to_string();
membership_type = membership_type_str;
break;
}
match update_sqlite_tokens(
&refresh_token,
&access_token,
&email,
&signup_type,
&membership_type,
) {
Ok(_) => println!("所有信息更新成功!"),
Err(e) => println!("更新失败: {}", e),
}
}
"2" => {
let mut ids = Vec::new();
let validators: [(Box<dyn Fn(&str) -> bool>, &str); 4] = [
(Box::new(is_valid_sha256), "macMachineId"),
(Box::new(is_valid_sqm_id), "sqmId"),
(Box::new(is_valid_sha256), "machineId"),
(Box::new(is_valid_device_id), "devDeviceId"),
];
for (validator, name) in validators.iter() {
loop {
print!("请输入 {}: ", name);
io::stdout().flush().unwrap();
let mut id = String::new();
io::stdin().read_line(&mut id).unwrap();
let id = id.trim().to_string();
if validator(&id) {
ids.push(id);
break;
}
println!("请重新输入正确格式的 ID");
}
}
match update_storage_json(&ids.try_into().unwrap()) {
Ok(_) => println!("设备 ID 更新成功!"),
Err(e) => println!("更新失败: {}", e),
}
}
"3" => {
let mut uuid = String::new();
loop {
print!("请输入自定义 UUID (格式: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx): ");
io::stdout().flush().unwrap();
uuid.clear();
io::stdin().read_line(&mut uuid).unwrap();
uuid = uuid.trim().to_string();
if is_valid_uuid(&uuid) {
break;
}
println!("请重新输入正确格式的 UUID");
}
match create_uuid_launcher(&uuid) {
Ok(_) => println!("启动脚本创建成功!"),
Err(e) => println!("创建失败: {}", e),
}
}
_ => println!("无效选项,请重试"),
}
}
}