Compare commits

..
5 Commits
Author SHA1 Message Date
hero920103 60d46a4951 更新cursor检测 2026-06-17 00:22:26 +08:00
hero920103 0011b1b558 'update' 2026-06-16 18:14:51 +08:00
hero920103 1b54447ccb fix: build both Intel and Apple Silicon architectures for macOS 2026-06-16 17:10:44 +08:00
hero920103 cb3a661611 fix: resolve win unicode error and install pillow for 2026-06-16 16:45:34 +08:00
hero920103 84cc672823 first commit 2026-06-16 16:30:36 +08:00
14 changed files with 2308 additions and 145 deletions
+5
View File
@@ -0,0 +1,5 @@
# CodeGraph data files — local to each machine, not for committing.
# Ignore everything in .codegraph/ except this file itself, so transient
# files (the database, daemon.pid, sockets, logs) never show up in git.
*
!.gitignore
+189
View File
@@ -0,0 +1,189 @@
name: Build Multi-Platform Release
on:
push:
tags:
- 'v*' # 当推送形如 v1.0.0 的 tag 时触发构建
workflow_dispatch: # 支持手动触发构建
jobs:
# 1. 编译 Windows 端 (.exe)
build-windows:
runs-on: windows-latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pyinstaller
- name: Build EXE
run: |
python -m PyInstaller build.spec --clean
- name: Upload Windows Artifact
uses: actions/upload-artifact@v4
with:
name: CursorTokenLogin-Windows
path: dist/CursorTokenLogin.exe
# 2. 编译 macOS 端 (.app)
build-macos:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-13, macos-latest]
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pyinstaller Pillow
- name: Set Architecture Name
run: |
if [ "${{ matrix.os }}" = "macos-13" ]; then
echo "ARCH_NAME=Intel" >> $GITHUB_ENV
else
echo "ARCH_NAME=AppleSilicon" >> $GITHUB_ENV
fi
shell: bash
- name: Build App Bundle
run: |
python -m PyInstaller build.spec --clean
- name: Zip macOS App
run: |
cd dist
zip -r CursorTokenLogin-macOS-${{ env.ARCH_NAME }}.zip CursorTokenLogin.app
- name: Upload macOS Artifact
uses: actions/upload-artifact@v4
with:
name: CursorTokenLogin-macOS-${{ env.ARCH_NAME }}
path: dist/CursorTokenLogin-macOS-${{ env.ARCH_NAME }}.zip
# 3. 编译 Linux 端并打包为 .deb 和 .rpm
build-linux:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pyinstaller
# 安装打包 deb 和 rpm 所需的系统工具
sudo apt-get update
sudo apt-get install -y dpkg rpm alien
- name: Build Linux Binary
run: |
python -m PyInstaller build.spec --clean
- name: Pack DEB Package
run: |
# 创建 deb 包目录结构
mkdir -p deb-package/DEBIAN
mkdir -p deb-package/usr/bin
mkdir -p deb-package/usr/share/applications
mkdir -p deb-package/usr/share/pixmaps
# 拷贝编译好的二进制文件
cp dist/CursorTokenLogin deb-package/usr/bin/cursortokenlogin
chmod +x deb-package/usr/bin/cursortokenlogin
# 拷贝图标
cp logo.png deb-package/usr/share/pixmaps/cursortokenlogin.png
# 创建控制文件
cat <<EOF > deb-package/DEBIAN/control
Package: cursortokenlogin
Version: 1.0.0
Section: utils
Priority: optional
Architecture: amd64
Maintainer: Yunzer
Description: Cursor Token Login Helper
EOF
# 创建桌面启动快捷方式
cat <<EOF > deb-package/usr/share/applications/cursortokenlogin.desktop
[Desktop Entry]
Name=CursorTokenLogin
Comment=Cursor Token Login Helper
Exec=/usr/bin/cursortokenlogin
Icon=cursortokenlogin
Terminal=false
Type=Application
Categories=Utility;Development;
EOF
# 构建 deb
dpkg-deb --build deb-package cursortokenlogin.deb
- name: Pack RPM Package (Convert from DEB using alien)
run: |
# 使用 alien 工具将 deb 快速转换为 rpm,避免编写复杂的 spec 构建脚本
sudo alien --to-rpm --scripts cursortokenlogin.deb
# 重命名生成的 rpm 文件方便识别
mv *.rpm cursortokenlogin.rpm
- name: Upload Linux Artifacts
uses: actions/upload-artifact@v4
with:
name: CursorTokenLogin-Linux
path: |
cursortokenlogin.deb
cursortokenlogin.rpm
# 4. 自动创建 GitHub Release 并上传 5 个包
create-release:
needs: [build-windows, build-macos, build-linux]
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v') # 仅在推送 tag 时执行 Release 发布
steps:
- name: Download All Artifacts
uses: actions/download-artifact@v4
with:
path: ./release-files
merge-multiple: true
- name: Create Release
uses: softprops/action-gh-release@v2
with:
files: |
release-files/CursorTokenLogin.exe
release-files/CursorTokenLogin-macOS-Intel.zip
release-files/CursorTokenLogin-macOS-AppleSilicon.zip
release-files/cursortokenlogin.deb
release-files/cursortokenlogin.rpm
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+80 -8
View File
@@ -1,13 +1,85 @@
python -m PyInstaller -w -F main.py # 多平台编译与打包指南
<br /> 在 Windows 系统下,由于 PyInstaller 的工作机制是直接打包当前平台的 Python 解析器和系统动态库,**它不支持交叉编译**(即在 Windows 本机无法编译出 macOS 的 `.app` 苹果程序,也无法直接打包 Linux 的 `.deb``.rpm` 安装包)。
# 编译成exe 为了解决这个问题,并在一台电脑上实现 4 端程序(Windows `.exe`、macOS `.app`、Debian/Ubuntu `.deb`、RedHat/Fedora `.rpm`)的统一打包,我们已经在项目中配置了 **GitHub Actions 自动化流水线**
---
## 🚀 推荐方案:使用 GitHub Actions 自动打包 4 端程序
我们已经在项目中创建了自动化打包脚本:[.github/workflows/build.yml](file:///.github/workflows/build.yml)。
您只需将代码托管在 GitHub 仓库中,GitHub 就会为您调用免费的 Windows、macOS 和 Linux 虚拟服务器,一键自动构建出所有安装包!
### 操作步骤:
1. **首次配置并推送代码到 GitHub 仓库** (在本地项目终端运行)
```bash
# 关联远程 GitHub 仓库,并将其命名为 github (实现双仓库并存)
git remote add github https://github.com/hero920103/cursorlogin.git
# 将本地的 master 分支推送到 GitHub
git push -u github master
```
2. **推送 Tag 触发 4 端自动打包** (后续每次需要打包发布时,直接运行下面两行):
```bash
# 1. 本地打上版本 tag
git tag vx.x.x
# 2. 将 tag 单独推送到 github 触发云端自动构建
git push github vx.x.x
```
3. GitHub Actions 收到 Tag 后会自动触发构建,云端会启动多个编译任务,最终产出:
- 💻 **Windows**:编译出 `CursorTokenLogin.exe`
- 🍏 **macOS (M1/M2/M3 芯片)**:编译出 `CursorTokenLogin-macOS-AppleSilicon.zip` (解压即得 `.app`)
- 🍏 **macOS (Intel 芯片)**:编译出 `CursorTokenLogin-macOS-Intel.zip` (解压即得 `.app`)
- 🐧 **Linux**:编译出 Linux 运行文件,并自动打包为 `cursortokenlogin.deb` 和 `cursortokenlogin.rpm`
4. **下载成品**:构建完成后(耗时约 3~5 分钟),GitHub 会自动在您的仓库右侧 **Releases** 栏目中生成一个名为 `v1.0.0` 的发布页,进入即可直接下载编译好的 5 个安装包!
---
## 🛠️ 本地手动构建说明(Windows/Linux/Mac
如果您不想使用 GitHub 托管,也可以在本地不同的系统环境中分别运行打包:
### 1. 编译 Windows 程序 (`.exe`)
在 Windows 命令行中运行:
```bash
python -m PyInstaller build.spec --clean
```
### 2. 编译 macOS 程序 (`.app`)
在 macOS 终端中运行:
```bash
python -m PyInstaller build.spec --clean
```
*(编译生成 `.app` 文件目录,拷贝分发前建议压缩为 `.zip`)*
### 3. 编译 Linux 安装包 (`.deb` / `.rpm`)
在 Ubuntu / Debian 终端下,可以使用以下命令快速打包:
```bash
# 1. 编译 Linux 原生可执行文件
python -m PyInstaller build.spec --clean python -m PyInstaller build.spec --clean
<br /> # 2. 组织 DEB 包目录结构并打包
mkdir -p deb-package/DEBIAN
mkdir -p deb-package/usr/bin
cp dist/CursorTokenLogin deb-package/usr/bin/cursortokenlogin
chmod +x deb-package/usr/bin/cursortokenlogin
# 关于 Win + Mac 同时构建(后续) # 创建 control 配置文件
- PyInstaller 不能在 Windows 本机直接产出 macOS 程序。 cat <<EOF > deb-package/DEBIAN/control
- 当前命令在 Windows 只能生成 `.exe`,在 macOS 才能生成 `.app` Package: cursortokenlogin
- 后续可用 GitHub Actions 做双平台构建:本地打 Windows,云端 `macos-latest` 打 macOS。 Version: 1.0.0
Architecture: amd64
Maintainer: Yunzer
Description: Cursor Token Login Helper
EOF
# 生成 deb 安装包
dpkg-deb --build deb-package cursortokenlogin.deb
# 3. 使用 alien 将 deb 包转换为 rpm 包
sudo apt-get install alien
sudo alien --to-rpm --scripts cursortokenlogin.deb
```
Binary file not shown.
Binary file not shown.
+58
View File
@@ -0,0 +1,58 @@
import sqlite3
from pathlib import Path
p = Path("state.vscdb")
print("db_exists=", p.exists(), p.resolve())
conn = sqlite3.connect(p)
cur = conn.cursor()
tables = cur.execute(
"SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"
).fetchall()
print("tables=", tables)
try:
schema = cur.execute("PRAGMA table_info(ItemTable)").fetchall()
print("ItemTable schema=", schema)
rows = cur.execute(
"""
SELECT key, value
FROM ItemTable
WHERE lower(key) LIKE '%auth%'
OR lower(key) LIKE '%token%'
OR lower(key) LIKE '%cursoraccount%'
OR lower(key) LIKE '%cursor%'
ORDER BY key
"""
).fetchall()
print("matched_rows=", len(rows))
for k, v in rows:
s = "" if v is None else str(v)
masked = (s[:18] + "..." + s[-12:]) if len(s) > 40 else s
print(f"{k} len={len(s)} value={masked}")
print("\ncursorDiskKV schema=", cur.execute("PRAGMA table_info(cursorDiskKV)").fetchall())
disk_rows = cur.execute(
"""
SELECT key, value
FROM cursorDiskKV
WHERE lower(key) LIKE '%auth%'
OR lower(key) LIKE '%token%'
OR lower(key) LIKE '%cursor%'
OR lower(key) LIKE '%account%'
ORDER BY key
"""
).fetchall()
print("cursorDiskKV matched_rows=", len(disk_rows))
for k, v in disk_rows:
if isinstance(v, bytes):
s = v.decode("utf-8", "ignore")
else:
s = "" if v is None else str(v)
masked = (s[:18] + "..." + s[-12:]) if len(s) > 40 else s
print(f"{k} len={len(s)} value={masked}")
finally:
conn.close()
+10 -1
View File
@@ -13,7 +13,7 @@ if not os.path.isfile(_LOGO_ICO):
": %s\n logo.ico build.spec " % _LOGO_ICO ": %s\n logo.ico build.spec " % _LOGO_ICO
) )
_LOGO_ICO_ABS = os.path.abspath(_LOGO_ICO) _LOGO_ICO_ABS = os.path.abspath(_LOGO_ICO)
print("[build.spec] EXE :", _LOGO_ICO_ABS) print("[build.spec] EXE icon path:", _LOGO_ICO_ABS)
block_cipher = None block_cipher = None
@@ -66,3 +66,12 @@ exe = EXE(
# 必须用绝对路径;资源管理器若仍显示旧图标多半是 Windows 图标缓存,可改 exe 文件名或清 IconCache # 必须用绝对路径;资源管理器若仍显示旧图标多半是 Windows 图标缓存,可改 exe 文件名或清 IconCache
icon=_LOGO_ICO_ABS, icon=_LOGO_ICO_ABS,
) )
import sys
if sys.platform == 'darwin':
app = BUNDLE(
exe,
name='CursorTokenLogin.app',
icon='logo.png',
bundle_identifier='com.yunzer.cursortokenlogin',
)
+124
View File
@@ -623,6 +623,11 @@
<string>关于软件</string> <string>关于软件</string>
</property> </property>
</item> </item>
<item>
<property name="text">
<string>无感检测</string>
</property>
</item>
</widget> </widget>
</item> </item>
<item> <item>
@@ -826,6 +831,125 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="pageHelpSilentDetect">
<layout class="QVBoxLayout" name="verticalLayout_helpSilentDetect">
<item>
<widget class="QGroupBox" name="groupHelpSilentDetect">
<property name="title">
<string>无感检测</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_helpSilentDetectContent">
<item>
<widget class="QLabel" name="lblHelpSilentDetectDesc">
<property name="text">
<string>无感检测:不关闭 Cursor 程序,直接写入 Token 和自动生成的随机邮箱地址,方便快速检测 Token 是否可用。</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="txtSilentToken">
<property name="maximumSize">
<size>
<width>150</width>
<height>16777215</height>
</size>
</property>
<property name="placeholderText">
<string>在此输入 ID,例如:11</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblCurrentDetectId">
<property name="text">
<string>当前检测 ID-</string>
</property>
</widget>
</item>
<item>
<widget class="QTextEdit" name="lblCurrentDetectToken">
<property name="readOnly">
<bool>true</bool>
</property>
<property name="html">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'SimSun'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;当前 Token-&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>80</height>
</size>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_silentButtons">
<item>
<widget class="QPushButton" name="btnSilentChange">
<property name="minimumSize">
<size>
<width>0</width>
<height>42</height>
</size>
</property>
<property name="text">
<string>无感换号</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnSilentUnavailable">
<property name="minimumSize">
<size>
<width>0</width>
<height>42</height>
</size>
</property>
<property name="text">
<string>不可用</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btnSilentCopyToken">
<property name="minimumSize">
<size>
<width>0</width>
<height>42</height>
</size>
</property>
<property name="text">
<string>复制 Token</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_helpSilentDetect">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget> </widget>
</item> </item>
</layout> </layout>
+1823 -99
View File
File diff suppressed because it is too large Load Diff
+5 -23
View File
@@ -1,29 +1,11 @@
# -*- mode: python ; coding: utf-8 -*- # -*- mode: python ; coding: utf-8 -*-
import os
try:
_SPEC_ROOT = os.path.dirname(os.path.abspath(SPEC))
except NameError:
_SPEC_ROOT = os.getcwd()
_LOGO_ICO = os.path.normpath(os.path.join(_SPEC_ROOT, "logo.ico"))
if not os.path.isfile(_LOGO_ICO):
raise FileNotFoundError(
": %s\n logo.ico main.spec " % _LOGO_ICO
)
_LOGO_ICO_ABS = os.path.abspath(_LOGO_ICO)
# 须与 build.spec 一致,否则 onefile 内无 layout/main.ui,会出现「找不到 UI 文件」
_datas = [
(os.path.join(os.getcwd(), 'layout'), 'layout'),
(os.path.join(os.getcwd(), 'assets'), 'assets'),
(_LOGO_ICO_ABS, "."),
]
a = Analysis( a = Analysis(
['main.py'], ['main.py'],
pathex=[os.getcwd()], pathex=[],
binaries=[], binaries=[],
datas=_datas, datas=[],
hiddenimports=[], hiddenimports=[],
hookspath=[], hookspath=[],
hooksconfig={}, hooksconfig={},
@@ -44,14 +26,14 @@ exe = EXE(
debug=False, debug=False,
bootloader_ignore_signals=False, bootloader_ignore_signals=False,
strip=False, strip=False,
upx=False, upx=True,
upx_exclude=[], upx_exclude=[],
runtime_tmpdir=r'%LOCALAPPDATA%\CursorTokenLogin\runtime', runtime_tmpdir=None,
console=False, console=False,
disable_windowed_traceback=False, disable_windowed_traceback=False,
argv_emulation=False, argv_emulation=False,
target_arch=None, target_arch=None,
codesign_identity=None, codesign_identity=None,
entitlements_file=None, entitlements_file=None,
icon=_LOGO_ICO_ABS, icon=['logo.ico'],
) )
+1 -1
View File
@@ -1120,7 +1120,7 @@ class MainWindow(QMainWindow):
self.statusBar().addPermanentWidget(QLabel(f"Version: {__VERSION__}")) self.statusBar().addPermanentWidget(QLabel(f"Version: {__VERSION__}"))
self.log("🚀 程序启动成功") self.log("🚀 程序启动成功")
self.log("📋 请先粘贴Token,然后点击换号") # self.log("📋 请先粘贴Token,然后点击换号")
if self._splash: if self._splash:
self._splash.finish(self) self._splash.finish(self)
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.