Compare commits
362 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
c5386b51ad | ||
![]() |
5f86b0a76a | ||
![]() |
3a3d490025 | ||
![]() |
abe92608bc | ||
![]() |
24396c5a7a | ||
![]() |
8065efd6af | ||
![]() |
460b2b47a7 | ||
![]() |
7507be7e8b | ||
![]() |
3504d0e5e2 | ||
![]() |
81763a34dd | ||
![]() |
e319c2c972 | ||
![]() |
40d7e844a2 | ||
![]() |
b660ba0080 | ||
![]() |
900a07cc8c | ||
![]() |
31331a8991 | ||
![]() |
aca18c3ea6 | ||
![]() |
57fc804106 | ||
![]() |
70a6c49ba0 | ||
![]() |
730cb66194 | ||
![]() |
fabff7298b | ||
![]() |
421e7fd2da | ||
![]() |
db938c088a | ||
![]() |
9788bcb7e0 | ||
![]() |
9336372d18 | ||
![]() |
0e03ee274e | ||
![]() |
442a90c323 | ||
![]() |
06bc74b261 | ||
![]() |
c4834133c8 | ||
![]() |
c4c1aff308 | ||
![]() |
ab6cbc36bb | ||
![]() |
874dc94792 | ||
![]() |
45354c22d7 | ||
![]() |
1c34aecf45 | ||
![]() |
b984cfae26 | ||
![]() |
0cdceeed83 | ||
![]() |
c5c4b62612 | ||
![]() |
a530e42de6 | ||
![]() |
80079bd4cc | ||
![]() |
dfa1c7ba40 | ||
![]() |
7e566bc768 | ||
![]() |
ad51a92595 | ||
![]() |
7e3239b2dc | ||
![]() |
9d1acba9bb | ||
![]() |
29311545b8 | ||
![]() |
b2a5edef74 | ||
![]() |
9860fb8466 | ||
![]() |
f942fc7f2c | ||
![]() |
fba9857c3f | ||
![]() |
ab6db7d431 | ||
![]() |
e282b95c8a | ||
![]() |
bc450ddc16 | ||
![]() |
4992f775da | ||
![]() |
4396251d47 | ||
![]() |
9a0a1260e0 | ||
![]() |
a864cf9284 | ||
![]() |
f3953cd838 | ||
![]() |
a48185e6a5 | ||
![]() |
c1b7e42e27 | ||
![]() |
776ecb0434 | ||
![]() |
86b2bae098 | ||
![]() |
291c6198bc | ||
![]() |
bc787f2076 | ||
![]() |
9204cef000 | ||
![]() |
363518ad40 | ||
![]() |
d01dcdb7ba | ||
![]() |
234ec180a0 | ||
![]() |
974852944b | ||
![]() |
e5ea1700bd | ||
![]() |
3f7ae1fbf3 | ||
![]() |
e481d97bd2 | ||
![]() |
702bb5d05f | ||
![]() |
a34ff83bca | ||
![]() |
207689ff56 | ||
![]() |
a2d55a00a2 | ||
![]() |
d48d9fa1d0 | ||
![]() |
33cd677188 | ||
![]() |
f6aba31832 | ||
![]() |
f022fc1736 | ||
![]() |
0c59c68340 | ||
![]() |
3501dd3602 | ||
![]() |
9eba036c80 | ||
![]() |
00bbf18ce7 | ||
![]() |
2cc31dd77f | ||
![]() |
ec954b3130 | ||
![]() |
2ae42e3c2a | ||
![]() |
fc5ea3511b | ||
![]() |
4daa24e80a | ||
![]() |
6d53a926e0 | ||
![]() |
d47f127ba3 | ||
![]() |
49f5af4827 | ||
![]() |
b0455d2c46 | ||
![]() |
ff17ed48d7 | ||
![]() |
df9507a433 | ||
![]() |
f4d8ba125c | ||
![]() |
9ec3f0518c | ||
![]() |
f130869f51 | ||
![]() |
2a382d7faa | ||
![]() |
fc4f4b870e | ||
![]() |
42b6c3f159 | ||
![]() |
d6155e5342 | ||
![]() |
f74541b712 | ||
![]() |
b099f4d8a9 | ||
![]() |
d90c9e49d0 | ||
![]() |
3b677e23bb | ||
![]() |
34b45b5dda | ||
![]() |
28de33a5ad | ||
![]() |
febb2f551b | ||
![]() |
f6cd48a7a5 | ||
![]() |
f2e315beac | ||
![]() |
5a8d32a807 | ||
![]() |
7834b85bde | ||
![]() |
6c8a458be2 | ||
![]() |
7b0e701422 | ||
![]() |
4f8f988518 | ||
![]() |
cf20a2a470 | ||
![]() |
56688bdf5d | ||
![]() |
1fe30260b0 | ||
![]() |
b3620ff9f3 | ||
![]() |
26355e2270 | ||
![]() |
a63fcac86b | ||
![]() |
a61be4ba15 | ||
![]() |
f05ab67262 | ||
![]() |
92d88d275d | ||
![]() |
d7044b6ac8 | ||
![]() |
689a0df174 | ||
![]() |
9306749c62 | ||
![]() |
c3ab2b62ee | ||
![]() |
afabbb2247 | ||
![]() |
a9bb8ab031 | ||
![]() |
8bde88472f | ||
![]() |
419dadf308 | ||
![]() |
097f685e2a | ||
![]() |
27b2f28765 | ||
![]() |
f752fa8322 | ||
![]() |
644d663a4e | ||
![]() |
e8e37b3f89 | ||
![]() |
d83c95e413 | ||
![]() |
17897199ac | ||
![]() |
e8bf820c6d | ||
![]() |
706830daff | ||
![]() |
dc1e0fd13c | ||
![]() |
19f4beec47 | ||
![]() |
73773be6d8 | ||
![]() |
dc60f1ef07 | ||
![]() |
68eff9c9d9 | ||
![]() |
875c60738c | ||
![]() |
1c98a590b2 | ||
![]() |
abd809d68b | ||
![]() |
7ed6397075 | ||
![]() |
dc8807f3dc | ||
![]() |
30af54da9c | ||
![]() |
d1d947ae25 | ||
![]() |
e7ec5635d9 | ||
![]() |
37a866800f | ||
![]() |
0b26cd9f4d | ||
![]() |
b07a94f46d | ||
![]() |
bfd4aacecd | ||
![]() |
150eb4ac95 | ||
![]() |
27d9b84604 | ||
![]() |
e59b1130ad | ||
![]() |
f3933dc398 | ||
![]() |
79fed06fd9 | ||
![]() |
11275296b4 | ||
![]() |
11cda5b82d | ||
![]() |
e3da5eadd1 | ||
![]() |
41d6554ac8 | ||
![]() |
4b3d154199 | ||
![]() |
9f42395a3e | ||
![]() |
5d33049d7b | ||
![]() |
fd6872cb2d | ||
![]() |
3f3d0a6533 | ||
![]() |
2bb74b41e0 | ||
![]() |
4407650e67 | ||
![]() |
2cc9adf8b8 | ||
![]() |
8a3ff84f08 | ||
![]() |
b4fd75079c | ||
![]() |
8dff3e96c6 | ||
![]() |
4d68f4bdb7 | ||
![]() |
a1625536a2 | ||
![]() |
5fb473013b | ||
![]() |
3041fb84ca | ||
![]() |
76c7832b38 | ||
![]() |
0c59acaed6 | ||
![]() |
6e773bdd29 | ||
![]() |
89751a79c3 | ||
![]() |
1ac23403e5 | ||
![]() |
88541eaeda | ||
![]() |
2bfac391b3 | ||
![]() |
3970940524 | ||
![]() |
cecccd2c7f | ||
![]() |
f85d60789e | ||
![]() |
e3f6e51984 | ||
![]() |
a5617fb67e | ||
![]() |
45ff3658f2 | ||
![]() |
a8933b848d | ||
![]() |
d86701fd94 | ||
![]() |
d8810042f1 | ||
![]() |
63beebef24 | ||
![]() |
3d832fbd7d | ||
![]() |
d8f7964d36 | ||
![]() |
2a93a856bf | ||
![]() |
60def59221 | ||
![]() |
edc68edddb | ||
![]() |
be4c3a7681 | ||
![]() |
b0950287a3 | ||
![]() |
3d61fa9753 | ||
![]() |
e0639f7419 | ||
![]() |
1c898ec594 | ||
![]() |
719b418b7f | ||
![]() |
fe5c1253e9 | ||
![]() |
e9bcc02a10 | ||
![]() |
15ee4454e8 | ||
![]() |
6254888a50 | ||
![]() |
a66bb1d8ab | ||
![]() |
5f26e0a3c1 | ||
![]() |
252833b228 | ||
![]() |
7e9d937138 | ||
![]() |
5501f2d447 | ||
![]() |
0be607f986 | ||
![]() |
708285a2ef | ||
![]() |
2cfd7e63e5 | ||
![]() |
09a95b999f | ||
![]() |
5a3c1c79a8 | ||
![]() |
1c733d36b1 | ||
![]() |
a76b0ed71c | ||
![]() |
f7345eae36 | ||
![]() |
7568d9f423 | ||
![]() |
63fdf398d6 | ||
![]() |
820d722ac9 | ||
![]() |
b2f07c2eed | ||
![]() |
3c9fe8b9c4 | ||
![]() |
cd238f8913 | ||
![]() |
2b77ea43aa | ||
![]() |
186e79b064 | ||
![]() |
8c3e6a7dd0 | ||
![]() |
e6cfcac976 | ||
![]() |
49c2a8fa21 | ||
![]() |
9df644ddcd | ||
![]() |
7d05540177 | ||
![]() |
ca7efde1b2 | ||
![]() |
d4ac7e2bbf | ||
![]() |
55ec244325 | ||
![]() |
5ec30f9c1e | ||
![]() |
96347d7ea4 | ||
![]() |
54594b701a | ||
![]() |
99c8e55475 | ||
![]() |
479aecdc78 | ||
![]() |
c3c9d778c0 | ||
![]() |
46cf8ac78f | ||
![]() |
5b525c1294 | ||
![]() |
10d06b9987 | ||
![]() |
4b90d906ba | ||
![]() |
9495ba7f71 | ||
![]() |
d0415ff710 | ||
![]() |
25e8b6a28e | ||
![]() |
d6c76e3952 | ||
![]() |
d39b78efd9 | ||
![]() |
50e3f5dd23 | ||
![]() |
4365cd32d2 | ||
![]() |
90e18cd86b | ||
![]() |
b6f664242d | ||
![]() |
a5f51bdbe4 | ||
![]() |
a6508e9d47 | ||
![]() |
7b3ca99f08 | ||
![]() |
57c9c8cc5d | ||
![]() |
82869fb32d | ||
![]() |
a62b7b2361 | ||
![]() |
23fcc59dae | ||
![]() |
229639653a | ||
![]() |
df3e5a534c | ||
![]() |
4683460ed5 | ||
![]() |
15d8210bd4 | ||
![]() |
12468bc363 | ||
![]() |
8f52929e59 | ||
![]() |
d32476d827 | ||
![]() |
a0d84c8290 | ||
![]() |
df71df76e2 | ||
![]() |
397e9d950e | ||
![]() |
dc7761593d | ||
![]() |
d36f868f4e | ||
![]() |
8f40c70504 | ||
![]() |
3050a6c0ad | ||
![]() |
c41ade9b88 | ||
![]() |
2d008db25e | ||
![]() |
bf6adcbac2 | ||
![]() |
f0f77352ae | ||
![]() |
c1d5ba84b3 | ||
![]() |
97e6e5165e | ||
![]() |
b9bd994803 | ||
![]() |
f192037eb5 | ||
![]() |
ff67a07ca7 | ||
![]() |
f90d6d7e5f | ||
![]() |
84cd8c15c7 | ||
![]() |
1e4f2457c2 | ||
![]() |
deb1aafcb5 | ||
![]() |
c4ecb7cede | ||
![]() |
6fb415bfcb | ||
![]() |
f440f47e57 | ||
![]() |
32a30afaeb | ||
![]() |
d7fa7a1442 | ||
![]() |
622f681377 | ||
![]() |
9690adf5a4 | ||
![]() |
5ab9a96c32 | ||
![]() |
01a932f3e5 | ||
![]() |
a8765f85a7 | ||
![]() |
e65c5f5fa8 | ||
![]() |
c681b2473c | ||
![]() |
e0db041e93 | ||
![]() |
dff58fb4fb | ||
![]() |
fa53723283 | ||
![]() |
778f48e57a | ||
![]() |
835bcfc60c | ||
![]() |
1b8aeb4b81 | ||
![]() |
ad7ae6fa58 | ||
![]() |
a9094d34dd | ||
![]() |
ee2532175a | ||
![]() |
0f0d1c7d6f | ||
![]() |
fc9ebdec98 | ||
![]() |
96af6471e4 | ||
![]() |
947d11fbc6 | ||
![]() |
56f09677ca | ||
![]() |
9d41a9a2c7 | ||
![]() |
b3e748554f | ||
![]() |
1f1f4144bb | ||
![]() |
62129aa239 | ||
![]() |
f96a71f909 | ||
![]() |
fa364cb3ce | ||
![]() |
ab4806bc2e | ||
![]() |
98d60390f5 | ||
![]() |
2617a8ece8 | ||
![]() |
e54f1236ec | ||
![]() |
98537b94f9 | ||
![]() |
17c2cb9c2a | ||
![]() |
78ea5908db | ||
![]() |
114ac32618 | ||
![]() |
23295a049d | ||
![]() |
992941fc68 | ||
![]() |
4dc0034165 | ||
![]() |
dae6dbcdaa | ||
![]() |
99e31a7bba | ||
![]() |
d8e2714c1c | ||
![]() |
ab08227350 | ||
![]() |
5ee2265958 | ||
![]() |
cc3388ae10 | ||
![]() |
b694948d3c | ||
![]() |
8d47175bd8 | ||
![]() |
26dd64d8ee | ||
![]() |
791156055f | ||
![]() |
5884f82d66 | ||
![]() |
d45323f316 | ||
![]() |
ed46393aea | ||
![]() |
180c3fd275 | ||
![]() |
3316f8b0b0 | ||
![]() |
9e7d6be7f7 | ||
![]() |
079a7bb9b2 | ||
![]() |
2b2fb6d515 | ||
![]() |
f89aa17acf | ||
![]() |
0d03e6c462 | ||
![]() |
627c2220a6 | ||
![]() |
e3fed51d43 | ||
![]() |
8a28f29202 | ||
![]() |
374b509e98 |
272
.github/workflows/auto-tag-release.yml
vendored
Normal file
@ -0,0 +1,272 @@
|
||||
# This workflow requires Ubuntu 22.04 or 24.04
|
||||
|
||||
name: Auto Tag & Release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
tags:
|
||||
- "v*"
|
||||
paths-ignore:
|
||||
- "**.md"
|
||||
- "LICENSE"
|
||||
- ".gitignore"
|
||||
workflow_call: {}
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
actions: write
|
||||
|
||||
jobs:
|
||||
pre_job:
|
||||
runs-on: ubuntu-22.04
|
||||
outputs:
|
||||
should_skip: ${{ steps.skip_check.outputs.should_skip }}
|
||||
steps:
|
||||
- id: skip_check
|
||||
uses: fkirc/skip-duplicate-actions@v5.3.0
|
||||
with:
|
||||
cancel_others: "true"
|
||||
concurrent_skipping: "same_content"
|
||||
|
||||
auto-tag-release:
|
||||
needs: pre_job
|
||||
if: |
|
||||
needs.pre_job.outputs.should_skip != 'true' ||
|
||||
startsWith(github.ref, 'refs/tags/v')
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 15
|
||||
outputs:
|
||||
version: ${{ steps.get_latest_tag.outputs.version }}
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
lfs: true
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: "1.21"
|
||||
check-latest: true
|
||||
cache: true
|
||||
|
||||
- name: Cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
~/go/pkg/mod
|
||||
~/.cache/git
|
||||
key: ${{ runner.os }}-build-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-build-
|
||||
${{ runner.os }}-
|
||||
|
||||
# 只在非tag推送时执行自动打tag
|
||||
- name: Get latest tag
|
||||
if: ${{ !startsWith(github.ref, 'refs/tags/v') }}
|
||||
id: get_latest_tag
|
||||
run: |
|
||||
set -euo pipefail
|
||||
git fetch --tags --force || {
|
||||
echo "::error::Failed to fetch tags"
|
||||
exit 1
|
||||
}
|
||||
latest_tag=$(git tag -l 'v*' --sort=-v:refname | head -n 1)
|
||||
if [ -z "$latest_tag" ]; then
|
||||
new_version="v0.1.0"
|
||||
else
|
||||
major=$(echo $latest_tag | cut -d. -f1)
|
||||
minor=$(echo $latest_tag | cut -d. -f2)
|
||||
patch=$(echo $latest_tag | cut -d. -f3)
|
||||
new_patch=$((patch + 1))
|
||||
new_version="$major.$minor.$new_patch"
|
||||
fi
|
||||
echo "version=$new_version" >> "$GITHUB_OUTPUT"
|
||||
echo "Generated version: $new_version"
|
||||
|
||||
- name: Validate version
|
||||
if: ${{ !startsWith(github.ref, 'refs/tags/v') }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
new_tag="${{ steps.get_latest_tag.outputs.version }}"
|
||||
echo "Validating version: $new_tag"
|
||||
if [[ ! $new_tag =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
echo "::error::Invalid version format: $new_tag"
|
||||
exit 1
|
||||
fi
|
||||
major=$(echo $new_tag | cut -d. -f1 | tr -d 'v')
|
||||
minor=$(echo $new_tag | cut -d. -f2)
|
||||
patch=$(echo $new_tag | cut -d. -f3)
|
||||
if [[ $major -gt 99 || $minor -gt 99 || $patch -gt 999 ]]; then
|
||||
echo "::error::Version numbers out of valid range"
|
||||
exit 1
|
||||
fi
|
||||
echo "Version validation passed"
|
||||
|
||||
- name: Create new tag
|
||||
if: ${{ !startsWith(github.ref, 'refs/tags/v') }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
new_tag=${{ steps.get_latest_tag.outputs.version }}
|
||||
git config --global user.name 'github-actions[bot]'
|
||||
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
|
||||
git tag -a $new_tag -m "Release $new_tag"
|
||||
git push origin $new_tag
|
||||
|
||||
# 在 Run GoReleaser 之前添加配置检查步骤
|
||||
- name: Check GoReleaser config
|
||||
run: |
|
||||
if [ ! -f ".goreleaser.yml" ] && [ ! -f ".goreleaser.yaml" ]; then
|
||||
echo "::error::GoReleaser configuration file not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 添加依赖检查步骤
|
||||
- name: Check Dependencies
|
||||
run: |
|
||||
go mod verify
|
||||
go mod download
|
||||
# 如果使用 vendor 模式,则执行以下命令
|
||||
if [ -d "vendor" ]; then
|
||||
go mod vendor
|
||||
fi
|
||||
|
||||
# 添加构建环境准备步骤
|
||||
- name: Prepare Build Environment
|
||||
run: |
|
||||
echo "Building version: ${VERSION:-development}"
|
||||
echo "GOOS=${GOOS:-$(go env GOOS)}" >> $GITHUB_ENV
|
||||
echo "GOARCH=${GOARCH:-$(go env GOARCH)}" >> $GITHUB_ENV
|
||||
echo "GO111MODULE=on" >> $GITHUB_ENV
|
||||
|
||||
# 添加清理步骤
|
||||
- name: Cleanup workspace
|
||||
run: |
|
||||
rm -rf /tmp/go/
|
||||
rm -rf .cache/
|
||||
rm -rf dist/
|
||||
git clean -fdx
|
||||
git status
|
||||
|
||||
# 修改 GoReleaser 步骤
|
||||
- name: Run GoReleaser
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/v') || (success() && steps.get_latest_tag.outputs.version != '') }}
|
||||
uses: goreleaser/goreleaser-action@v3
|
||||
with:
|
||||
distribution: goreleaser
|
||||
version: latest
|
||||
args: release --clean --timeout 60m
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
VERSION: ${{ steps.get_latest_tag.outputs.version }}
|
||||
CGO_ENABLED: 0
|
||||
GOPATH: /tmp/go
|
||||
GOROOT: ${{ env.GOROOT }}
|
||||
GOCACHE: /tmp/.cache/go-build
|
||||
GOMODCACHE: /tmp/go/pkg/mod
|
||||
GORELEASER_DEBUG: 1
|
||||
GORELEASER_CURRENT_TAG: ${{ steps.get_latest_tag.outputs.version }}
|
||||
# 添加额外的构建信息
|
||||
BUILD_TIME: ${{ steps.get_latest_tag.outputs.version }}
|
||||
BUILD_COMMIT: ${{ github.sha }}
|
||||
|
||||
# 优化 vendor 同步步骤
|
||||
- name: Sync vendor directory
|
||||
run: |
|
||||
echo "Syncing vendor directory..."
|
||||
go mod tidy
|
||||
go mod vendor
|
||||
go mod verify
|
||||
# 验证 vendor 目录
|
||||
if [ -d "vendor" ]; then
|
||||
echo "Verifying vendor directory..."
|
||||
go mod verify
|
||||
# 检查是否有未跟踪的文件
|
||||
if [ -n "$(git status --porcelain vendor/)" ]; then
|
||||
echo "Warning: Vendor directory has uncommitted changes"
|
||||
git status vendor/
|
||||
fi
|
||||
fi
|
||||
|
||||
# 添加错误检查步骤
|
||||
- name: Check GoReleaser Output
|
||||
if: failure()
|
||||
run: |
|
||||
echo "::group::GoReleaser Debug Info"
|
||||
cat dist/artifacts.json || true
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::GoReleaser Config"
|
||||
cat .goreleaser.yml
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Environment Info"
|
||||
go version
|
||||
go env
|
||||
echo "::endgroup::"
|
||||
|
||||
- name: Set Release Version
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
run: |
|
||||
echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
|
||||
|
||||
# 改进验证步骤
|
||||
- name: Verify Release
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/v') || (success() && steps.get_latest_tag.outputs.version != '') }}
|
||||
run: |
|
||||
echo "Verifying release artifacts..."
|
||||
if [ ! -d "dist" ]; then
|
||||
echo "::error::Release artifacts not found"
|
||||
exit 1
|
||||
fi
|
||||
# 验证生成的二进制文件
|
||||
for file in dist/cursor-id-modifier_*; do
|
||||
if [ -f "$file" ]; then
|
||||
echo "Verifying: $file"
|
||||
if [[ "$file" == *.exe ]]; then
|
||||
# Windows 二进制文件检查
|
||||
if ! [ -x "$file" ]; then
|
||||
echo "::error::$file is not executable"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
# Unix 二进制文件检查
|
||||
if ! [ -x "$file" ]; then
|
||||
echo "::error::$file is not executable"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Notify on failure
|
||||
if: failure()
|
||||
run: |
|
||||
echo "::error::Release process failed"
|
||||
|
||||
# 修改构建摘要步骤
|
||||
- name: Build Summary
|
||||
if: always()
|
||||
run: |
|
||||
echo "## Build Summary" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- Go Version: $(go version)" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- Release Version: ${VERSION:-N/A}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- GPG Signing: Disabled" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- Build Status: ${{ job.status }}" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
if [ -d "dist" ]; then
|
||||
echo "### Generated Artifacts" >> $GITHUB_STEP_SUMMARY
|
||||
ls -lh dist/ | awk '{print "- "$9" ("$5")"}' >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
46
.gitignore
vendored
@ -1,20 +1,44 @@
|
||||
# Binary files
|
||||
# Compiled binary
|
||||
/cursor-id-modifier
|
||||
/cursor-id-modifier.exe
|
||||
|
||||
# Build output directories
|
||||
bin/
|
||||
dist/
|
||||
|
||||
# Go specific
|
||||
go.sum
|
||||
go/
|
||||
.cache/
|
||||
|
||||
# IDE and editor files
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# OS specific
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Build and release artifacts
|
||||
releases/
|
||||
*.syso
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Build directories
|
||||
releases/
|
||||
cursor-id-modifier
|
||||
|
||||
|
||||
# Go specific
|
||||
go.sum
|
||||
# Test files
|
||||
*.test
|
||||
*.out
|
||||
coverage.txt
|
||||
|
||||
# Temporary files
|
||||
*.tmp
|
||||
*~
|
||||
*.bak
|
||||
*.log
|
||||
|
||||
# System files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
.cunzhi*/
|
92
.goreleaser.yml
Normal file
@ -0,0 +1,92 @@
|
||||
version: 2
|
||||
|
||||
before:
|
||||
hooks:
|
||||
- go mod tidy
|
||||
- go mod vendor
|
||||
- go mod verify
|
||||
|
||||
builds:
|
||||
- id: cursor-id-modifier
|
||||
main: ./cmd/cursor-id-modifier/main.go
|
||||
binary: cursor-id-modifier
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
- GO111MODULE=on
|
||||
goos:
|
||||
- linux
|
||||
- windows
|
||||
- darwin
|
||||
goarch:
|
||||
- amd64
|
||||
- arm64
|
||||
- "386"
|
||||
ignore:
|
||||
- goos: darwin
|
||||
goarch: "386"
|
||||
ldflags:
|
||||
- -s -w
|
||||
- -X 'main.version={{.Version}}'
|
||||
- -X 'main.commit={{.ShortCommit}}'
|
||||
- -X 'main.date={{.CommitDate}}'
|
||||
- -X 'main.builtBy=goreleaser'
|
||||
flags:
|
||||
- -trimpath
|
||||
mod_timestamp: '{{ .CommitTimestamp }}'
|
||||
|
||||
archives:
|
||||
- id: binary
|
||||
format: binary
|
||||
name_template: >-
|
||||
{{- .ProjectName }}_
|
||||
{{- .Version }}_
|
||||
{{- .Os }}_
|
||||
{{- if eq .Arch "amd64" }}x86_64
|
||||
{{- else if eq .Arch "386" }}i386
|
||||
{{- else }}{{ .Arch }}{{ end }}
|
||||
builds:
|
||||
- cursor-id-modifier
|
||||
allow_different_binary_count: true
|
||||
files:
|
||||
- none*
|
||||
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
algorithm: sha256
|
||||
|
||||
release:
|
||||
draft: true
|
||||
prerelease: auto
|
||||
mode: replace
|
||||
header: |
|
||||
## Release {{.Tag}} ({{.Date}})
|
||||
|
||||
See [CHANGELOG.md](CHANGELOG.md) for details.
|
||||
footer: |
|
||||
**Full Changelog**: https://github.com/owner/repo/compare/{{ .PreviousTag }}...{{ .Tag }}
|
||||
extra_files:
|
||||
- glob: 'LICENSE*'
|
||||
- glob: 'README*'
|
||||
- glob: 'CHANGELOG*'
|
||||
|
||||
changelog:
|
||||
sort: asc
|
||||
use: github
|
||||
filters:
|
||||
exclude:
|
||||
- '^docs:'
|
||||
- '^test:'
|
||||
- '^ci:'
|
||||
- Merge pull request
|
||||
- Merge branch
|
||||
groups:
|
||||
- title: Features
|
||||
regexp: "^.*feat[(\\w)]*:+.*$"
|
||||
order: 0
|
||||
- title: 'Bug fixes'
|
||||
regexp: "^.*fix[(\\w)]*:+.*$"
|
||||
order: 1
|
||||
- title: Others
|
||||
order: 999
|
||||
|
||||
project_name: cursor-id-modifier
|
28
.vscode/settings.json
vendored
@ -1,28 +0,0 @@
|
||||
{
|
||||
"cSpell.words": [
|
||||
"apprun",
|
||||
"buildmode",
|
||||
"dylib",
|
||||
"endlocal",
|
||||
"errorlevel",
|
||||
"fatih",
|
||||
"gcflags",
|
||||
"GOARCH",
|
||||
"IMAGENAME",
|
||||
"killall",
|
||||
"ldflags",
|
||||
"LOCALAPPDATA",
|
||||
"mktemp",
|
||||
"msys",
|
||||
"pgrep",
|
||||
"pkill",
|
||||
"runas",
|
||||
"setlocal",
|
||||
"taskkill",
|
||||
"tasklist",
|
||||
"trimpath",
|
||||
"urandom",
|
||||
"uuidgen",
|
||||
"xattr"
|
||||
]
|
||||
}
|
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 dacrab
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
661
README.md
@ -3,206 +3,577 @@
|
||||
<div align="center">
|
||||
|
||||
[](https://github.com/yuaotian/go-cursor-help/releases/latest)
|
||||
[](https://github.com/yuaotian/go-cursor-help/blob/main/LICENSE)
|
||||
[](https://github.com/yuaotian/go-cursor-help/blob/master/LICENSE)
|
||||
[](https://github.com/yuaotian/go-cursor-help/stargazers)
|
||||
|
||||
[English](#-english) | [中文](#-chinese)
|
||||
[🌟 English](README.md) | [🌏 中文](README_CN.md) | [🌏 日本語](README_JP.md)
|
||||
|
||||
<img src="https://ai-cursor.com/wp-content/uploads/2024/09/logo-cursor-ai-png.webp" alt="Cursor Logo" width="120"/>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
# 🌟 English
|
||||
> ⚠️ **IMPORTANT NOTICE**
|
||||
>
|
||||
> This tool currently supports:
|
||||
> - ✅ Windows: Latest 1.0.x versions (Supported)
|
||||
> - ✅ Mac/Linux: Latest 1.0.x versions (Supported, feedback welcome)
|
||||
>
|
||||
> Please check your Cursor version before using this tool.
|
||||
|
||||
### 📝 Description
|
||||
<details open>
|
||||
<summary><b>📦 Version History & Downloads</b></summary>
|
||||
|
||||
Resets Cursor's free trial limitation when you see:
|
||||
<div class="version-card" style="background: linear-gradient(135deg, #6e8efb, #a777e3); border-radius: 8px; padding: 15px; margin: 10px 0; color: white;">
|
||||
|
||||
```
|
||||
Too many free trial accounts used on this machine.
|
||||
Please upgrade to pro. We have this limit in place
|
||||
to prevent abuse. Please let us know if you believe
|
||||
this is a mistake.
|
||||
```
|
||||
### 🌟 Latest Versions
|
||||
|
||||
### 💻 System Support
|
||||
[View Full Version History]([CursorHistoryDown.md](https://github.com/oslook/cursor-ai-downloads?tab=readme-ov-file))
|
||||
|
||||
**Windows** ✅ x64
|
||||
**macOS** ✅ Intel & M-series
|
||||
**Linux** ✅ x64 & ARM64
|
||||
</div>
|
||||
|
||||
### 📥 Installation
|
||||
|
||||
#### Automatic Installation
|
||||
|
||||
**Linux/macOS**
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/install.sh | bash -s -- --auto-sudo && rm -f /tmp/cursor_id_modifier_*
|
||||
```
|
||||
</details>
|
||||
|
||||
**Windows** (Run in PowerShell as Admin)
|
||||
```powershell
|
||||
Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/bin/cursor_id_modifier_v2.5.0_windows_amd64.exe')); Remove-Item -Path "$env:TEMP\cursor-id-modifier.exe" -ErrorAction SilentlyContinue
|
||||
```
|
||||
⚠️ **General Solutions for Cursor**
|
||||
> 1. Close Cursor, log out of your account, and delete your account in the official website Settings (refresh IP node: Japan, Singapore, USA, Hong Kong, prioritizing low latency - not necessarily required but change if conditions allow; Windows users are recommended to refresh DNS cache: `ipconfig /flushdns`)
|
||||
> Go to the Cursor official website to delete your current account
|
||||
> Steps: User avatar -> Setting -> Advanced▼ in the bottom left -> Delete Account
|
||||
>
|
||||
> 2. Run the machine code refresh script, see the script address below, available in China
|
||||
>
|
||||
> 3. Re-register an account, log in, and open Cursor to resume normal use.
|
||||
>
|
||||
> 4. Alternative solution: If still unusable after step [**3**], or if you encounter problems such as account registration failure or inability to delete an account, this usually means your browser has been identified or restricted by the target website (risk control). In this case, try switching browsers, such as: Edge, Google Chrome, Firefox. (Or, consider using a browser that can modify or randomize browser fingerprint information).
|
||||
|
||||
#### Manual Method
|
||||
|
||||
1. Close Cursor completely
|
||||
2. Navigate to the configuration file location:
|
||||
- Windows: `%APPDATA%\Cursor\User\globalStorage\storage.json`
|
||||
- macOS: `~/Library/Application Support/Cursor/User/globalStorage/storage.json`
|
||||
- Linux: `~/.config/Cursor/User/globalStorage/storage.json`
|
||||
3. Create a backup of `storage.json`
|
||||
4. Edit `storage.json` and update these fields with new random UUIDs:
|
||||
```json
|
||||
{
|
||||
"telemetry.machineId": "generate-new-uuid",
|
||||
"telemetry.macMachineId": "generate-new-uuid",
|
||||
"telemetry.devDeviceId": "generate-new-uuid",
|
||||
"telemetry.sqmId": "generate-new-uuid",
|
||||
"lastModified": "2024-01-01T00:00:00.000Z",
|
||||
"version": "2.5.0"
|
||||
}
|
||||
```
|
||||
5. Save the file and restart Cursor
|
||||
|
||||
#### Script Method (Alternative)
|
||||
|
||||
If you prefer using scripts directly, you can use these platform-specific scripts:
|
||||
|
||||
**For Linux/macOS:**
|
||||
1. Download the [cursor_modifier.sh](scripts/cursor_modifier.sh)
|
||||
2. Make it executable:
|
||||
```bash
|
||||
chmod +x cursor_modifier.sh
|
||||
```
|
||||
3. Run with sudo:
|
||||
```bash
|
||||
sudo ./cursor_modifier.sh
|
||||
```
|
||||
|
||||
**For Windows:**
|
||||
1. Download the [cursor_modifier.bat](scripts/cursor_modifier.bat)
|
||||
2. Right-click and "Run as administrator"
|
||||
|
||||
These scripts will:
|
||||
- Automatically detect system language (English/Chinese)
|
||||
- Check for and close any running Cursor instances
|
||||
- Generate new random IDs
|
||||
- Update the configuration file
|
||||
- Show the results with a nice UI
|
||||
|
||||
### 🔧 Technical Details
|
||||
|
||||
The program modifies Cursor's `storage.json` config file:
|
||||
- Windows: `%APPDATA%\Cursor\User\globalStorage\`
|
||||
- macOS: `~/Library/Application Support/Cursor/User/globalStorage/`
|
||||
- Linux: `~/.config/Cursor/User/globalStorage/`
|
||||
|
||||
Generates new unique identifiers for:
|
||||
- `telemetry.machineId`
|
||||
- `telemetry.macMachineId`
|
||||
- `telemetry.devDeviceId`
|
||||
- `telemetry.sqmId`
|
||||
|
||||
---
|
||||
|
||||
# 🌏 Chinese
|
||||
⚠️ **MAC Address Modification Warning**
|
||||
>
|
||||
> For Mac users: This script includes a MAC address modification feature that will:
|
||||
> - Modify your network interface's MAC address
|
||||
> - Backup original MAC addresses before modification
|
||||
> - This modification may temporarily affect network connectivity
|
||||
> - You can skip this step when prompted during execution
|
||||
>
|
||||
|
||||
### 📝 问题描述
|
||||
<details >
|
||||
<summary><b>🔒 Disable Auto-Update Feature</b></summary>
|
||||
|
||||
当看到以下提示时重置Cursor试用期:
|
||||
> To prevent Cursor from automatically updating to unsupported new versions, you can choose to disable the auto-update feature.
|
||||
|
||||
#### Method 1: Using Built-in Script (Recommended)
|
||||
|
||||
When running the reset tool, the script will ask if you want to disable auto-updates:
|
||||
```text
|
||||
[Question] Do you want to disable Cursor auto-update feature?
|
||||
0) No - Keep default settings (Press Enter)
|
||||
1) Yes - Disable auto-update
|
||||
```
|
||||
|
||||
Select `1` to automatically complete the disable operation.
|
||||
|
||||
#### Method 2: Manual Disable
|
||||
|
||||
**Windows:**
|
||||
1. Close all Cursor processes
|
||||
2. Delete directory: `%LOCALAPPDATA%\cursor-updater`
|
||||
3. Create a file with the same name (without extension) in the same location
|
||||
|
||||
**macOS:**
|
||||
```bash
|
||||
# NOTE: As tested, this method only works for version 0.45.11 and below.
|
||||
# Close Cursor
|
||||
pkill -f "Cursor"
|
||||
# Replacing app-update.yml with a blank/read-only file
|
||||
cd /Applications/Cursor.app/Contents/Resources
|
||||
mv app-update.yml app-update.yml.bak
|
||||
touch app-update.yml
|
||||
chmod 444 app-update.yml
|
||||
|
||||
# Go to Settings -> Application -> Update, set Mode to none.
|
||||
# This must be done to prevent Cursor from checking for updates.
|
||||
|
||||
# NOTE: The cursor-updater modification method may no longer be effective
|
||||
# In any case, remove update directory and create blocking file
|
||||
rm -rf ~/Library/Application\ Support/Caches/cursor-updater
|
||||
touch ~/Library/Application\ Support/Caches/cursor-updater
|
||||
```
|
||||
|
||||
**Linux:**
|
||||
```bash
|
||||
# Close Cursor
|
||||
pkill -f "Cursor"
|
||||
# Remove update directory and create blocking file
|
||||
rm -rf ~/.config/cursor-updater
|
||||
touch ~/.config/cursor-updater
|
||||
```
|
||||
|
||||
> ⚠️ **Note:** After disabling auto-updates, you'll need to manually download and install new versions. It's recommended to update only after confirming the new version is compatible.
|
||||
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
### 📝 Description
|
||||
|
||||
> When you encounter any of these messages:
|
||||
|
||||
#### Issue 1: Trial Account Limit <p align="right"><a href="#issue1"><img src="https://img.shields.io/badge/Move%20to%20Solution-Blue?style=plastic" alt="Back To Top"></a></p>
|
||||
|
||||
```text
|
||||
Too many free trial accounts used on this machine.
|
||||
Please upgrade to pro. We have this limit in place
|
||||
to prevent abuse. Please let us know if you believe
|
||||
this is a mistake.
|
||||
```
|
||||
|
||||
### 💻 系统支持
|
||||
#### Issue 2: API Key Limitation <p align="right"><a href="#issue2"><img src="https://img.shields.io/badge/Move%20to%20Solution-green?style=plastic" alt="Back To Top"></a></p>
|
||||
|
||||
**Windows** ✅ x64
|
||||
**macOS** ✅ Intel和M系列
|
||||
**Linux** ✅ x64和ARM64
|
||||
```text
|
||||
[New Issue]
|
||||
|
||||
### 📥 安装方法
|
||||
Composer relies on custom models that cannot be billed to an API key.
|
||||
Please disable API keys and use a Pro or Business subscription.
|
||||
Request ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
```
|
||||
|
||||
#### 自动安装
|
||||
#### Issue 3: Trial Request Limit
|
||||
|
||||
> This indicates you've reached the usage limit during the VIP free trial period:
|
||||
|
||||
```text
|
||||
You've reached your trial request limit.
|
||||
```
|
||||
|
||||
#### Issue 4: Claude 3.7 High Load <p align="right"><a href="#issue4"><img src="https://img.shields.io/badge/Move%20to%20Solution-purple?style=plastic" alt="Back To Top"></a></p>
|
||||
|
||||
```text
|
||||
High Load
|
||||
We're experiencing high demand for Claude 3.7 Sonnet right now. Please upgrade to Pro, or switch to the
|
||||
'default' model, Claude 3.5 sonnet, another model, or try again in a few moments.
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
<p id="issue2"></p>
|
||||
|
||||
#### Solution : Uninstall Cursor Completely And Reinstall (API key Issue)
|
||||
|
||||
1. Download [Geek.exe Uninstaller[Free]](https://geekuninstaller.com/download)
|
||||
2. Uninstall Cursor app completely
|
||||
3. Re-Install Cursor app
|
||||
4. Continue to Solution 1
|
||||
|
||||
<br>
|
||||
|
||||
<p id="issue1"></p>
|
||||
|
||||
> Temporary Solution:
|
||||
|
||||
#### Solution 1: Quick Reset (Recommended)
|
||||
|
||||
1. Close Cursor application
|
||||
2. Run the machine code reset script (see installation instructions below)
|
||||
3. Reopen Cursor to continue using
|
||||
|
||||
#### Solution 2: Account Switch
|
||||
|
||||
1. File -> Cursor Settings -> Sign Out
|
||||
2. Close Cursor
|
||||
3. Run the machine code reset script
|
||||
4. Login with a new account
|
||||
|
||||
#### Solution 3: Network Optimization
|
||||
|
||||
If the above solutions don't work, try:
|
||||
|
||||
- Switch to low-latency nodes (Recommended regions: Japan, Singapore, US, Hong Kong)
|
||||
- Ensure network stability
|
||||
- Clear browser cache and retry
|
||||
|
||||
#### Solution 4: Claude 3.7 Access Issue (High Load)
|
||||
|
||||
If you see the "High Load" message for Claude 3.7 Sonnet, this indicates Cursor is limiting free trial accounts from using the 3.7 model during certain times of the day. Try:
|
||||
|
||||
1. Switch to a new account created with Gmail, possibly connecting through a different IP address
|
||||
2. Try accessing during off-peak hours (typically 5-10 AM or 3-7 PM when restrictions are often lighter)
|
||||
3. Consider upgrading to Pro for guaranteed access
|
||||
4. Use Claude 3.5 Sonnet as a fallback option
|
||||
|
||||
> Note: These access patterns may change as Cursor adjusts their resource allocation policies.
|
||||
|
||||
### 💻 System Support
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
**Windows** ✅
|
||||
|
||||
- x64 (64-bit)
|
||||
- x86 (32-bit)
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
**macOS** ✅
|
||||
|
||||
- Intel (x64)
|
||||
- Apple Silicon (M1/M2)
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
**Linux** ✅
|
||||
|
||||
- x64 (64-bit)
|
||||
- x86 (32-bit)
|
||||
- ARM64
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
### 🚀 One-Click Solution
|
||||
|
||||
<details open>
|
||||
<summary><b>Global Users</b></summary>
|
||||
|
||||
**macOS**
|
||||
|
||||
**Linux/macOS**
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/install.sh | bash -s -- --auto-sudo && rm -f /tmp/cursor_id_modifier_*
|
||||
# Method two
|
||||
curl -fsSL https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_mac_id_modifier.sh -o ./cursor_mac_id_modifier.sh && sudo bash ./cursor_mac_id_modifier.sh && rm ./cursor_mac_id_modifier.sh
|
||||
```
|
||||
|
||||
**Windows** (以管理员身份运行PowerShell)
|
||||
**Linux**
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_linux_id_modifier.sh | sudo bash
|
||||
```
|
||||
|
||||
> **Note for Linux users:** The script attempts to find your Cursor installation by checking common paths (`/usr/bin`, `/usr/local/bin`, `$HOME/.local/bin`, `/opt/cursor`, `/snap/bin`), using the `which cursor` command, and searching within `/usr`, `/opt`, and `$HOME/.local`. If Cursor is installed elsewhere or not found via these methods, the script may fail. Ensure Cursor is accessible via one of these standard locations or methods.
|
||||
|
||||
**Windows**
|
||||
|
||||
```powershell
|
||||
Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/bin/cursor_id_modifier_v2.5.0_windows_amd64.exe')); Remove-Item -Path "$env:TEMP\cursor-id-modifier.exe" -ErrorAction SilentlyContinue
|
||||
irm https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex
|
||||
```
|
||||
|
||||
#### 手动方法
|
||||
**Windows (Enhanced Version)**
|
||||
|
||||
1. 完全关闭 Cursor
|
||||
2. 找到配置文件位置:
|
||||
- Windows: `%APPDATA%\Cursor\User\globalStorage\storage.json`
|
||||
- macOS: `~/Library/Application Support/Cursor/User/globalStorage/storage.json`
|
||||
- Linux: `~/.config/Cursor/User/globalStorage/storage.json`
|
||||
3. 备份 `storage.json`
|
||||
4. 编辑 `storage.json` 并更新以下字段(使用新的随机UUID):
|
||||
```json
|
||||
{
|
||||
"telemetry.machineId": "生成新的uuid",
|
||||
"telemetry.macMachineId": "生成新的uuid",
|
||||
"telemetry.devDeviceId": "生成新的uuid",
|
||||
"telemetry.sqmId": "生成新的uuid",
|
||||
"lastModified": "2024-01-01T00:00:00.000Z",
|
||||
"version": "2.5.0"
|
||||
}
|
||||
```
|
||||
5. 保存文件并重启 Cursor
|
||||
```powershell
|
||||
irm https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex
|
||||
```
|
||||
> Enhanced Cursor machine code modifier with dual-mode operation and trial reset functionality
|
||||
|
||||
#### 脚本方法(替代方法)
|
||||
<div align="center">
|
||||
<img src="img/run_success.png" alt="Run Success" width="600"/>
|
||||
</div>
|
||||
|
||||
如果您喜欢直接使用脚本,可以使用这些特定平台的脚本:
|
||||
</details>
|
||||
|
||||
**适用于 Linux/macOS:**
|
||||
1. 下载 [cursor_modifier.sh](scripts/cursor_modifier.sh)
|
||||
2. 使其可执行:
|
||||
```bash
|
||||
chmod +x cursor_modifier.sh
|
||||
```
|
||||
3. 用 sudo 运行
|
||||
```bash
|
||||
sudo ./cursor_modifier.sh
|
||||
```
|
||||
<details open>
|
||||
<summary><b>China Users (Recommended)</b></summary>
|
||||
|
||||
**适用于 Windows:**
|
||||
1. 下载 [cursor_modifier.bat](脚本/cursor_modifier.bat)
|
||||
2. 右键单击并 “以管理员身份运行”。
|
||||
**macOS**
|
||||
|
||||
这些脚本将
|
||||
- 自动检测系统语言(英语/中文)
|
||||
- 检查并关闭任何正在运行的光标实例
|
||||
- 生成新的随机 ID
|
||||
- 更新配置文件
|
||||
- 以漂亮的用户界面显示结果
|
||||
```bash
|
||||
curl -fsSL https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_mac_id_modifier.sh -o ./cursor_mac_id_modifier.sh && sudo bash ./cursor_mac_id_modifier.sh && rm ./cursor_mac_id_modifier.sh
|
||||
```
|
||||
|
||||
### 🔧 技术细节
|
||||
**Linux**
|
||||
|
||||
程序修改Cursor的`storage.json`配置文件:
|
||||
- Windows: `%APPDATA%\Cursor\User\globalStorage\`
|
||||
- macOS: `~/Library/Application Support/Cursor/User/globalStorage/`
|
||||
- Linux: `~/.config/Cursor/User/globalStorage/`
|
||||
```bash
|
||||
curl -fsSL https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_linux_id_modifier.sh | sudo bash
|
||||
```
|
||||
|
||||
**Windows**
|
||||
|
||||
```powershell
|
||||
irm https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex
|
||||
```
|
||||
|
||||
**Windows (Enhanced Version)**
|
||||
|
||||
```powershell
|
||||
irm https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex
|
||||
```
|
||||
> Enhanced Cursor machine code modifier with dual-mode operation and trial reset functionality
|
||||
|
||||
</details>
|
||||
|
||||
<details open>
|
||||
<summary><b>Windows Terminal Run and Configuration</b></summary>
|
||||
|
||||
#### How to Open Administrator Terminal in Windows:
|
||||
|
||||
##### Method 1: Using Win + X Shortcut
|
||||
```md
|
||||
1. Press Win + X key combination
|
||||
2. Select one of these options from the menu:
|
||||
- "Windows PowerShell (Administrator)"
|
||||
- "Windows Terminal (Administrator)"
|
||||
- "Terminal (Administrator)"
|
||||
(Options may vary depending on Windows version)
|
||||
```
|
||||
|
||||
##### Method 2: Using Win + R Run Command
|
||||
```md
|
||||
1. Press Win + R key combination
|
||||
2. Type powershell or pwsh in the Run dialog
|
||||
3. Press Ctrl + Shift + Enter to run as administrator
|
||||
or type in the opened window: Start-Process pwsh -Verb RunAs
|
||||
4. Enter the reset script in the administrator terminal:
|
||||
|
||||
irm https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex
|
||||
```
|
||||
|
||||
For the enhanced version:
|
||||
```powershell
|
||||
irm https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex
|
||||
```
|
||||
|
||||
##### Method 3: Using Search
|
||||
>
|
||||
>
|
||||
>Type pwsh in the search box, right-click and select "Run as administrator"
|
||||
>
|
||||
|
||||
Enter the reset script in the administrator terminal:
|
||||
```powershell
|
||||
irm https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex
|
||||
```
|
||||
|
||||
For the enhanced version:
|
||||
```powershell
|
||||
irm https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex
|
||||
```
|
||||
|
||||
### 🔧 PowerShell Installation Guide
|
||||
|
||||
If PowerShell is not installed on your system, you can install it using one of these methods:
|
||||
|
||||
#### Method 1: Install via Winget (Recommended)
|
||||
|
||||
1. Open Command Prompt or PowerShell
|
||||
2. Run the following command:
|
||||
```powershell
|
||||
winget install --id Microsoft.PowerShell --source winget
|
||||
```
|
||||
|
||||
#### Method 2: Manual Installation
|
||||
|
||||
1. Download the installer for your system:
|
||||
- [PowerShell-7.4.6-win-x64.msi](https://github.com/PowerShell/PowerShell/releases/download/v7.4.6/PowerShell-7.4.6-win-x64.msi) (64-bit systems)
|
||||
- [PowerShell-7.4.6-win-x86.msi](https://github.com/PowerShell/PowerShell/releases/download/v7.4.6/PowerShell-7.4.6-win-x86.msi) (32-bit systems)
|
||||
- [PowerShell-7.4.6-win-arm64.msi](https://github.com/PowerShell/PowerShell/releases/download/v7.4.6/PowerShell-7.4.6-win-arm64.msi) (ARM64 systems)
|
||||
|
||||
2. Double-click the downloaded installer and follow the installation prompts
|
||||
|
||||
> 💡 If you encounter any issues, please refer to the [Microsoft Official Installation Guide](https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-windows)
|
||||
|
||||
</details>
|
||||
|
||||
#### Windows 安装特性:
|
||||
|
||||
- 🔍 Automatically detects and uses PowerShell 7 if available
|
||||
- 🛡️ Requests administrator privileges via UAC prompt
|
||||
- 📝 Falls back to Windows PowerShell if PS7 isn't found
|
||||
- 💡 Provides manual instructions if elevation fails
|
||||
|
||||
That's it! The script will:
|
||||
|
||||
1. ✨ Install the tool automatically
|
||||
2. 🔄 Reset your Cursor trial immediately
|
||||
|
||||
### 📦 Manual Installation
|
||||
|
||||
> Download the appropriate file for your system from [releases](https://github.com/yuaotian/go-cursor-help/releases/latest)
|
||||
|
||||
<details>
|
||||
<summary>Windows Packages</summary>
|
||||
|
||||
- 64-bit: `cursor-id-modifier_windows_x64.exe`
|
||||
- 32-bit: `cursor-id-modifier_windows_x86.exe`
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>macOS Packages</summary>
|
||||
|
||||
- Intel: `cursor-id-modifier_darwin_x64_intel`
|
||||
- M1/M2: `cursor-id-modifier_darwin_arm64_apple_silicon`
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Linux Packages</summary>
|
||||
|
||||
- 64-bit: `cursor-id-modifier_linux_x64`
|
||||
- 32-bit: `cursor-id-modifier_linux_x86`
|
||||
- ARM64: `cursor-id-modifier_linux_arm64`
|
||||
</details>
|
||||
|
||||
### 🔧 Technical Details
|
||||
|
||||
<details>
|
||||
<summary><b>Configuration Files</b></summary>
|
||||
|
||||
The program modifies Cursor's `storage.json` config file located at:
|
||||
|
||||
- Windows: `%APPDATA%\Cursor\User\globalStorage\storage.json`
|
||||
- macOS: `~/Library/Application Support/Cursor/User/globalStorage/storage.json`
|
||||
- Linux: `~/.config/Cursor/User/globalStorage/storage.json`
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Modified Fields</b></summary>
|
||||
|
||||
The tool generates new unique identifiers for:
|
||||
|
||||
生成新的唯一标识符:
|
||||
- `telemetry.machineId`
|
||||
- `telemetry.macMachineId`
|
||||
- `telemetry.devDeviceId`
|
||||
- `telemetry.sqmId`
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Manual Auto-Update Disable</b></summary>
|
||||
|
||||
Windows users can manually disable the auto-update feature:
|
||||
|
||||
1. Close all Cursor processes
|
||||
2. Delete directory: `C:\Users\username\AppData\Local\cursor-updater`
|
||||
3. Create a file with the same name: `cursor-updater` (without extension)
|
||||
|
||||
macOS/Linux users can try to locate similar `cursor-updater` directory in their system and perform the same operation.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Safety Features</b></summary>
|
||||
|
||||
- ✅ Safe process termination
|
||||
- ✅ Atomic file operations
|
||||
- ✅ Error handling and recovery
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>Registry Modification Notice</b></summary>
|
||||
|
||||
> ⚠️ **Important: This tool modifies the Windows Registry**
|
||||
|
||||
#### Modified Registry
|
||||
- Path: `Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography`
|
||||
- Key: `MachineGuid`
|
||||
|
||||
#### Potential Impact
|
||||
Modifying this registry key may affect:
|
||||
- Windows system's unique device identification
|
||||
- Device recognition and authorization status of certain software
|
||||
- System features based on hardware identification
|
||||
|
||||
#### Safety Measures
|
||||
1. Automatic Backup
|
||||
- Original value is automatically backed up before modification
|
||||
- Backup location: `%APPDATA%\Cursor\User\globalStorage\backups`
|
||||
- Backup file format: `MachineGuid.backup_YYYYMMDD_HHMMSS`
|
||||
|
||||
2. Manual Recovery Steps
|
||||
- Open Registry Editor (regedit)
|
||||
- Navigate to: `Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography`
|
||||
- Right-click on `MachineGuid`
|
||||
- Select "Modify"
|
||||
- Paste the value from backup file
|
||||
|
||||
#### Important Notes
|
||||
- Verify backup file existence before modification
|
||||
- Use backup file to restore original value if needed
|
||||
- Administrator privileges required for registry modification
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
### 📚 Recommended Reading
|
||||
|
||||
- [Cursor Issues Collection and Solutions](https://mp.weixin.qq.com/s/pnJrH7Ifx4WZvseeP1fcEA)
|
||||
- [AI Universal Development Assistant Prompt Guide](https://mp.weixin.qq.com/s/PRPz-qVkFJSgkuEKkTdzwg)
|
||||
|
||||
---
|
||||
|
||||
## 💬 Feedback & Suggestions
|
||||
|
||||
We value your feedback on the new enhanced script! If you've tried the `cursor_win_id_modifier.ps1` script, please share your experience:
|
||||
|
||||
- 🐛 **Bug Reports**: Found any issues? Let us know!
|
||||
- 💡 **Feature Suggestions**: Have ideas for improvements?
|
||||
- ⭐ **Success Stories**: Share how the tool helped you!
|
||||
- 🔧 **Technical Feedback**: Performance, compatibility, or usability insights
|
||||
|
||||
Your feedback helps us improve the tool for everyone. Feel free to open an issue or contribute to the project!
|
||||
|
||||
---
|
||||
|
||||
## Support
|
||||
|
||||
<div align="center">
|
||||
<b>If you find this helpful, consider buying me a spicy gluten snack (Latiao) as appreciation~ 💁☕️</b>
|
||||
<table>
|
||||
<tr>
|
||||
|
||||
<td align="center">
|
||||
<b>微信赞赏</b><br>
|
||||
<img src="img/wx_zsm2.png" width="500" alt="微信赞赏码"><br>
|
||||
<small>要到饭咧?啊咧?啊咧?不给也没事~ 请随意打赏</small>
|
||||
</td>
|
||||
<td align="center">
|
||||
<b>支付宝赞赏</b><br>
|
||||
<img src="img/alipay.png" width="500" alt="支付宝赞赏码"><br>
|
||||
<small>如果觉得有帮助,来包辣条犒劳一下吧~</small>
|
||||
</td>
|
||||
<td align="center">
|
||||
<b>Alipay</b><br>
|
||||
<img src="img/alipay_scan_pay.jpg" width="500" alt="Alipay"><br>
|
||||
<em>1 Latiao = 1 AI thought cycle</em>
|
||||
</td>
|
||||
<td align="center">
|
||||
<b>WeChat</b><br>
|
||||
<img src="img/qun-16.png" width="500" alt="WeChat"><br>
|
||||
<em>二维码7天内(8月4日前前)有效,过期请加微信</em>
|
||||
</td>
|
||||
<!-- <td align="center">
|
||||
<b>ETC</b><br>
|
||||
<img src="img/etc.png" width="100" alt="ETC Address"><br>
|
||||
ETC: 0xa2745f4CD5d32310AC01694ABDB28bA32D125a6b
|
||||
</td>
|
||||
<td align="center"> -->
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## ⭐ Project Stats
|
||||
|
||||
<div align="center">
|
||||
|
||||
[](https://star-history.com/#yuaotian/go-cursor-help&Date)
|
||||
|
||||

|
||||
|
||||
</div>
|
||||
|
||||
## 📄 License
|
||||
|
||||
MIT License
|
||||
<details>
|
||||
<summary><b>MIT License</b></summary>
|
||||
|
||||
Copyright (c) 2024
|
||||
|
||||
@ -216,3 +587,5 @@ furnished to do so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
</details>
|
||||
|
||||
|
535
README_CN.md
Normal file
@ -0,0 +1,535 @@
|
||||
# 🚀 Cursor 免费试用重置工具
|
||||
|
||||
<div align="center">
|
||||
|
||||
[](https://github.com/yuaotian/go-cursor-help/releases/latest)
|
||||
[](https://github.com/yuaotian/go-cursor-help/blob/master/LICENSE)
|
||||
[](https://github.com/yuaotian/go-cursor-help/stargazers)
|
||||
|
||||
[🌟 English](README.md) | [🌏 中文](README_CN.md) | [🌏 日本語](README_JP.md)
|
||||
|
||||
<img src="https://ai-cursor.com/wp-content/uploads/2024/09/logo-cursor-ai-png.webp" alt="Cursor Logo" width="120"/>
|
||||
|
||||
|
||||
|
||||
> ⚠️ **重要提示**
|
||||
>
|
||||
> 本工具当前支持版本:
|
||||
> - ✅ Windows: 最新的 1.0.x 版本(已支持)
|
||||
> - ✅ Mac/Linux: 最新的 1.0.x 版本(已支持,欢迎测试并反馈问题)
|
||||
|
||||
> 使用前请确认您的 Cursor 版本。
|
||||
|
||||
<details open>
|
||||
<summary><b>📦 版本历史与下载</b></summary>
|
||||
|
||||
<div class="version-card" style="background: linear-gradient(135deg, #6e8efb, #a777e3); border-radius: 8px; padding: 15px; margin: 10px 0; color: white;">
|
||||
|
||||
|
||||
[查看完整版本历史]([CursorHistoryDown.md](https://github.com/oslook/cursor-ai-downloads?tab=readme-ov-file))
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</details>
|
||||
|
||||
⚠️ **Cursor通用解决方案**
|
||||
> 1. 关闭Cursor、退出账号、官网Setting删除账号(刷新节点IP:日本、新加坡、 美国、香港,低延迟为主不一定需要但是有条件就换,Windows用户建议刷新DNS缓存:`ipconfig /flushdns`)
|
||||
> 前往Cursor官网删除当前账号
|
||||
> 步骤:用户头像->Setting-左下角Advanced▼->Delete Account
|
||||
>
|
||||
> 2. 刷新机器码脚本,看下面脚本地址,国内可用
|
||||
>
|
||||
> 3. 重新注册账号、登录、打开Cursor,即可恢复正常使用。
|
||||
>
|
||||
> 4. 备用方案:如果步骤 [**3**] 后仍不可用,或者遇到注册账号失败、无法删除账号等问题,这通常意味着您的浏览器被目标网站识别或限制(风控)。此时,请尝试更换浏览器,例如:Edge、Google Chrome、Firefox。(或者,可以尝试使用能够修改或随机化浏览器指纹信息的浏览器)。
|
||||
|
||||
|
||||
关注大佬公众号:煎饼果子卷AI
|
||||
|
||||
|
||||
---
|
||||
|
||||
> ⚠️ **MAC地址修改警告**
|
||||
>
|
||||
> Mac用户请注意: 本脚本包含MAC地址修改功能,将会:
|
||||
> - 修改您的网络接口MAC地址
|
||||
> - 在修改前备份原始MAC地址
|
||||
> - 此修改可能会暂时影响网络连接
|
||||
> - 执行过程中可以选择跳过此步骤
|
||||
|
||||
---
|
||||
|
||||
### 📝 问题描述
|
||||
|
||||
> 当您遇到以下任何消息时:
|
||||
|
||||
#### 问题 1: 试用账号限制 <p align="right"><a href="#solution1"><img src="https://img.shields.io/badge/跳转到解决方案-Blue?style=plastic" alt="跳转到顶部"></a></p>
|
||||
|
||||
```text
|
||||
Too many free trial accounts used on this machine.
|
||||
Please upgrade to pro. We have this limit in place
|
||||
to prevent abuse. Please let us know if you believe
|
||||
this is a mistake.
|
||||
```
|
||||
|
||||
#### 问题 2: API密钥限制 <p align="right"><a href="#solution2"><img src="https://img.shields.io/badge/跳转到解决方案-green?style=plastic" alt="跳转到顶部"></a></p>
|
||||
|
||||
```text
|
||||
[New Issue]
|
||||
|
||||
Composer relies on custom models that cannot be billed to an API key.
|
||||
Please disable API keys and use a Pro or Business subscription.
|
||||
Request ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
```
|
||||
|
||||
#### 问题 3: 试用请求限制
|
||||
|
||||
> 这表明您在VIP免费试用期间已达到使用限制:
|
||||
|
||||
```text
|
||||
You've reached your trial request limit.
|
||||
```
|
||||
|
||||
#### 问题 4: Claude 3.7 高负载 (High Load) <p align="right"><a href="#solution4"><img src="https://img.shields.io/badge/跳转到解决方案-purple?style=plastic" alt="跳转到顶部"></a></p>
|
||||
|
||||
```text
|
||||
High Load
|
||||
We're experiencing high demand for Claude 3.7 Sonnet right now. Please upgrade to Pro, or switch to the
|
||||
'default' model, Claude 3.5 sonnet, another model, or try again in a few moments.
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
<p id="solution2"></p>
|
||||
|
||||
#### 解决方案:完全卸载Cursor并重新安装(API密钥问题)
|
||||
|
||||
1. 下载 [Geek.exe 卸载工具[免费]](https://geekuninstaller.com/download)
|
||||
2. 完全卸载Cursor应用
|
||||
3. 重新安装Cursor应用
|
||||
4. 继续执行解决方案1
|
||||
|
||||
<br>
|
||||
|
||||
<p id="solution1"></p>
|
||||
|
||||
> 临时解决方案:
|
||||
|
||||
#### 解决方案 1: 快速重置(推荐)
|
||||
|
||||
1. 关闭Cursor应用
|
||||
2. 运行机器码重置脚本(见下方安装说明)
|
||||
3. 重新打开Cursor继续使用
|
||||
|
||||
#### 解决方案 2: 切换账号
|
||||
|
||||
1. 文件 -> Cursor设置 -> 退出登录
|
||||
2. 关闭Cursor
|
||||
3. 运行机器码重置脚本
|
||||
4. 使用新账号登录
|
||||
|
||||
#### 解决方案 3: 网络优化
|
||||
|
||||
如果上述解决方案不起作用,请尝试:
|
||||
|
||||
- 切换到低延迟节点(推荐区域:日本、新加坡、美国、香港)
|
||||
- 确保网络稳定性
|
||||
- 清除浏览器缓存并重试
|
||||
|
||||
<p id="solution4"></p>
|
||||
|
||||
#### 解决方案 4: Claude 3.7 访问问题(High Load )
|
||||
|
||||
如果您看到Claude 3.7 Sonnet的"High Load"(高负载)消息,这表明Cursor在一天中某些时段限制免费试用账号使用3.7模型。请尝试:
|
||||
|
||||
1. 使用Gmail邮箱创建新账号,可能需要通过不同IP地址连接
|
||||
2. 尝试在非高峰时段访问(通常在早上5-10点或下午3-7点之间限制较少)
|
||||
3. 考虑升级到Pro版本获取保证访问权限
|
||||
4. 使用Claude 3.5 Sonnet作为备选方案
|
||||
|
||||
> 注意:随着Cursor调整资源分配策略,这些访问模式可能会发生变化。
|
||||
|
||||
### 🚀 系统支持
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
**Windows** ✅
|
||||
|
||||
- x64 & x86
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
**macOS** ✅
|
||||
|
||||
- Intel & M-series
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
**Linux** ✅
|
||||
|
||||
- x64 & ARM64
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
### 🚀 一键解决方案
|
||||
|
||||
<details open>
|
||||
<summary><b>国内用户(推荐)</b></summary>
|
||||
|
||||
**macOS**
|
||||
|
||||
```bash
|
||||
curl -fsSL https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_mac_id_modifier.sh -o ./cursor_mac_id_modifier.sh && sudo bash ./cursor_mac_id_modifier.sh && rm ./cursor_mac_id_modifier.sh
|
||||
```
|
||||
|
||||
**Linux**
|
||||
|
||||
```bash
|
||||
curl -fsSL https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_linux_id_modifier.sh | sudo bash
|
||||
```
|
||||
|
||||
> **Linux 用户请注意:** 该脚本通过检查常用路径(`/usr/bin`, `/usr/local/bin`, `$HOME/.local/bin`, `/opt/cursor`, `/snap/bin`)、使用 `which cursor` 命令以及在 `/usr`、`/opt` 和 `$HOME/.local` 目录内搜索,来尝试定位您的 Cursor 安装。如果 Cursor 安装在其他位置或通过这些方法无法找到,脚本可能会失败。请确保可以通过这些标准位置或方法之一访问到 Cursor。
|
||||
|
||||
**Windows**
|
||||
|
||||
```powershell
|
||||
irm https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex
|
||||
```
|
||||
|
||||
**Windows (增强版)**
|
||||
|
||||
```powershell
|
||||
irm https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex
|
||||
```
|
||||
> 增强版Cursor机器码修改工具,支持双模式操作和试用重置功能
|
||||
<div align="center">
|
||||
<img src="img/run_success.png" alt="运行成功" width="600"/>
|
||||
</div>
|
||||
|
||||
</details>
|
||||
<details open>
|
||||
<summary><b>Windows 管理员终端运行和手动安装</b></summary>
|
||||
|
||||
#### Windows 系统打开管理员终端的方法:
|
||||
|
||||
##### 方法一:使用 Win + X 快捷键
|
||||
```md
|
||||
1. 按下 Win + X 组合键
|
||||
2. 在弹出的菜单中选择以下任一选项:
|
||||
- "Windows PowerShell (管理员)"
|
||||
- "Windows Terminal (管理员)"
|
||||
- "终端(管理员)"
|
||||
(具体选项因Windows版本而异)
|
||||
```
|
||||
|
||||
##### 方法二:使用 Win + R 运行命令
|
||||
```md
|
||||
1. 按下 Win + R 组合键
|
||||
2. 在运行框中输入 powershell 或 pwsh
|
||||
3. 按 Ctrl + Shift + Enter 以管理员身份运行
|
||||
或在打开的窗口中输入: Start-Process pwsh -Verb RunAs
|
||||
4. 在管理员终端中输入以下重置脚本:
|
||||
|
||||
irm https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex
|
||||
```
|
||||
|
||||
增强版脚本:
|
||||
```powershell
|
||||
irm https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex
|
||||
```
|
||||
|
||||
##### 方法三:通过搜索启动
|
||||
>
|
||||
>
|
||||
>在搜索框中输入 pwsh,右键选择"以管理员身份运行"
|
||||
>
|
||||
|
||||
在管理员终端中输入重置脚本:
|
||||
```powershell
|
||||
irm https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex
|
||||
```
|
||||
|
||||
增强版脚本:
|
||||
```powershell
|
||||
irm https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex
|
||||
```
|
||||
|
||||
### 🔧 PowerShell 安装指南
|
||||
|
||||
如果您的系统没有安装 PowerShell,可以通过以下方法安装:
|
||||
|
||||
#### 方法一:使用 Winget 安装(推荐)
|
||||
|
||||
1. 打开命令提示符或 PowerShell
|
||||
2. 运行以下命令:
|
||||
```powershell
|
||||
winget install --id Microsoft.PowerShell --source winget
|
||||
```
|
||||
|
||||
#### 方法二:手动下载安装
|
||||
|
||||
1. 下载对应系统的安装包:
|
||||
- [PowerShell-7.4.6-win-x64.msi](https://github.com/PowerShell/PowerShell/releases/download/v7.4.6/PowerShell-7.4.6-win-x64.msi) (64位系统)
|
||||
- [PowerShell-7.4.6-win-x86.msi](https://github.com/PowerShell/PowerShell/releases/download/v7.4.6/PowerShell-7.4.6-win-x86.msi) (32位系统)
|
||||
- [PowerShell-7.4.6-win-arm64.msi](https://github.com/PowerShell/PowerShell/releases/download/v7.4.6/PowerShell-7.4.6-win-arm64.msi) (ARM64系统)
|
||||
|
||||
2. 双击下载的安装包,按提示完成安装
|
||||
|
||||
> 💡 如果仍然遇到问题,可以参考 [Microsoft 官方安装指南](https://learn.microsoft.com/zh-cn/powershell/scripting/install/installing-powershell-on-windows)
|
||||
|
||||
</details>
|
||||
|
||||
#### Windows 安装特性:
|
||||
|
||||
- 🔍 自动检测并使用 PowerShell 7(如果可用)
|
||||
- 🛡️ 通过 UAC 提示请求管理员权限
|
||||
- 📝 如果没有 PS7 则使用 Windows PowerShell
|
||||
- 💡 如果提权失败会提供手动说明
|
||||
|
||||
完成后,脚本将:
|
||||
|
||||
1. ✨ 自动安装工具
|
||||
2. 🔄 立即重置 Cursor 试用期
|
||||
|
||||
### 📦 手动安装
|
||||
|
||||
> 从 [releases](https://github.com/yuaotian/go-cursor-help/releases/latest) 下载适合您系统的文件
|
||||
|
||||
<details>
|
||||
<summary>Windows 安装包</summary>
|
||||
|
||||
- 64 位: `cursor-id-modifier_windows_x64.exe`
|
||||
- 32 位: `cursor-id-modifier_windows_x86.exe`
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>macOS 安装包</summary>
|
||||
|
||||
- Intel: `cursor-id-modifier_darwin_x64_intel`
|
||||
- M1/M2: `cursor-id-modifier_darwin_arm64_apple_silicon`
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Linux 安装包</summary>
|
||||
|
||||
- 64 位: `cursor-id-modifier_linux_x64`
|
||||
- 32 位: `cursor-id-modifier_linux_x86`
|
||||
- ARM64: `cursor-id-modifier_linux_arm64`
|
||||
</details>
|
||||
|
||||
### 🔧 技术细节
|
||||
|
||||
<details>
|
||||
<summary><b>注册表修改说明</b></summary>
|
||||
|
||||
> ⚠️ **重要提示:本工具会修改系统注册表**
|
||||
|
||||
#### 修改内容
|
||||
- 路径:`计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography`
|
||||
- 项目:`MachineGuid`
|
||||
|
||||
#### 潜在影响
|
||||
修改此注册表项可能会影响:
|
||||
- Windows 系统对设备的唯一标识
|
||||
- 某些软件的设备识别和授权状态
|
||||
- 基于硬件标识的系统功能
|
||||
|
||||
#### 安全措施
|
||||
1. 自动备份
|
||||
- 每次修改前会自动备份原始值
|
||||
- 备份保存在:`%APPDATA%\Cursor\User\globalStorage\backups`
|
||||
- 备份文件格式:`MachineGuid.backup_YYYYMMDD_HHMMSS`
|
||||
|
||||
2. 手动恢复方法
|
||||
- 打开注册表编辑器(regedit)
|
||||
- 定位到:`计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography`
|
||||
- 右键点击 `MachineGuid`
|
||||
- 选择"修改"
|
||||
- 粘贴备份文件中的值
|
||||
|
||||
#### 注意事项
|
||||
- 建议在修改前先确认备份文件的存在
|
||||
- 如遇问题可通过备份文件恢复原始值
|
||||
- 必须以管理员权限运行才能修改注册表
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>配置文件</b></summary>
|
||||
|
||||
程序修改 Cursor 的`storage.json`配置文件,位于:
|
||||
|
||||
- Windows: `%APPDATA%\Cursor\User\globalStorage\`
|
||||
- macOS: `~/Library/Application Support/Cursor/User/globalStorage/`
|
||||
- Linux: `~/.config/Cursor/User/globalStorage/`
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>修改字段</b></summary>
|
||||
|
||||
工具会生成新的唯一标识符:
|
||||
|
||||
- `telemetry.machineId`
|
||||
- `telemetry.macMachineId`
|
||||
- `telemetry.devDeviceId`
|
||||
- `telemetry.sqmId`
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>手动禁用自动更新</b></summary>
|
||||
|
||||
Windows 用户可以手动禁用自动更新功能:
|
||||
|
||||
1. 关闭所有 Cursor 进程
|
||||
2. 删除目录:`C:\Users\用户名\AppData\Local\cursor-updater`
|
||||
3. 创建同名文件:`cursor-updater`(不带扩展名)
|
||||
|
||||
Linux用户可以尝试在系统中找到类似的`cursor-updater`目录进行相同操作。
|
||||
|
||||
MacOS用户按照以下步骤操作:
|
||||
|
||||
```bash
|
||||
# 注意:经测试,此方法仅适用于0.45.11及以下版本,不支持0.46.*版本
|
||||
# 关闭所有 Cursor 进程
|
||||
pkill -f "Cursor"
|
||||
|
||||
# 备份app-update.yml并创建空的只读文件代替原文件
|
||||
cd /Applications/Cursor.app/Contents/Resources
|
||||
mv app-update.yml app-update.yml.bak
|
||||
touch app-update.yml
|
||||
chmod 444 app-update.yml
|
||||
|
||||
# 打开Cursor设置,将更新模式设置为"无",该步骤必须执行,否则Cursor依然会自动检查更新
|
||||
# 步骤:Settings -> Application -> Update, 将Mode设置为none
|
||||
|
||||
# 注意: cursor-updater修改方法可能已失效。但为了以防万一,还是删除更新目录并创建阻止文件
|
||||
rm -rf ~/Library/Application\ Support/Caches/cursor-updater
|
||||
touch ~/Library/Application\ Support/Caches/cursor-updater
|
||||
```
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>安全特性</b></summary>
|
||||
|
||||
- ✅ 安全的进程终止
|
||||
- ✅ 原子文件操作
|
||||
- ✅ 错误处理和恢复
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>重置 Cursor 免费试用</b></summary>
|
||||
|
||||
### 使用 `cursor_free_trial_reset.sh` 脚本
|
||||
|
||||
#### macOS
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_free_trial_reset.sh -o ./cursor_free_trial_reset.sh && sudo bash ./cursor_free_trial_reset.sh && rm ./cursor_free_trial_reset.sh
|
||||
```
|
||||
|
||||
#### Linux
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_free_trial_reset.sh | sudo bash
|
||||
```
|
||||
|
||||
#### Windows
|
||||
|
||||
```powershell
|
||||
irm https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_free_trial_reset.sh | iex
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## 联系方式
|
||||
|
||||
<div align="center">
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<b>个人微信</b><br>
|
||||
<img src="img/wx_me.png" width="250" alt="作者微信"><br>
|
||||
<b>微信:JavaRookie666</b>
|
||||
</td>
|
||||
<td align="center">
|
||||
<b>微信交流群</b><br>
|
||||
<img src="img/qun-16.png" width="500" alt="WeChat"><br>
|
||||
<small>二维码7天内(8月4日前)有效,过期请加微信</small>
|
||||
</td>
|
||||
<td align="center">
|
||||
<b>公众号</b><br>
|
||||
<img src="img/wx_public_2.png" width="250" alt="微信公众号"><br>
|
||||
<small>获取更多AI开发资源</small>
|
||||
</td>
|
||||
<td align="center">
|
||||
<b>微信赞赏</b><br>
|
||||
<img src="img/wx_zsm2.png" width="500" alt="微信赞赏码"><br>
|
||||
<small>要到饭咧?啊咧?啊咧?不给也没事~ 请随意打赏</small>
|
||||
</td>
|
||||
<td align="center">
|
||||
<b>支付宝赞赏</b><br>
|
||||
<img src="img/alipay.png" width="500" alt="支付宝赞赏码"><br>
|
||||
<small>如果觉得有帮助,来包辣条犒劳一下吧~</small>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
### 📚 推荐阅读
|
||||
|
||||
- [Cursor 异常问题收集和解决方案](https://mp.weixin.qq.com/s/pnJrH7Ifx4WZvseeP1fcEA)
|
||||
- [AI 通用开发助手提示词指南](https://mp.weixin.qq.com/s/PRPz-qVkFJSgkuEKkTdzwg)
|
||||
|
||||
---
|
||||
|
||||
## 💬 反馈与建议
|
||||
|
||||
我们非常重视您对新增强脚本的反馈!如果您已经尝试了 `cursor_win_id_modifier.ps1` 脚本,请分享您的使用体验:
|
||||
|
||||
- 🐛 **错误报告**:发现任何问题?请告诉我们!
|
||||
- 💡 **功能建议**:有改进想法?
|
||||
- ⭐ **成功案例**:分享工具如何帮助到您!
|
||||
- 🔧 **技术反馈**:性能、兼容性或易用性方面的见解
|
||||
|
||||
您的反馈帮助我们为所有人改进工具。欢迎提交issue或为项目做出贡献!
|
||||
|
||||
---
|
||||
|
||||
## ⭐ 项目统计
|
||||
|
||||
<div align="center">
|
||||
|
||||
[](https://star-history.com/#yuaotian/go-cursor-help&Date)
|
||||
|
||||

|
||||
|
||||
</div>
|
||||
|
||||
## 📄 许可证
|
||||
|
||||
<details>
|
||||
<summary><b>MIT 许可证</b></summary>
|
||||
|
||||
Copyright (c) 2024
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
</details>
|
587
README_JP.md
Normal file
@ -0,0 +1,587 @@
|
||||
# 🚀 Cursor 無料試用リセットツール
|
||||
|
||||
<div align="center">
|
||||
|
||||
[](https://github.com/yuaotian/go-cursor-help/releases/latest)
|
||||
[](https://github.com/yuaotian/go-cursor-help/blob/master/LICENSE)
|
||||
[](https://github.com/yuaotian/go-cursor-help/stargazers)
|
||||
|
||||
[🌟 English](README.md) | [🌏 中文](README_CN.md) | [🌏 日本語](README_JP.md)
|
||||
|
||||
<img src="https://ai-cursor.com/wp-content/uploads/2024/09/logo-cursor-ai-png.webp" alt="Cursor Logo" width="120"/>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
> ⚠️ **重要なお知らせ**
|
||||
>
|
||||
> このツールは現在以下のバージョンをサポートしています:
|
||||
> - ✅ Windows: 最新の1.0.xバージョン(サポート済み)
|
||||
> - ✅ Mac/Linux: 最新の1.0.xバージョン(サポート済み、フィードバック歓迎)
|
||||
>
|
||||
> このツールを使用する前に、Cursorのバージョンを確認してください。
|
||||
|
||||
<details open>
|
||||
<summary><b>📦 バージョン履歴とダウンロード</b></summary>
|
||||
|
||||
<div class="version-card" style="background: linear-gradient(135deg, #6e8efb, #a777e3); border-radius: 8px; padding: 15px; margin: 10px 0; color: white;">
|
||||
|
||||
### 🌟 最新バージョン
|
||||
|
||||
[完全なバージョン履歴を見る]([CursorHistoryDown.md](https://github.com/oslook/cursor-ai-downloads?tab=readme-ov-file))
|
||||
|
||||
</div>
|
||||
|
||||
</details>
|
||||
|
||||
⚠️ **Cursorの一般的な解決策**
|
||||
> 1. Cursorを閉じ、アカウントからログアウトし、公式サイトの設定からアカウントを削除します(IPノードを更新:日本、シンガポール、アメリカ、香港など、低遅延を優先。必須ではありませんが条件が整えば変更してください。Windowsユーザーの場合はDNSキャッシュの更新をお勧めします:`ipconfig /flushdns`)
|
||||
> Cursor公式サイトで現在のアカウントを削除します
|
||||
> 手順:ユーザーアイコン->設定->左下のAdvanced▼->Delete Account
|
||||
>
|
||||
> 2. マシンコードリセットスクリプトを実行します。下記のスクリプトアドレスを参照してください。
|
||||
>
|
||||
> 3. アカウントを再登録し、ログインして、Cursorを開くと、正常に使用できるようになります。
|
||||
>
|
||||
> 4. 代替案:ステップ[**3**]の後でもまだ使用できない場合、またはアカウント登録に失敗したり、アカウントを削除できないなどの問題が発生した場合、これは通常、ブラウザがターゲットサイトに識別または制限されている(リスク管理)ことを意味します。この場合、Edge、Google Chrome、Firefoxなど別のブラウザを試してみてください(または、ブラウザのフィンガープリント情報を変更またはランダム化できるブラウザの使用を検討してください)。
|
||||
|
||||
|
||||
---
|
||||
|
||||
⚠️ **MACアドレス変更警告**
|
||||
>
|
||||
> Macユーザーの皆様へ: このスクリプトにはMACアドレス変更機能が含まれています。以下の操作が行われます:
|
||||
> - ネットワークインターフェースのMACアドレスを変更します
|
||||
> - 変更前に元のMACアドレスをバックアップします
|
||||
> - この変更により一時的にネットワーク接続が影響を受ける可能性があります
|
||||
> - 実行中にこのステップをスキップすることができます
|
||||
>
|
||||
|
||||
<details >
|
||||
<summary><b>🔒 自動更新機能の無効化</b></summary>
|
||||
|
||||
> Cursorがサポートされていない新しいバージョンに自動的に更新されるのを防ぐために、自動更新機能を無効にすることができます。
|
||||
|
||||
#### 方法1: 組み込みスクリプトを使用する(推奨)
|
||||
|
||||
リセットツールを実行するとき、スクリプトは自動更新を無効にするかどうかを尋ねます:
|
||||
```text
|
||||
[質問] Cursorの自動更新機能を無効にしますか?
|
||||
0) いいえ - デフォルト設定を維持(Enterキーを押す)
|
||||
1) はい - 自動更新を無効にする
|
||||
```
|
||||
|
||||
`1`を選択して無効化操作を自動的に完了します。
|
||||
|
||||
#### 方法2: 手動で無効化
|
||||
|
||||
**Windows:**
|
||||
1. すべてのCursorプロセスを閉じます
|
||||
2. ディレクトリを削除します: `%LOCALAPPDATA%\cursor-updater`
|
||||
3. 同じ名前のファイルを作成します(拡張子なし)
|
||||
|
||||
**macOS:**
|
||||
```bash
|
||||
# 注意: テスト済みでは、この方法はバージョン0.45.11およびそれ以前のバージョンでのみ機能します。
|
||||
# Cursorを閉じます
|
||||
pkill -f "Cursor"
|
||||
# app-update.ymlを空の読み取り専用ファイルに置き換えます
|
||||
cd /Applications/Cursor.app/Contents/Resources
|
||||
mv app-update.yml app-update.yml.bak
|
||||
touch app-update.yml
|
||||
chmod 444 app-update.yml
|
||||
|
||||
# 設定 -> アプリケーション -> 更新、モードをnoneに設定します。
|
||||
# これを行わないと、Cursorは更新をチェックし続けます。
|
||||
|
||||
# 注意: cursor-updaterの変更方法はもはや有効ではないかもしれません
|
||||
# いずれにせよ、更新ディレクトリを削除し、ブロックファイルを作成します
|
||||
rm -rf ~/Library/Application\ Support/Caches/cursor-updater
|
||||
touch ~/Library/Application\ Support/Caches/cursor-updater
|
||||
```
|
||||
|
||||
**Linux:**
|
||||
```bash
|
||||
# Cursorを閉じます
|
||||
pkill -f "Cursor"
|
||||
# 更新ディレクトリを削除し、ブロックファイルを作成します
|
||||
rm -rf ~/.config/cursor-updater
|
||||
touch ~/.config/cursor-updater
|
||||
```
|
||||
|
||||
> ⚠️ **注意:** 自動更新を無効にした後、新しいバージョンを手動でダウンロードしてインストールする必要があります。新しいバージョンが互換性があることを確認した後に更新することをお勧めします。
|
||||
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
### 📝 説明
|
||||
|
||||
> これらのメッセージのいずれかに遭遇した場合:
|
||||
|
||||
#### 問題1: 試用アカウント制限 <p align="right"><a href="#issue1"><img src="https://img.shields.io/badge/Move%20to%20Solution-Blue?style=plastic" alt="Back To Top"></a></p>
|
||||
|
||||
```text
|
||||
Too many free trial accounts used on this machine.
|
||||
Please upgrade to pro. We have this limit in place
|
||||
to prevent abuse. Please let us know if you believe
|
||||
this is a mistake.
|
||||
```
|
||||
|
||||
#### 問題2: APIキー制限 <p align="right"><a href="#issue2"><img src="https://img.shields.io/badge/Move%20to%20Solution-green?style=plastic" alt="Back To Top"></a></p>
|
||||
|
||||
```text
|
||||
[New Issue]
|
||||
|
||||
Composer relies on custom models that cannot be billed to an API key.
|
||||
Please disable API keys and use a Pro or Business subscription.
|
||||
Request ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
```
|
||||
|
||||
#### 問題3: 試用リクエスト制限
|
||||
|
||||
> これは、VIP無料試用期間中に使用制限に達したことを示しています:
|
||||
|
||||
```text
|
||||
You've reached your trial request limit.
|
||||
```
|
||||
|
||||
#### 問題4: Claude 3.7 高負荷 <p align="right"><a href="#issue4"><img src="https://img.shields.io/badge/Move%20to%20Solution-purple?style=plastic" alt="Back To Top"></a></p>
|
||||
|
||||
```text
|
||||
High Load
|
||||
We're experiencing high demand for Claude 3.7 Sonnet right now. Please upgrade to Pro, or switch to the
|
||||
'default' model, Claude 3.5 sonnet, another model, or try again in a few moments.
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
<p id="issue2"></p>
|
||||
|
||||
#### 解決策 : Cursorを完全にアンインストールして再インストールする(APIキーの問題)
|
||||
|
||||
1. [Geek.exeアンインストーラー[無料]](https://geekuninstaller.com/download)をダウンロードします
|
||||
2. Cursorアプリを完全にアンインストールします
|
||||
3. Cursorアプリを再インストールします
|
||||
4. 解決策1を続行します
|
||||
|
||||
<br>
|
||||
|
||||
<p id="issue1"></p>
|
||||
|
||||
> 一時的な解決策:
|
||||
|
||||
#### 解決策1: クイックリセット(推奨)
|
||||
|
||||
1. Cursorアプリケーションを閉じます
|
||||
2. マシンコードリセットスクリプトを実行します(以下のインストール手順を参照)
|
||||
3. Cursorを再度開いて使用を続けます
|
||||
|
||||
#### 解決策2: アカウントの切り替え
|
||||
|
||||
1. ファイル -> Cursor設定 -> サインアウト
|
||||
2. Cursorを閉じます
|
||||
3. マシンコードリセットスクリプトを実行します
|
||||
4. 新しいアカウントでログインします
|
||||
|
||||
#### 解決策3: ネットワークの最適化
|
||||
|
||||
上記の解決策が機能しない場合は、次のことを試してください:
|
||||
|
||||
- 低遅延ノードに切り替えます(推奨地域:日本、シンガポール、米国、香港)
|
||||
- ネットワークの安定性を確保します
|
||||
- ブラウザのキャッシュをクリアして再試行します
|
||||
|
||||
#### 解決策4: Claude 3.7 アクセス問題(高負荷)
|
||||
|
||||
Claude 3.7 Sonnetの"High Load"メッセージが表示された場合、これはCursorが特定の時間帯に無料試用アカウントの3.7モデルの使用を制限していることを示しています。次のことを試してください:
|
||||
|
||||
1. Gmailで作成した新しいアカウントに切り替えます。異なるIPアドレスを使用して接続することをお勧めします
|
||||
2. 非ピーク時間帯にアクセスを試みます(通常、5-10 AMまたは3-7 PMの間に制限が少ないです)
|
||||
3. Proにアップグレードしてアクセスを保証します
|
||||
4. Claude 3.5 Sonnetを代替オプションとして使用します
|
||||
|
||||
> 注意: Cursorがリソース配分ポリシーを調整するにつれて、これらのアクセスパターンは変更される可能性があります。
|
||||
|
||||
### 💻 システムサポート
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
**Windows** ✅
|
||||
|
||||
- x64 (64ビット)
|
||||
- x86 (32ビット)
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
**macOS** ✅
|
||||
|
||||
- Intel (x64)
|
||||
- Apple Silicon (M1/M2)
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
**Linux** ✅
|
||||
|
||||
- x64 (64ビット)
|
||||
- x86 (32ビット)
|
||||
- ARM64
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
### 🚀 ワンクリックソリューション
|
||||
|
||||
<details open>
|
||||
<summary><b>グローバルユーザー</b></summary>
|
||||
|
||||
**macOS**
|
||||
|
||||
```bash
|
||||
# 方法2
|
||||
curl -fsSL https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_mac_id_modifier.sh -o ./cursor_mac_id_modifier.sh && sudo bash ./cursor_mac_id_modifier.sh && rm ./cursor_mac_id_modifier.sh
|
||||
```
|
||||
|
||||
**Linux**
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_linux_id_modifier.sh | sudo bash
|
||||
```
|
||||
|
||||
**Windows**
|
||||
|
||||
```powershell
|
||||
irm https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex
|
||||
```
|
||||
|
||||
**Windows (強化版)**
|
||||
|
||||
```powershell
|
||||
irm https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex
|
||||
```
|
||||
> デュアルモード操作とトライアルリセット機能を備えた強化版Cursorマシンコード修正ツール
|
||||
|
||||
<div align="center">
|
||||
<img src="img/run_success.png" alt="Run Success" width="600"/>
|
||||
</div>
|
||||
|
||||
</details>
|
||||
|
||||
<details open>
|
||||
<summary><b>中国ユーザー(推奨)</b></summary>
|
||||
|
||||
**macOS**
|
||||
|
||||
```bash
|
||||
curl -fsSL https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_mac_id_modifier.sh -o ./cursor_mac_id_modifier.sh && sudo bash ./cursor_mac_id_modifier.sh && rm ./cursor_mac_id_modifier.sh
|
||||
```
|
||||
|
||||
**Linux**
|
||||
|
||||
```bash
|
||||
curl -fsSL https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_linux_id_modifier.sh | sudo bash
|
||||
```
|
||||
|
||||
> **Linuxユーザーへの注意:** スクリプトは、一般的なパス(`/usr/bin`, `/usr/local/bin`, `$HOME/.local/bin`, `/opt/cursor`, `/snap/bin`)の確認、`which cursor` コマンドの使用、および `/usr`、`/opt`、`$HOME/.local` ディレクトリ内の検索によって、Cursor のインストールを見つけようとします。Cursorが他の場所にインストールされているか、これらの方法で見つからない場合、スクリプトは失敗する可能性があります。これらの標準的な場所または方法のいずれかを通じてCursorにアクセスできることを確認してください。
|
||||
|
||||
**Windows**
|
||||
|
||||
```powershell
|
||||
irm https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex
|
||||
```
|
||||
|
||||
**Windows (強化版)**
|
||||
|
||||
```powershell
|
||||
irm https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex
|
||||
```
|
||||
> デュアルモード操作とトライアルリセット機能を備えた強化版Cursorマシンコード修正ツール
|
||||
|
||||
</details>
|
||||
|
||||
<details open>
|
||||
<summary><b>Windowsターミナルの実行と構成</b></summary>
|
||||
|
||||
#### Windowsで管理者ターミナルを開く方法:
|
||||
|
||||
##### 方法1: Win + Xショートカットを使用する
|
||||
```md
|
||||
1. Win + Xキーの組み合わせを押します
|
||||
2. メニューから次のオプションのいずれかを選択します:
|
||||
- "Windows PowerShell (管理者)"
|
||||
- "Windows Terminal (管理者)"
|
||||
- "ターミナル (管理者)"
|
||||
(Windowsのバージョンによってオプションが異なる場合があります)
|
||||
```
|
||||
|
||||
##### 方法2: Win + R実行コマンドを使用する
|
||||
```md
|
||||
1. Win + Rキーの組み合わせを押します
|
||||
2. 実行ダイアログにpowershellまたはpwshと入力します
|
||||
3. Ctrl + Shift + Enterを押して管理者として実行します
|
||||
または開いたウィンドウに次のように入力します: Start-Process pwsh -Verb RunAs
|
||||
4. 管理者ターミナルにリセットスクリプトを入力します:
|
||||
|
||||
irm https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex
|
||||
```
|
||||
|
||||
強化版スクリプト:
|
||||
```powershell
|
||||
irm https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex
|
||||
```
|
||||
|
||||
##### 方法3: 検索を使用する
|
||||
>
|
||||
>
|
||||
>検索ボックスにpwshと入力し、右クリックして「管理者として実行」を選択します
|
||||
>
|
||||
|
||||
管理者ターミナルにリセットスクリプトを入力します:
|
||||
```powershell
|
||||
irm https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex
|
||||
```
|
||||
|
||||
強化版スクリプト:
|
||||
```powershell
|
||||
irm https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run/cursor_win_id_modifier.ps1 | iex
|
||||
```
|
||||
|
||||
### 🔧 PowerShellインストールガイド
|
||||
|
||||
システムにPowerShellがインストールされていない場合は、次の方法でインストールできます:
|
||||
|
||||
#### 方法1: Wingetを使用してインストール(推奨)
|
||||
|
||||
1. コマンドプロンプトまたはPowerShellを開きます
|
||||
2. 次のコマンドを実行します:
|
||||
```powershell
|
||||
winget install --id Microsoft.PowerShell --source winget
|
||||
```
|
||||
|
||||
#### 方法2: 手動でインストール
|
||||
|
||||
1. システムに適したインストーラーをダウンロードします:
|
||||
- [PowerShell-7.4.6-win-x64.msi](https://github.com/PowerShell/PowerShell/releases/download/v7.4.6/PowerShell-7.4.6-win-x64.msi)(64ビットシステム用)
|
||||
- [PowerShell-7.4.6-win-x86.msi](https://github.com/PowerShell/PowerShell/releases/download/v7.4.6/PowerShell-7.4.6-win-x86.msi)(32ビットシステム用)
|
||||
- [PowerShell-7.4.6-win-arm64.msi](https://github.com/PowerShell/PowerShell/releases/download/v7.4.6/PowerShell-7.4.6-win-arm64.msi)(ARM64システム用)
|
||||
|
||||
2. ダウンロードしたインストーラーをダブルクリックし、インストールの指示に従います
|
||||
|
||||
> 💡 問題が発生した場合は、[Microsoft公式インストールガイド](https://learn.microsoft.com/ja-jp/powershell/scripting/install/installing-powershell-on-windows)を参照してください
|
||||
|
||||
</details>
|
||||
|
||||
#### Windowsインストール機能:
|
||||
|
||||
- 🔍 PowerShell 7が利用可能な場合は自動的に検出して使用します
|
||||
- 🛡️ UACプロンプトを介して管理者権限を要求します
|
||||
- 📝 PS7が見つからない場合はWindows PowerShellにフォールバックします
|
||||
- 💡 権限昇格に失敗した場合は手動の指示を提供します
|
||||
|
||||
これで完了です!スクリプトは次のことを行います:
|
||||
|
||||
1. ✨ ツールを自動的にインストールします
|
||||
2. 🔄 Cursorの試用期間を即座にリセットします
|
||||
|
||||
### 📦 手動インストール
|
||||
|
||||
> [リリース](https://github.com/yuaotian/go-cursor-help/releases/latest)からシステムに適したファイルをダウンロードします
|
||||
|
||||
<details>
|
||||
<summary>Windowsパッケージ</summary>
|
||||
|
||||
- 64ビット: `cursor-id-modifier_windows_x64.exe`
|
||||
- 32ビット: `cursor-id-modifier_windows_x86.exe`
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>macOSパッケージ</summary>
|
||||
|
||||
- Intel: `cursor-id-modifier_darwin_x64_intel`
|
||||
- M1/M2: `cursor-id-modifier_darwin_arm64_apple_silicon`
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Linuxパッケージ</summary>
|
||||
|
||||
- 64ビット: `cursor-id-modifier_linux_x64`
|
||||
- 32ビット: `cursor-id-modifier_linux_x86`
|
||||
- ARM64: `cursor-id-modifier_linux_arm64`
|
||||
</details>
|
||||
|
||||
### 🔧 技術的詳細
|
||||
|
||||
<details>
|
||||
<summary><b>構成ファイル</b></summary>
|
||||
|
||||
プログラムはCursorの`storage.json`構成ファイルを変更します。場所は次のとおりです:
|
||||
|
||||
- Windows: `%APPDATA%\Cursor\User\globalStorage\storage.json`
|
||||
- macOS: `~/Library/Application Support/Cursor/User/globalStorage/storage.json`
|
||||
- Linux: `~/.config/Cursor/User/globalStorage/storage.json`
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>変更されたフィールド</b></summary>
|
||||
|
||||
ツールは次の新しい一意の識別子を生成します:
|
||||
|
||||
- `telemetry.machineId`
|
||||
- `telemetry.macMachineId`
|
||||
- `telemetry.devDeviceId`
|
||||
- `telemetry.sqmId`
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>手動自動更新無効化</b></summary>
|
||||
|
||||
Windowsユーザーは自動更新機能を手動で無効にすることができます:
|
||||
|
||||
1. すべてのCursorプロセスを閉じます
|
||||
2. ディレクトリを削除します: `C:\Users\username\AppData\Local\cursor-updater`
|
||||
3. 同じ名前のファイルを作成します: `cursor-updater`(拡張子なし)
|
||||
|
||||
macOS/Linuxユーザーはシステム内で同様の`cursor-updater`ディレクトリを見つけて同じ操作を行うことができます。
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>安全機能</b></summary>
|
||||
|
||||
- ✅ 安全なプロセス終了
|
||||
- ✅ アトミックファイル操作
|
||||
- ✅ エラーハンドリングとリカバリ
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>レジストリ変更通知</b></summary>
|
||||
|
||||
> ⚠️ **重要: このツールはWindowsレジストリを変更します**
|
||||
|
||||
#### 変更されたレジストリ
|
||||
- パス: `コンピュータ\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography`
|
||||
- キー: `MachineGuid`
|
||||
|
||||
#### 潜在的な影響
|
||||
このレジストリキーを変更すると、次のことに影響を与える可能性があります:
|
||||
- Windowsシステムの一意のデバイス識別
|
||||
- 特定のソフトウェアのデバイス認識と認証状態
|
||||
- ハードウェア識別に基づくシステム機能
|
||||
|
||||
#### 安全対策
|
||||
1. 自動バックアップ
|
||||
- 変更前に元の値が自動的にバックアップされます
|
||||
- バックアップ場所: `%APPDATA%\Cursor\User\globalStorage\backups`
|
||||
- バックアップファイル形式: `MachineGuid.backup_YYYYMMDD_HHMMSS`
|
||||
|
||||
2. 手動復元手順
|
||||
- レジストリエディタ(regedit)を開きます
|
||||
- 次の場所に移動します: `コンピュータ\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography`
|
||||
- `MachineGuid`を右クリックします
|
||||
- 「修正」を選択します
|
||||
- バックアップファイルの値を貼り付けます
|
||||
|
||||
#### 重要な注意事項
|
||||
- 変更前にバックアップファイルの存在を確認します
|
||||
- 必要に応じてバックアップファイルを使用して元の値を復元します
|
||||
- レジストリの変更には管理者権限が必要です
|
||||
</details>
|
||||
|
||||
---
|
||||
|
||||
### 📚 推奨読書
|
||||
|
||||
- [Cursorの問題収集と解決策](https://mp.weixin.qq.com/s/pnJrH7Ifx4WZvseeP1fcEA)
|
||||
- [AIユニバーサル開発アシスタントプロンプトガイド](https://mp.weixin.qq.com/s/PRPz-qVkFJSgkuEKkTdzwg)
|
||||
|
||||
---
|
||||
|
||||
## サポート
|
||||
|
||||
<div align="center">
|
||||
<b>このツールが役立つと感じた場合、スパイシーグルテンのおやつ(Latiao)を買っていただけると嬉しいです~ 💁☕️</b>
|
||||
<table>
|
||||
<tr>
|
||||
|
||||
<td align="center">
|
||||
<b>WeChat Pay</b><br>
|
||||
<img src="img/wx_zsm2.png" width="500" alt="WeChat Pay"><br>
|
||||
<small>要到饭咧?啊咧?啊咧?不给也没事~ 请随意打赏</small>
|
||||
</td>
|
||||
<td align="center">
|
||||
<b>Alipay</b><br>
|
||||
<img src="img/alipay.png" width="500" alt="Alipay"><br>
|
||||
<small>如果觉得有帮助,来包辣条犒劳一下吧~</small>
|
||||
</td>
|
||||
<td align="center">
|
||||
<b>Alipay</b><br>
|
||||
<img src="img/alipay_scan_pay.jpg" width="500" alt="Alipay"><br>
|
||||
<em>1 Latiao = 1 AI thought cycle</em>
|
||||
</td>
|
||||
<td align="center">
|
||||
<b>WeChat</b><br>
|
||||
<img src="img/qun-16.png" width="500" alt="WeChat"><br>
|
||||
<em>二维码7天内(8月4日前前)有效,过期请加微信</em>
|
||||
</td>
|
||||
<!-- <td align="center">
|
||||
<b>ETC</b><br>
|
||||
<img src="img/etc.png" width="100" alt="ETC Address"><br>
|
||||
ETC: 0xa2745f4CD5d32310AC01694ABDB28bA32D125a6b
|
||||
</td>
|
||||
<td align="center"> -->
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## 💬 フィードバック&提案
|
||||
|
||||
新しい強化スクリプトに関するフィードバックをお待ちしています!`cursor_win_id_modifier.ps1` スクリプトをお試しいただいた方は、ぜひご体験をお聞かせください:
|
||||
|
||||
- 🐛 **バグレポート**:問題を発見されましたか?お知らせください!
|
||||
- 💡 **機能提案**:改善のアイデアはありますか?
|
||||
- ⭐ **成功事例**:ツールがどのようにお役に立ったかお聞かせください!
|
||||
- 🔧 **技術的フィードバック**:パフォーマンス、互換性、使いやすさに関するご意見
|
||||
|
||||
皆様のフィードバックは、すべてのユーザーのためにツールを改善するのに役立ちます。お気軽にissueを開いたり、プロジェクトに貢献してください!
|
||||
|
||||
---
|
||||
|
||||
## ⭐ プロジェクト統計
|
||||
|
||||
<div align="center">
|
||||
|
||||
[](https://star-history.com/#yuaotian/go-cursor-help&Date)
|
||||
|
||||

|
||||
|
||||
</div>
|
||||
|
||||
## 📄 ライセンス
|
||||
|
||||
<details>
|
||||
<summary><b>MITライセンス</b></summary>
|
||||
|
||||
Copyright (c) 2024
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
</details>
|
328
cmd/cursor-id-modifier/main.go
Normal file
@ -0,0 +1,328 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/yuaotian/go-cursor-help/internal/config"
|
||||
"github.com/yuaotian/go-cursor-help/internal/lang"
|
||||
"github.com/yuaotian/go-cursor-help/internal/process"
|
||||
"github.com/yuaotian/go-cursor-help/internal/ui"
|
||||
"github.com/yuaotian/go-cursor-help/pkg/idgen"
|
||||
)
|
||||
|
||||
// Global variables
|
||||
var (
|
||||
version = "dev"
|
||||
setReadOnly = flag.Bool("r", false, "set storage.json to read-only mode")
|
||||
showVersion = flag.Bool("v", false, "show version information")
|
||||
log = logrus.New()
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Place defer at the beginning of main to ensure it can catch panics from all subsequent function calls
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
log.Errorf("Panic recovered: %v\n", r)
|
||||
debug.PrintStack()
|
||||
waitExit()
|
||||
}
|
||||
}()
|
||||
|
||||
handleFlags()
|
||||
setupLogger()
|
||||
|
||||
username := getCurrentUser()
|
||||
log.Debug("Running as user:", username)
|
||||
|
||||
// Initialize components
|
||||
display := ui.NewDisplay(nil)
|
||||
configManager := initConfigManager(username)
|
||||
generator := idgen.NewGenerator()
|
||||
processManager := process.NewManager(nil, log)
|
||||
|
||||
// Check and handle privileges
|
||||
if err := handlePrivileges(display); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Setup display
|
||||
setupDisplay(display)
|
||||
|
||||
text := lang.GetText()
|
||||
|
||||
// Handle Cursor processes
|
||||
if err := handleCursorProcesses(display, processManager); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Handle configuration
|
||||
oldConfig := readExistingConfig(display, configManager, text)
|
||||
newConfig := generateNewConfig(display, generator, oldConfig, text)
|
||||
|
||||
if err := saveConfiguration(display, configManager, newConfig); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Show completion messages
|
||||
showCompletionMessages(display)
|
||||
|
||||
if os.Getenv("AUTOMATED_MODE") != "1" {
|
||||
waitExit()
|
||||
}
|
||||
}
|
||||
|
||||
func handleFlags() {
|
||||
flag.Parse()
|
||||
if *showVersion {
|
||||
fmt.Printf("Cursor ID Modifier v%s\n", version)
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
func setupLogger() {
|
||||
log.SetFormatter(&logrus.TextFormatter{
|
||||
FullTimestamp: true,
|
||||
DisableLevelTruncation: true,
|
||||
PadLevelText: true,
|
||||
})
|
||||
log.SetLevel(logrus.InfoLevel)
|
||||
}
|
||||
|
||||
func getCurrentUser() string {
|
||||
if username := os.Getenv("SUDO_USER"); username != "" {
|
||||
return username
|
||||
}
|
||||
|
||||
user, err := user.Current()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return user.Username
|
||||
}
|
||||
|
||||
func initConfigManager(username string) *config.Manager {
|
||||
configManager, err := config.NewManager(username)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return configManager
|
||||
}
|
||||
|
||||
func handlePrivileges(display *ui.Display) error {
|
||||
isAdmin, err := checkAdminPrivileges()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
waitExit()
|
||||
return err
|
||||
}
|
||||
|
||||
if !isAdmin {
|
||||
if runtime.GOOS == "windows" {
|
||||
return handleWindowsPrivileges(display)
|
||||
}
|
||||
display.ShowPrivilegeError(
|
||||
lang.GetText().PrivilegeError,
|
||||
lang.GetText().RunWithSudo,
|
||||
lang.GetText().SudoExample,
|
||||
)
|
||||
waitExit()
|
||||
return fmt.Errorf("insufficient privileges")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleWindowsPrivileges(display *ui.Display) error {
|
||||
message := "\nRequesting administrator privileges..."
|
||||
if lang.GetCurrentLanguage() == lang.CN {
|
||||
message = "\n请求管理员权限..."
|
||||
}
|
||||
fmt.Println(message)
|
||||
|
||||
if err := selfElevate(); err != nil {
|
||||
log.Error(err)
|
||||
display.ShowPrivilegeError(
|
||||
lang.GetText().PrivilegeError,
|
||||
lang.GetText().RunAsAdmin,
|
||||
lang.GetText().RunWithSudo,
|
||||
lang.GetText().SudoExample,
|
||||
)
|
||||
waitExit()
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setupDisplay(display *ui.Display) {
|
||||
if err := display.ClearScreen(); err != nil {
|
||||
log.Warn("Failed to clear screen:", err)
|
||||
}
|
||||
display.ShowLogo()
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func handleCursorProcesses(display *ui.Display, processManager *process.Manager) error {
|
||||
if os.Getenv("AUTOMATED_MODE") == "1" {
|
||||
log.Debug("Running in automated mode, skipping Cursor process closing")
|
||||
return nil
|
||||
}
|
||||
|
||||
display.ShowProgress("Closing Cursor...")
|
||||
log.Debug("Attempting to close Cursor processes")
|
||||
|
||||
if err := processManager.KillCursorProcesses(); err != nil {
|
||||
log.Error("Failed to close Cursor:", err)
|
||||
display.StopProgress()
|
||||
display.ShowError("Failed to close Cursor. Please close it manually and try again.")
|
||||
waitExit()
|
||||
return err
|
||||
}
|
||||
|
||||
if processManager.IsCursorRunning() {
|
||||
log.Error("Cursor processes still detected after closing")
|
||||
display.StopProgress()
|
||||
display.ShowError("Failed to close Cursor completely. Please close it manually and try again.")
|
||||
waitExit()
|
||||
return fmt.Errorf("cursor still running")
|
||||
}
|
||||
|
||||
log.Debug("Successfully closed all Cursor processes")
|
||||
display.StopProgress()
|
||||
fmt.Println()
|
||||
return nil
|
||||
}
|
||||
|
||||
func readExistingConfig(display *ui.Display, configManager *config.Manager, text lang.TextResource) *config.StorageConfig {
|
||||
fmt.Println()
|
||||
display.ShowProgress(text.ReadingConfig)
|
||||
oldConfig, err := configManager.ReadConfig()
|
||||
if err != nil {
|
||||
log.Warn("Failed to read existing config:", err)
|
||||
oldConfig = nil
|
||||
}
|
||||
display.StopProgress()
|
||||
fmt.Println()
|
||||
return oldConfig
|
||||
}
|
||||
|
||||
func generateNewConfig(display *ui.Display, generator *idgen.Generator, oldConfig *config.StorageConfig, text lang.TextResource) *config.StorageConfig {
|
||||
display.ShowProgress(text.GeneratingIds)
|
||||
newConfig := &config.StorageConfig{}
|
||||
|
||||
if machineID, err := generator.GenerateMachineID(); err != nil {
|
||||
log.Fatal("Failed to generate machine ID:", err)
|
||||
} else {
|
||||
newConfig.TelemetryMachineId = machineID
|
||||
}
|
||||
|
||||
if macMachineID, err := generator.GenerateMacMachineID(); err != nil {
|
||||
log.Fatal("Failed to generate MAC machine ID:", err)
|
||||
} else {
|
||||
newConfig.TelemetryMacMachineId = macMachineID
|
||||
}
|
||||
|
||||
if deviceID, err := generator.GenerateDeviceID(); err != nil {
|
||||
log.Fatal("Failed to generate device ID:", err)
|
||||
} else {
|
||||
newConfig.TelemetryDevDeviceId = deviceID
|
||||
}
|
||||
|
||||
if oldConfig != nil && oldConfig.TelemetrySqmId != "" {
|
||||
newConfig.TelemetrySqmId = oldConfig.TelemetrySqmId
|
||||
} else if sqmID, err := generator.GenerateSQMID(); err != nil {
|
||||
log.Fatal("Failed to generate SQM ID:", err)
|
||||
} else {
|
||||
newConfig.TelemetrySqmId = sqmID
|
||||
}
|
||||
|
||||
display.StopProgress()
|
||||
fmt.Println()
|
||||
return newConfig
|
||||
}
|
||||
|
||||
func saveConfiguration(display *ui.Display, configManager *config.Manager, newConfig *config.StorageConfig) error {
|
||||
display.ShowProgress("Saving configuration...")
|
||||
if err := configManager.SaveConfig(newConfig, *setReadOnly); err != nil {
|
||||
log.Error(err)
|
||||
waitExit()
|
||||
return err
|
||||
}
|
||||
display.StopProgress()
|
||||
fmt.Println()
|
||||
return nil
|
||||
}
|
||||
|
||||
func showCompletionMessages(display *ui.Display) {
|
||||
display.ShowSuccess(lang.GetText().SuccessMessage, lang.GetText().RestartMessage)
|
||||
fmt.Println()
|
||||
|
||||
message := "Operation completed!"
|
||||
if lang.GetCurrentLanguage() == lang.CN {
|
||||
message = "操作完成!"
|
||||
}
|
||||
display.ShowInfo(message)
|
||||
}
|
||||
|
||||
func waitExit() {
|
||||
fmt.Print(lang.GetText().PressEnterToExit)
|
||||
os.Stdout.Sync()
|
||||
bufio.NewReader(os.Stdin).ReadString('\n')
|
||||
}
|
||||
|
||||
func checkAdminPrivileges() (bool, error) {
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
cmd := exec.Command("net", "session")
|
||||
return cmd.Run() == nil, nil
|
||||
|
||||
case "darwin", "linux":
|
||||
currentUser, err := user.Current()
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to get current user: %w", err)
|
||||
}
|
||||
return currentUser.Uid == "0", nil
|
||||
|
||||
default:
|
||||
return false, fmt.Errorf("unsupported operating system: %s", runtime.GOOS)
|
||||
}
|
||||
}
|
||||
|
||||
func selfElevate() error {
|
||||
os.Setenv("AUTOMATED_MODE", "1")
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
verb := "runas"
|
||||
exe, _ := os.Executable()
|
||||
cwd, _ := os.Getwd()
|
||||
args := strings.Join(os.Args[1:], " ")
|
||||
|
||||
cmd := exec.Command("cmd", "/C", "start", verb, exe, args)
|
||||
cmd.Dir = cwd
|
||||
return cmd.Run()
|
||||
|
||||
case "darwin", "linux":
|
||||
exe, err := os.Executable()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := exec.Command("sudo", append([]string{exe}, os.Args[1:]...)...)
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
return cmd.Run()
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unsupported operating system: %s", runtime.GOOS)
|
||||
}
|
||||
}
|
@ -1,139 +0,0 @@
|
||||
@echo off
|
||||
chcp 65001 >nul
|
||||
setlocal EnableDelayedExpansion
|
||||
|
||||
:: 版本号
|
||||
set "VERSION=2.5.0"
|
||||
|
||||
:: 检测语言
|
||||
for /f "tokens=2 delims==" %%a in ('wmic os get OSLanguage /value') do set OSLanguage=%%a
|
||||
if "%OSLanguage%"=="2052" (
|
||||
set "LANG=cn"
|
||||
) else (
|
||||
set "LANG=en"
|
||||
)
|
||||
|
||||
:: 多语言文本
|
||||
if "%LANG%"=="cn" (
|
||||
set "SUCCESS_MSG=[√] 配置文件已成功更新!"
|
||||
set "RESTART_MSG=[!] 请手动重启 Cursor 以使更新生效"
|
||||
set "READING_CONFIG=正在读取配置文件..."
|
||||
set "GENERATING_IDS=正在生成新的标识符..."
|
||||
set "CHECKING_PROCESSES=正在检查运行中的 Cursor 实例..."
|
||||
set "CLOSING_PROCESSES=正在关闭 Cursor 实例..."
|
||||
set "PROCESSES_CLOSED=所有 Cursor 实例已关闭"
|
||||
set "PLEASE_WAIT=请稍候..."
|
||||
) else (
|
||||
set "SUCCESS_MSG=[√] Configuration file updated successfully!"
|
||||
set "RESTART_MSG=[!] Please restart Cursor manually for changes to take effect"
|
||||
set "READING_CONFIG=Reading configuration file..."
|
||||
set "GENERATING_IDS=Generating new identifiers..."
|
||||
set "CHECKING_PROCESSES=Checking for running Cursor instances..."
|
||||
set "CLOSING_PROCESSES=Closing Cursor instances..."
|
||||
set "PROCESSES_CLOSED=All Cursor instances have been closed"
|
||||
set "PLEASE_WAIT=Please wait..."
|
||||
)
|
||||
|
||||
:: 检查管理员权限
|
||||
net session >nul 2>&1
|
||||
if %errorLevel% neq 0 (
|
||||
echo 请以管理员身份运行此脚本
|
||||
echo Please run this script as administrator
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
:: 主程序
|
||||
:main
|
||||
cls
|
||||
call :printBanner
|
||||
|
||||
echo %CHECKING_PROCESSES%
|
||||
tasklist | find /i "Cursor.exe" >nul
|
||||
if %errorLevel% equ 0 (
|
||||
echo %CLOSING_PROCESSES%
|
||||
taskkill /F /IM "Cursor.exe" >nul 2>&1
|
||||
timeout /t 2 >nul
|
||||
echo %PROCESSES_CLOSED%
|
||||
)
|
||||
|
||||
set "CONFIG_PATH=%APPDATA%\Cursor\User\globalStorage\storage.json"
|
||||
echo %READING_CONFIG%
|
||||
|
||||
echo %GENERATING_IDS%
|
||||
:: 生成随机ID
|
||||
set "machineId="
|
||||
set "macMachineId="
|
||||
set "devDeviceId="
|
||||
set "sqmId="
|
||||
|
||||
:: 生成32位随机ID
|
||||
for /L %%i in (1,1,32) do (
|
||||
set /a "r=!random! %% 16"
|
||||
set "hex=0123456789abcdef"
|
||||
for %%j in (!r!) do set "machineId=!machineId!!hex:~%%j,1!"
|
||||
)
|
||||
|
||||
for /L %%i in (1,1,32) do (
|
||||
set /a "r=!random! %% 16"
|
||||
for %%j in (!r!) do set "macMachineId=!macMachineId!!hex:~%%j,1!"
|
||||
)
|
||||
|
||||
:: 生成UUID格式的devDeviceId
|
||||
for /L %%i in (1,1,32) do (
|
||||
set /a "r=!random! %% 16"
|
||||
for %%j in (!r!) do set "devDeviceId=!devDeviceId!!hex:~%%j,1!"
|
||||
if %%i==8 set "devDeviceId=!devDeviceId!-"
|
||||
if %%i==12 set "devDeviceId=!devDeviceId!-"
|
||||
if %%i==16 set "devDeviceId=!devDeviceId!-"
|
||||
if %%i==20 set "devDeviceId=!devDeviceId!-"
|
||||
)
|
||||
|
||||
for /L %%i in (1,1,32) do (
|
||||
set /a "r=!random! %% 16"
|
||||
for %%j in (!r!) do set "sqmId=!sqmId!!hex:~%%j,1!"
|
||||
)
|
||||
|
||||
:: 创建配置目录
|
||||
if not exist "%APPDATA%\Cursor\User\globalStorage" (
|
||||
mkdir "%APPDATA%\Cursor\User\globalStorage"
|
||||
)
|
||||
|
||||
:: 生成配置文件
|
||||
(
|
||||
echo {
|
||||
echo "telemetry.macMachineId": "%macMachineId%",
|
||||
echo "telemetry.machineId": "%machineId%",
|
||||
echo "telemetry.devDeviceId": "%devDeviceId%",
|
||||
echo "telemetry.sqmId": "%sqmId%"
|
||||
echo }
|
||||
) > "%CONFIG_PATH%"
|
||||
|
||||
echo.
|
||||
echo ============================================================
|
||||
echo %SUCCESS_MSG%
|
||||
echo %RESTART_MSG%
|
||||
echo ============================================================
|
||||
echo.
|
||||
echo Config file location:
|
||||
echo %CONFIG_PATH%
|
||||
echo.
|
||||
pause
|
||||
exit /b
|
||||
|
||||
:: 打印banner
|
||||
:printBanner
|
||||
echo.
|
||||
echo ██████╗██╗ ██╗██████╗ ███████╗ ██████╗ ██████╗
|
||||
echo ██╔════╝██║ ██║██╔══██╗██╔════╝██╔═══██╗██╔══██╗
|
||||
echo ██║ ██║ ██║██████╔╝███████╗██║ ██║█████╔╝
|
||||
echo ██║ ██║ ██║██╔══██╗╚════██║██║ ██║██╔══██╗
|
||||
echo ╚██████╗╚██████╔╝██║ ██║███████║╚██████╔╝██║ ██║
|
||||
echo ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝
|
||||
echo.
|
||||
echo ^>^> Cursor ID Modifier v1.0 ^<^<
|
||||
echo [ By Pancake Fruit Rolled Shark Chili ]
|
||||
echo.
|
||||
exit /b
|
||||
|
||||
endlocal
|
@ -1,211 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 版本号 - 与其他文件保持一致
|
||||
VERSION="2.5.0"
|
||||
|
||||
# 颜色定义
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m'
|
||||
|
||||
# 语言检测优化
|
||||
detect_language() {
|
||||
local lang
|
||||
if [ -n "$LANG" ]; then
|
||||
lang="$LANG"
|
||||
else
|
||||
lang=$(locale | grep "LANG=" | cut -d= -f2)
|
||||
fi
|
||||
|
||||
if [[ $lang == *"zh"* ]]; then
|
||||
echo "cn"
|
||||
else
|
||||
echo "en"
|
||||
fi
|
||||
}
|
||||
|
||||
LANG=$(detect_language)
|
||||
|
||||
# 多语言文本 - 修复编码问题
|
||||
if [ "$LANG" == "cn" ]; then
|
||||
SUCCESS_MSG="[√] 配置文件已成功更新!"
|
||||
RESTART_MSG="[!] 请手动重启 Cursor 以使更新生效"
|
||||
READING_CONFIG="正在读取配置文件..."
|
||||
GENERATING_IDS="正在生成新的标识符..."
|
||||
CHECKING_PROCESSES="正在检查运行中的 Cursor 实例..."
|
||||
CLOSING_PROCESSES="正在关闭 Cursor 实例..."
|
||||
PROCESSES_CLOSED="所有 Cursor 实例已关闭"
|
||||
PLEASE_WAIT="请稍候..."
|
||||
ERROR_NO_ROOT="请使用 sudo 运行此脚本"
|
||||
ERROR_CONFIG_PATH="无法获取配置文件路径"
|
||||
ERROR_CREATE_DIR="无法创建配置目录"
|
||||
ERROR_WRITE_CONFIG="无法写入配置文件"
|
||||
else
|
||||
SUCCESS_MSG="[√] Configuration file updated successfully!"
|
||||
RESTART_MSG="[!] Please restart Cursor manually for changes to take effect"
|
||||
READING_CONFIG="Reading configuration file..."
|
||||
GENERATING_IDS="Generating new identifiers..."
|
||||
CHECKING_PROCESSES="Checking for running Cursor instances..."
|
||||
CLOSING_PROCESSES="Closing Cursor instances..."
|
||||
PROCESSES_CLOSED="All Cursor instances have been closed"
|
||||
PLEASE_WAIT="Please wait..."
|
||||
ERROR_NO_ROOT="Please run this script with sudo"
|
||||
ERROR_CONFIG_PATH="Unable to get config file path"
|
||||
ERROR_CREATE_DIR="Unable to create config directory"
|
||||
ERROR_WRITE_CONFIG="Unable to write config file"
|
||||
fi
|
||||
|
||||
# 生成随机ID - 添加错误处理
|
||||
generate_machine_id() {
|
||||
if ! command -v openssl >/dev/null 2>&1; then
|
||||
echo "$(head -c 32 /dev/urandom | xxd -p)"
|
||||
else
|
||||
openssl rand -hex 32
|
||||
fi
|
||||
}
|
||||
|
||||
generate_dev_device_id() {
|
||||
local uuid=""
|
||||
if command -v uuidgen >/dev/null 2>&1; then
|
||||
uuid=$(uuidgen)
|
||||
else
|
||||
uuid=$(printf '%04x%04x-%04x-%04x-%04x-%04x%04x%04x' \
|
||||
$RANDOM $RANDOM \
|
||||
$RANDOM \
|
||||
$(($RANDOM & 0x0fff | 0x4000)) \
|
||||
$(($RANDOM & 0x3fff | 0x8000)) \
|
||||
$RANDOM $RANDOM $RANDOM)
|
||||
fi
|
||||
echo "$uuid"
|
||||
}
|
||||
|
||||
# 获取配置文件路径 - 优化路径处理
|
||||
get_config_path() {
|
||||
local username=$1
|
||||
case "$(uname)" in
|
||||
"Darwin")
|
||||
echo "/Users/$username/Library/Application Support/Cursor/User/globalStorage/storage.json"
|
||||
;;
|
||||
"Linux")
|
||||
echo "/home/$username/.config/Cursor/User/globalStorage/storage.json"
|
||||
;;
|
||||
*)
|
||||
echo ""
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# 检查Cursor进程 - 添加错误处理
|
||||
check_cursor_running() {
|
||||
if ! command -v pgrep >/dev/null 2>&1; then
|
||||
ps aux | grep -i "Cursor\|AppRun" | grep -v grep >/dev/null
|
||||
else
|
||||
pgrep -f "Cursor|AppRun" >/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
# 关闭Cursor进程 - 优化进程关闭
|
||||
kill_cursor_processes() {
|
||||
echo -e "${CYAN}$CLOSING_PROCESSES${NC}"
|
||||
if command -v pkill >/dev/null 2>&1; then
|
||||
pkill -f "Cursor|AppRun"
|
||||
else
|
||||
killall Cursor 2>/dev/null
|
||||
killall AppRun 2>/dev/null
|
||||
fi
|
||||
sleep 2
|
||||
if check_cursor_running; then
|
||||
if command -v pkill >/dev/null 2>&1; then
|
||||
pkill -9 -f "Cursor|AppRun"
|
||||
else
|
||||
killall -9 Cursor 2>/dev/null
|
||||
killall -9 AppRun 2>/dev/null
|
||||
fi
|
||||
fi
|
||||
echo -e "${GREEN}$PROCESSES_CLOSED${NC}"
|
||||
}
|
||||
|
||||
# 打印banner - 修复显示问题
|
||||
print_banner() {
|
||||
echo -e "${CYAN}"
|
||||
echo ' ██████╗██╗ ██╗██████╗ ███████╗ ██████╗ ██████╗ '
|
||||
echo ' ██╔════╝██║ ██║██╔══██╗██╔════╝██╔═══██╗██╔══██╗'
|
||||
echo ' ██║ ██║ ██║██████╔╝███████╗██║ ██║██████╔╝'
|
||||
echo ' ██║ ██║ ██║██╔══██╗╚════██║██║ ██║██╔══██╗'
|
||||
echo ' ╚██████╗╚██████╔╝██║ ██║███████║╚██████╔╝██║ ██║'
|
||||
echo ' ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝'
|
||||
echo -e "${NC}"
|
||||
echo -e "${YELLOW}\t\t>> Cursor ID Modifier ${VERSION} <<${NC}"
|
||||
echo -e "${CYAN}\t\t [ By Pancake Fruit Rolled Shark Chili ]${NC}"
|
||||
echo
|
||||
}
|
||||
|
||||
# 主函数 - 添加错误处理
|
||||
main() {
|
||||
# 检查root权限
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo -e "${RED}${ERROR_NO_ROOT}${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 获取实际用户名
|
||||
REAL_USER=${SUDO_USER:-$USER}
|
||||
|
||||
clear
|
||||
print_banner
|
||||
|
||||
# 确保Cursor已关闭
|
||||
if check_cursor_running; then
|
||||
kill_cursor_processes
|
||||
fi
|
||||
|
||||
CONFIG_PATH=$(get_config_path "$REAL_USER")
|
||||
if [ -z "$CONFIG_PATH" ]; then
|
||||
echo -e "${RED}${ERROR_CONFIG_PATH}${NC}"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "${CYAN}$READING_CONFIG${NC}"
|
||||
|
||||
# 生成新配置
|
||||
echo -e "${CYAN}$GENERATING_IDS${NC}"
|
||||
NEW_CONFIG=$(cat <<EOF
|
||||
{
|
||||
"telemetry.macMachineId": "$(generate_machine_id)",
|
||||
"telemetry.machineId": "$(generate_machine_id)",
|
||||
"telemetry.devDeviceId": "$(generate_dev_device_id)",
|
||||
"telemetry.sqmId": "$(generate_machine_id)"
|
||||
}
|
||||
EOF
|
||||
)
|
||||
|
||||
# 创建目录(如果不存在)
|
||||
if ! mkdir -p "$(dirname "$CONFIG_PATH")" 2>/dev/null; then
|
||||
echo -e "${RED}${ERROR_CREATE_DIR}${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 保存配置
|
||||
if ! echo "$NEW_CONFIG" > "$CONFIG_PATH"; then
|
||||
echo -e "${RED}${ERROR_WRITE_CONFIG}${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
chown "$REAL_USER" "$CONFIG_PATH" 2>/dev/null
|
||||
chmod 644 "$CONFIG_PATH" 2>/dev/null
|
||||
|
||||
# 显示成功消息
|
||||
echo -e "\n${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
echo -e "${GREEN}$SUCCESS_MSG${NC}"
|
||||
echo -e "${YELLOW}$RESTART_MSG${NC}"
|
||||
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
|
||||
echo -e "\n配置文件位置/Config file location:"
|
||||
echo -e "${CYAN}$CONFIG_PATH${NC}\n"
|
||||
|
||||
read -p "Press Enter to exit..."
|
||||
}
|
||||
|
||||
main "$@"
|
8
go.mod
@ -1,11 +1,15 @@
|
||||
module cursor-id-modifier
|
||||
module github.com/yuaotian/go-cursor-help
|
||||
|
||||
go 1.21
|
||||
|
||||
require github.com/fatih/color v1.15.0
|
||||
require (
|
||||
github.com/fatih/color v1.15.0
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
github.com/stretchr/testify v1.10.0 // indirect
|
||||
golang.org/x/sys v0.13.0 // indirect
|
||||
)
|
||||
|
16
go.sum
@ -1,3 +1,6 @@
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
@ -5,6 +8,19 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
BIN
img/alipay.png
Normal file
After Width: | Height: | Size: 66 KiB |
BIN
img/alipay_scan_pay.jpg
Normal file
After Width: | Height: | Size: 302 KiB |
BIN
img/etc.png
Normal file
After Width: | Height: | Size: 74 KiB |
BIN
img/pwsh_1.png
Normal file
After Width: | Height: | Size: 72 KiB |
BIN
img/pwsh_2.png
Normal file
After Width: | Height: | Size: 6.4 KiB |
BIN
img/qun-16.png
Normal file
After Width: | Height: | Size: 128 KiB |
BIN
img/run_success.png
Normal file
After Width: | Height: | Size: 1.0 MiB |
BIN
img/wx_group7.jpg
Normal file
After Width: | Height: | Size: 181 KiB |
BIN
img/wx_me.png
Normal file
After Width: | Height: | Size: 384 KiB |
BIN
img/wx_public.jpg
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
img/wx_public_2.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
img/wx_zsm.jpg
Normal file
After Width: | Height: | Size: 60 KiB |
BIN
img/wx_zsm2.png
Normal file
After Width: | Height: | Size: 224 KiB |
BIN
img/zanzhu/twillot.png
Normal file
After Width: | Height: | Size: 11 KiB |
255
install.sh
@ -1,255 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Version / 版本号
|
||||
VERSION="v2.5.0"
|
||||
|
||||
# Configuration / 配置
|
||||
KEEP_BINARY=false
|
||||
DOWNLOAD_DIR="/tmp"
|
||||
INSTALL_DIR="/usr/local/bin"
|
||||
AUTO_SUDO=false
|
||||
|
||||
# Colors / 颜色
|
||||
RED='\033[31m'
|
||||
GREEN='\033[32m'
|
||||
YELLOW='\033[33m'
|
||||
BLUE='\033[36m'
|
||||
BOLD='\033[1m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Separator / 分隔线
|
||||
SEPARATOR="${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||||
|
||||
# Bilingual message functions / 双语消息函数
|
||||
error() {
|
||||
echo -e "\n${SEPARATOR}"
|
||||
echo -e "${RED}${BOLD}❌ Error:${NC} $1"
|
||||
echo -e "${RED}${BOLD}❌ 错误:${NC}$2"
|
||||
echo -e "${SEPARATOR}\n"
|
||||
exit 1
|
||||
}
|
||||
|
||||
info() {
|
||||
echo -e "\n${BLUE}${BOLD}ℹ️ [EN]:${NC} $1"
|
||||
echo -e "${BLUE}${BOLD}ℹ️ [中文]:${NC} $2\n"
|
||||
}
|
||||
|
||||
success() {
|
||||
echo -e "\n${SEPARATOR}"
|
||||
echo -e "${GREEN}${BOLD}✅ [EN]:${NC} $1"
|
||||
echo -e "${GREEN}${BOLD}✅ [中文]:${NC} $2"
|
||||
echo -e "${SEPARATOR}\n"
|
||||
}
|
||||
|
||||
warning() {
|
||||
echo -e "\n${YELLOW}${BOLD}⚠️ [EN]:${NC} $1"
|
||||
echo -e "${YELLOW}${BOLD}⚠️ [中文]:${NC} $2\n"
|
||||
}
|
||||
|
||||
# System detection / 系统检测
|
||||
detect_platform() {
|
||||
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
|
||||
ARCH=$(uname -m)
|
||||
|
||||
case "$OS" in
|
||||
linux*)
|
||||
case "$ARCH" in
|
||||
x86_64) BINARY_NAME="cursor_id_modifier_${VERSION}_linux_amd64" ;;
|
||||
*) error "Unsupported Linux architecture: $ARCH" "不支持的Linux架构:$ARCH" ;;
|
||||
esac
|
||||
;;
|
||||
darwin*)
|
||||
case "$ARCH" in
|
||||
x86_64) BINARY_NAME="cursor_id_modifier_${VERSION}_darwin_amd64_intel" ;;
|
||||
arm64) BINARY_NAME="cursor_id_modifier_${VERSION}_darwin_arm64_m1" ;;
|
||||
*) error "Unsupported macOS architecture: $ARCH" "不支持的macOS架构:$ARCH" ;;
|
||||
esac
|
||||
;;
|
||||
msys*|mingw*|cygwin*)
|
||||
case "$ARCH" in
|
||||
x86_64) BINARY_NAME="cursor_id_modifier_${VERSION}_windows_amd64.exe" ;;
|
||||
*) error "Unsupported Windows architecture: $ARCH" "不支持的Windows架构:$ARCH" ;;
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
error "Unsupported operating system: $OS" "不支持的操作系统:$OS"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# System checks / 系统检查
|
||||
check_requirements() {
|
||||
info "Checking system requirements..." "正在检查系统要求..."
|
||||
|
||||
# Check network connectivity / 检查网络连接
|
||||
if ! ping -c 1 github.com >/dev/null 2>&1; then
|
||||
error "No network connection to GitHub" "无法连接到 GitHub"
|
||||
fi
|
||||
|
||||
# Check curl / 检查curl
|
||||
if ! command -v curl >/dev/null 2>&1; then
|
||||
error "curl is required. Please install curl first." \
|
||||
"需要安装 curl。请先安装 curl 后再运行此脚本。"
|
||||
fi
|
||||
}
|
||||
|
||||
# Privilege check / 权限检查
|
||||
check_privileges() {
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
if [ "$AUTO_SUDO" = "true" ]; then
|
||||
if command -v sudo >/dev/null 2>&1; then
|
||||
info "Re-running with sudo..." "使用 sudo 重新运行..."
|
||||
exec sudo bash "$0" "$@"
|
||||
else
|
||||
error "This script must be run as root. Please use sudo." \
|
||||
"此脚本必须以 root 身份运行。请使用 sudo。"
|
||||
fi
|
||||
else
|
||||
error "This script must be run as root. Please use sudo." \
|
||||
"此脚本必须以 root 身份运行。请使用 sudo。"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Binary verification / 二进制验证
|
||||
verify_binary() {
|
||||
info "Verifying binary..." "正在验证二进制文件..."
|
||||
|
||||
# Check file existence / 检查文件是否存在
|
||||
if [ ! -f "$DOWNLOAD_PATH" ]; then
|
||||
error "Binary file download failed or does not exist" \
|
||||
"二进制文件下载失败或不存在"
|
||||
fi
|
||||
|
||||
# Check executable format / 检查可执行格式
|
||||
if ! file "$DOWNLOAD_PATH" | grep -q "executable"; then
|
||||
error "Downloaded file is not an executable" \
|
||||
"下载的文件不是可执行文件"
|
||||
fi
|
||||
|
||||
# Check file size / 检查文件大小
|
||||
local size=$(wc -c < "$DOWNLOAD_PATH")
|
||||
if [ "$size" -lt 1000000 ]; then # At least 1MB / 至少1MB
|
||||
error "Downloaded file size is abnormal" \
|
||||
"下载的文件大小异常"
|
||||
fi
|
||||
|
||||
# Set executable permissions / 设置可执行权限
|
||||
info "Setting executable permissions..." "正在设置可执行权限..."
|
||||
if ! chmod +x "$DOWNLOAD_PATH"; then
|
||||
error "Failed to set executable permissions" "无法设置可执行权限"
|
||||
fi
|
||||
}
|
||||
|
||||
# Cleanup functions / 清理函数
|
||||
cleanup_old_version() {
|
||||
if [ -f "$INSTALL_DIR/cursor-id-modifier" ]; then
|
||||
info "Removing old version..." "正在删除旧版..."
|
||||
rm -f "$INSTALL_DIR/cursor-id-modifier" || \
|
||||
error "Failed to remove old version" "删除旧版本失败"
|
||||
fi
|
||||
}
|
||||
|
||||
cleanup_temp_files() {
|
||||
if [ "$KEEP_BINARY" = "false" ]; then
|
||||
rm -f "$DOWNLOAD_PATH"
|
||||
rm -f "$INSTALL_DIR/cursor-id-modifier-wrapper"
|
||||
fi
|
||||
}
|
||||
|
||||
# Parse arguments / 解析参数
|
||||
parse_args() {
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--auto-sudo)
|
||||
AUTO_SUDO=true
|
||||
shift
|
||||
;;
|
||||
--keep-binary)
|
||||
KEEP_BINARY=true
|
||||
shift
|
||||
;;
|
||||
--download-dir=*)
|
||||
DOWNLOAD_DIR="${1#*=}"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
# Print banner / 打印横幅
|
||||
print_banner() {
|
||||
echo -e "\n${BLUE}${BOLD}"
|
||||
echo " ██████╗██╗ ██╗██████╗ ███████╗ ██████╗ ██████╗"
|
||||
echo " ██╔════╝██║ ██║██╔══██╗██╔════╝█╔═══██╗██╔══██╗"
|
||||
echo " ██║ ██║ ██║██████╔╝███████╗██║ ██║██████╔╝"
|
||||
echo " ██║ ██║ ██║██╔══██╗╚════██ ██║ ██║██╔══██╗"
|
||||
echo " ╚██████╗╚██████╔╝██║ ██║███████║╚██████╔╝██║ ██║"
|
||||
echo " ╚════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚════╝ ╚═╝ ╚═╝"
|
||||
echo -e "${NC}"
|
||||
echo -e "${YELLOW}${BOLD} >> Cursor ID Modifier ${VERSION} <<${NC}"
|
||||
echo -e "${BLUE}${BOLD} [ By Pancake Fruit Rolled Shark Chili ]${NC}\n"
|
||||
}
|
||||
|
||||
# Main installation process / 主安装流程
|
||||
main() {
|
||||
check_privileges "$@"
|
||||
|
||||
print_banner
|
||||
|
||||
info "Starting installation of cursor-id-modifier ${VERSION}..." \
|
||||
"开始安装 cursor-id-modifier ${VERSION}..."
|
||||
|
||||
detect_platform
|
||||
check_requirements
|
||||
|
||||
# Create installation directory / 创建安装目录
|
||||
mkdir -p "$INSTALL_DIR" 2>/dev/null || \
|
||||
error "Failed to create installation directory" "无法创建安装目录"
|
||||
|
||||
# Download binary / 下载二进制文件
|
||||
info "Downloading cursor-id-modifier ($OS-$ARCH)..." \
|
||||
"正在下载 cursor-id-modifier ($OS-$ARCH)..."
|
||||
|
||||
DOWNLOAD_URL="https://github.com/yuaotian/go-cursor-help/raw/refs/heads/master/bin/$BINARY_NAME"
|
||||
DOWNLOAD_PATH="$DOWNLOAD_DIR/$BINARY_NAME"
|
||||
|
||||
if ! curl -L --progress-bar "$DOWNLOAD_URL" -o "$DOWNLOAD_PATH"; then
|
||||
error "Failed to download binary" "下载二进制文件失败"
|
||||
fi
|
||||
|
||||
success "Download completed" "下载完成"
|
||||
|
||||
verify_binary
|
||||
cleanup_old_version
|
||||
|
||||
# Install binary / 安装二进制文件
|
||||
info "Installing binary..." "正在安装二进制文件..."
|
||||
if ! cp "$DOWNLOAD_PATH" "$INSTALL_DIR/cursor-id-modifier"; then
|
||||
error "Failed to install binary" "安装二进制文件失败"
|
||||
fi
|
||||
|
||||
# Create wrapper script / 创建包装脚本
|
||||
cat > "$INSTALL_DIR/cursor-id-modifier-wrapper" << 'EOF'
|
||||
#!/bin/bash
|
||||
if [ "$(uname -s)" = "Darwin" ]; then
|
||||
sudo /usr/local/bin/cursor-id-modifier "$@"
|
||||
else
|
||||
sudo /usr/local/bin/cursor-id-modifier "$@"
|
||||
fi
|
||||
EOF
|
||||
chmod +x "$INSTALL_DIR/cursor-id-modifier-wrapper"
|
||||
|
||||
# Cleanup / 清理
|
||||
cleanup_temp_files
|
||||
|
||||
success "Installation successful! Run 'cursor-id-modifier-wrapper' from anywhere." \
|
||||
"安装成功!现在可以在任何位置运行 'cursor-id-modifier-wrapper'。"
|
||||
}
|
||||
|
||||
# Start installation / 开始安装
|
||||
parse_args "$@"
|
||||
main "$@"
|
153
internal/config/config.go
Normal file
@ -0,0 +1,153 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// StorageConfig represents the storage configuration
|
||||
type StorageConfig struct {
|
||||
TelemetryMacMachineId string `json:"telemetry.macMachineId"`
|
||||
TelemetryMachineId string `json:"telemetry.machineId"`
|
||||
TelemetryDevDeviceId string `json:"telemetry.devDeviceId"`
|
||||
TelemetrySqmId string `json:"telemetry.sqmId"`
|
||||
LastModified string `json:"lastModified"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
// Manager handles configuration operations
|
||||
type Manager struct {
|
||||
configPath string
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// NewManager creates a new configuration manager
|
||||
func NewManager(username string) (*Manager, error) {
|
||||
configPath, err := getConfigPath(username)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get config path: %w", err)
|
||||
}
|
||||
return &Manager{configPath: configPath}, nil
|
||||
}
|
||||
|
||||
// ReadConfig reads the existing configuration
|
||||
func (m *Manager) ReadConfig() (*StorageConfig, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
data, err := os.ReadFile(m.configPath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, fmt.Errorf("failed to read config file: %w", err)
|
||||
}
|
||||
|
||||
var config StorageConfig
|
||||
if err := json.Unmarshal(data, &config); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse config file: %w", err)
|
||||
}
|
||||
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
// SaveConfig saves the configuration
|
||||
func (m *Manager) SaveConfig(config *StorageConfig, readOnly bool) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
// Ensure parent directories exist
|
||||
if err := os.MkdirAll(filepath.Dir(m.configPath), 0755); err != nil {
|
||||
return fmt.Errorf("failed to create config directory: %w", err)
|
||||
}
|
||||
|
||||
// Prepare updated configuration
|
||||
updatedConfig := m.prepareUpdatedConfig(config)
|
||||
|
||||
// Write configuration
|
||||
if err := m.writeConfigFile(updatedConfig, readOnly); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// prepareUpdatedConfig merges existing config with updates
|
||||
func (m *Manager) prepareUpdatedConfig(config *StorageConfig) map[string]interface{} {
|
||||
// Read existing config
|
||||
originalFile := make(map[string]interface{})
|
||||
if data, err := os.ReadFile(m.configPath); err == nil {
|
||||
json.Unmarshal(data, &originalFile)
|
||||
}
|
||||
|
||||
// Update fields
|
||||
originalFile["telemetry.sqmId"] = config.TelemetrySqmId
|
||||
originalFile["telemetry.macMachineId"] = config.TelemetryMacMachineId
|
||||
originalFile["telemetry.machineId"] = config.TelemetryMachineId
|
||||
originalFile["telemetry.devDeviceId"] = config.TelemetryDevDeviceId
|
||||
originalFile["lastModified"] = time.Now().UTC().Format(time.RFC3339)
|
||||
// originalFile["version"] = "1.0.1"
|
||||
|
||||
return originalFile
|
||||
}
|
||||
|
||||
// writeConfigFile handles the atomic write of the config file
|
||||
func (m *Manager) writeConfigFile(config map[string]interface{}, readOnly bool) error {
|
||||
// Marshal with indentation
|
||||
content, err := json.MarshalIndent(config, "", " ")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal config: %w", err)
|
||||
}
|
||||
|
||||
// Write to temporary file
|
||||
tmpPath := m.configPath + ".tmp"
|
||||
if err := os.WriteFile(tmpPath, content, 0666); err != nil {
|
||||
return fmt.Errorf("failed to write temporary file: %w", err)
|
||||
}
|
||||
|
||||
// Set final permissions
|
||||
fileMode := os.FileMode(0666)
|
||||
if readOnly {
|
||||
fileMode = 0444
|
||||
}
|
||||
|
||||
if err := os.Chmod(tmpPath, fileMode); err != nil {
|
||||
os.Remove(tmpPath)
|
||||
return fmt.Errorf("failed to set temporary file permissions: %w", err)
|
||||
}
|
||||
|
||||
// Atomic rename
|
||||
if err := os.Rename(tmpPath, m.configPath); err != nil {
|
||||
os.Remove(tmpPath)
|
||||
return fmt.Errorf("failed to rename file: %w", err)
|
||||
}
|
||||
|
||||
// Sync directory
|
||||
if dir, err := os.Open(filepath.Dir(m.configPath)); err == nil {
|
||||
defer dir.Close()
|
||||
dir.Sync()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getConfigPath returns the path to the configuration file
|
||||
func getConfigPath(username string) (string, error) {
|
||||
var configDir string
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
configDir = filepath.Join(os.Getenv("APPDATA"), "Cursor", "User", "globalStorage")
|
||||
case "darwin":
|
||||
configDir = filepath.Join("/Users", username, "Library", "Application Support", "Cursor", "User", "globalStorage")
|
||||
case "linux":
|
||||
configDir = filepath.Join("/home", username, ".config", "Cursor", "User", "globalStorage")
|
||||
default:
|
||||
return "", fmt.Errorf("unsupported operating system: %s", runtime.GOOS)
|
||||
}
|
||||
return filepath.Join(configDir, "storage.json"), nil
|
||||
}
|
187
internal/lang/lang.go
Normal file
@ -0,0 +1,187 @@
|
||||
package lang
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Language represents a supported language code
|
||||
type Language string
|
||||
|
||||
const (
|
||||
// CN represents Chinese language
|
||||
CN Language = "cn"
|
||||
// EN represents English language
|
||||
EN Language = "en"
|
||||
)
|
||||
|
||||
// TextResource contains all translatable text resources
|
||||
type TextResource struct {
|
||||
// Success messages
|
||||
SuccessMessage string
|
||||
RestartMessage string
|
||||
|
||||
// Progress messages
|
||||
ReadingConfig string
|
||||
GeneratingIds string
|
||||
CheckingProcesses string
|
||||
ClosingProcesses string
|
||||
ProcessesClosed string
|
||||
PleaseWait string
|
||||
|
||||
// Error messages
|
||||
ErrorPrefix string
|
||||
PrivilegeError string
|
||||
|
||||
// Instructions
|
||||
RunAsAdmin string
|
||||
RunWithSudo string
|
||||
SudoExample string
|
||||
PressEnterToExit string
|
||||
SetReadOnlyMessage string
|
||||
|
||||
// Info messages
|
||||
ConfigLocation string
|
||||
}
|
||||
|
||||
var (
|
||||
currentLanguage Language
|
||||
currentLanguageOnce sync.Once
|
||||
languageMutex sync.RWMutex
|
||||
)
|
||||
|
||||
// GetCurrentLanguage returns the current language, detecting it if not already set
|
||||
func GetCurrentLanguage() Language {
|
||||
currentLanguageOnce.Do(func() {
|
||||
currentLanguage = detectLanguage()
|
||||
})
|
||||
|
||||
languageMutex.RLock()
|
||||
defer languageMutex.RUnlock()
|
||||
return currentLanguage
|
||||
}
|
||||
|
||||
// SetLanguage sets the current language
|
||||
func SetLanguage(lang Language) {
|
||||
languageMutex.Lock()
|
||||
defer languageMutex.Unlock()
|
||||
currentLanguage = lang
|
||||
}
|
||||
|
||||
// GetText returns the TextResource for the current language
|
||||
func GetText() TextResource {
|
||||
return texts[GetCurrentLanguage()]
|
||||
}
|
||||
|
||||
// detectLanguage detects the system language
|
||||
func detectLanguage() Language {
|
||||
// Check environment variables first
|
||||
if isChineseEnvVar() {
|
||||
return CN
|
||||
}
|
||||
|
||||
// Then check OS-specific locale
|
||||
if isWindows() {
|
||||
if isWindowsChineseLocale() {
|
||||
return CN
|
||||
}
|
||||
} else if isUnixChineseLocale() {
|
||||
return CN
|
||||
}
|
||||
|
||||
return EN
|
||||
}
|
||||
|
||||
func isChineseEnvVar() bool {
|
||||
for _, envVar := range []string{"LANG", "LANGUAGE", "LC_ALL"} {
|
||||
if lang := os.Getenv(envVar); lang != "" && strings.Contains(strings.ToLower(lang), "zh") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isWindows() bool {
|
||||
return os.Getenv("OS") == "Windows_NT"
|
||||
}
|
||||
|
||||
func isWindowsChineseLocale() bool {
|
||||
// Check Windows UI culture
|
||||
cmd := exec.Command("powershell", "-Command",
|
||||
"[System.Globalization.CultureInfo]::CurrentUICulture.Name")
|
||||
output, err := cmd.Output()
|
||||
if err == nil && strings.HasPrefix(strings.ToLower(strings.TrimSpace(string(output))), "zh") {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check Windows locale
|
||||
cmd = exec.Command("wmic", "os", "get", "locale")
|
||||
output, err = cmd.Output()
|
||||
return err == nil && strings.Contains(string(output), "2052")
|
||||
}
|
||||
|
||||
func isUnixChineseLocale() bool {
|
||||
cmd := exec.Command("locale")
|
||||
output, err := cmd.Output()
|
||||
return err == nil && strings.Contains(strings.ToLower(string(output)), "zh_cn")
|
||||
}
|
||||
|
||||
// texts contains all translations
|
||||
var texts = map[Language]TextResource{
|
||||
CN: {
|
||||
// Success messages
|
||||
SuccessMessage: "[√] 配置文件已成功更新!",
|
||||
RestartMessage: "[!] 请手动重启 Cursor 以使更新生效",
|
||||
|
||||
// Progress messages
|
||||
ReadingConfig: "正在读取配置文件...",
|
||||
GeneratingIds: "正在生成新的标识符...",
|
||||
CheckingProcesses: "正在检查运行中的 Cursor 实例...",
|
||||
ClosingProcesses: "正在关闭 Cursor 实例...",
|
||||
ProcessesClosed: "所有 Cursor 实例已关闭",
|
||||
PleaseWait: "请稍候...",
|
||||
|
||||
// Error messages
|
||||
ErrorPrefix: "程序发生严重错误: %v",
|
||||
PrivilegeError: "\n[!] 错误:需要管理员权限",
|
||||
|
||||
// Instructions
|
||||
RunAsAdmin: "请右键点击程序,选择「以管理员身份运行」",
|
||||
RunWithSudo: "请使用 sudo 命令运行此程序",
|
||||
SudoExample: "示例: sudo %s",
|
||||
PressEnterToExit: "\n按回车键退出程序...",
|
||||
SetReadOnlyMessage: "设置 storage.json 为只读模式, 这将导致 workspace 记录信息丢失等问题",
|
||||
|
||||
// Info messages
|
||||
ConfigLocation: "配置文件位置:",
|
||||
},
|
||||
EN: {
|
||||
// Success messages
|
||||
SuccessMessage: "[√] Configuration file updated successfully!",
|
||||
RestartMessage: "[!] Please restart Cursor manually for changes to take effect",
|
||||
|
||||
// Progress messages
|
||||
ReadingConfig: "Reading configuration file...",
|
||||
GeneratingIds: "Generating new identifiers...",
|
||||
CheckingProcesses: "Checking for running Cursor instances...",
|
||||
ClosingProcesses: "Closing Cursor instances...",
|
||||
ProcessesClosed: "All Cursor instances have been closed",
|
||||
PleaseWait: "Please wait...",
|
||||
|
||||
// Error messages
|
||||
ErrorPrefix: "Program encountered a serious error: %v",
|
||||
PrivilegeError: "\n[!] Error: Administrator privileges required",
|
||||
|
||||
// Instructions
|
||||
RunAsAdmin: "Please right-click and select 'Run as Administrator'",
|
||||
RunWithSudo: "Please run this program with sudo",
|
||||
SudoExample: "Example: sudo %s",
|
||||
PressEnterToExit: "\nPress Enter to exit...",
|
||||
SetReadOnlyMessage: "Set storage.json to read-only mode, which will cause issues such as lost workspace records",
|
||||
|
||||
// Info messages
|
||||
ConfigLocation: "Config file location:",
|
||||
},
|
||||
}
|
216
internal/process/manager.go
Normal file
@ -0,0 +1,216 @@
|
||||
package process
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Config holds process manager configuration
|
||||
type Config struct {
|
||||
MaxAttempts int // Maximum number of attempts to kill processes
|
||||
RetryDelay time.Duration // Delay between retry attempts
|
||||
ProcessPatterns []string // Process names to look for
|
||||
}
|
||||
|
||||
// DefaultConfig returns the default configuration
|
||||
func DefaultConfig() *Config {
|
||||
return &Config{
|
||||
MaxAttempts: 3,
|
||||
RetryDelay: 2 * time.Second,
|
||||
ProcessPatterns: []string{
|
||||
"Cursor.exe", // Windows executable
|
||||
"Cursor ", // Linux/macOS executable with space
|
||||
"cursor ", // Linux/macOS executable lowercase with space
|
||||
"cursor", // Linux/macOS executable lowercase
|
||||
"Cursor", // Linux/macOS executable
|
||||
"*cursor*", // Any process containing cursor
|
||||
"*Cursor*", // Any process containing Cursor
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Manager handles process-related operations
|
||||
type Manager struct {
|
||||
config *Config
|
||||
log *logrus.Logger
|
||||
}
|
||||
|
||||
// NewManager creates a new process manager with optional config and logger
|
||||
func NewManager(config *Config, log *logrus.Logger) *Manager {
|
||||
if config == nil {
|
||||
config = DefaultConfig()
|
||||
}
|
||||
if log == nil {
|
||||
log = logrus.New()
|
||||
}
|
||||
return &Manager{
|
||||
config: config,
|
||||
log: log,
|
||||
}
|
||||
}
|
||||
|
||||
// IsCursorRunning checks if any Cursor process is currently running
|
||||
func (m *Manager) IsCursorRunning() bool {
|
||||
processes, err := m.getCursorProcesses()
|
||||
if err != nil {
|
||||
m.log.Warn("Failed to get Cursor processes:", err)
|
||||
return false
|
||||
}
|
||||
return len(processes) > 0
|
||||
}
|
||||
|
||||
// KillCursorProcesses attempts to kill all running Cursor processes
|
||||
func (m *Manager) KillCursorProcesses() error {
|
||||
for attempt := 1; attempt <= m.config.MaxAttempts; attempt++ {
|
||||
processes, err := m.getCursorProcesses()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get processes: %w", err)
|
||||
}
|
||||
|
||||
if len(processes) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Try graceful shutdown first on Windows
|
||||
if runtime.GOOS == "windows" {
|
||||
for _, pid := range processes {
|
||||
exec.Command("taskkill", "/PID", pid).Run()
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
// Force kill remaining processes
|
||||
remainingProcesses, _ := m.getCursorProcesses()
|
||||
for _, pid := range remainingProcesses {
|
||||
m.killProcess(pid)
|
||||
}
|
||||
|
||||
time.Sleep(m.config.RetryDelay)
|
||||
|
||||
if processes, _ := m.getCursorProcesses(); len(processes) == 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getCursorProcesses returns PIDs of running Cursor processes
|
||||
func (m *Manager) getCursorProcesses() ([]string, error) {
|
||||
cmd := m.getProcessListCommand()
|
||||
if cmd == nil {
|
||||
return nil, fmt.Errorf("unsupported operating system: %s", runtime.GOOS)
|
||||
}
|
||||
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute command: %w", err)
|
||||
}
|
||||
|
||||
return m.parseProcessList(string(output)), nil
|
||||
}
|
||||
|
||||
// getProcessListCommand returns the appropriate command to list processes based on OS
|
||||
func (m *Manager) getProcessListCommand() *exec.Cmd {
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
return exec.Command("tasklist", "/FO", "CSV", "/NH")
|
||||
case "darwin":
|
||||
return exec.Command("ps", "-ax")
|
||||
case "linux":
|
||||
return exec.Command("ps", "-A")
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// parseProcessList extracts Cursor process PIDs from process list output
|
||||
func (m *Manager) parseProcessList(output string) []string {
|
||||
var processes []string
|
||||
for _, line := range strings.Split(output, "\n") {
|
||||
lowerLine := strings.ToLower(line)
|
||||
|
||||
if m.isOwnProcess(lowerLine) {
|
||||
continue
|
||||
}
|
||||
|
||||
if pid := m.findCursorProcess(line, lowerLine); pid != "" {
|
||||
processes = append(processes, pid)
|
||||
}
|
||||
}
|
||||
return processes
|
||||
}
|
||||
|
||||
// isOwnProcess checks if the process belongs to this application
|
||||
func (m *Manager) isOwnProcess(line string) bool {
|
||||
return strings.Contains(line, "cursor-id-modifier") ||
|
||||
strings.Contains(line, "cursor-helper")
|
||||
}
|
||||
|
||||
// findCursorProcess checks if a process line matches Cursor patterns and returns its PID
|
||||
func (m *Manager) findCursorProcess(line, lowerLine string) string {
|
||||
for _, pattern := range m.config.ProcessPatterns {
|
||||
if m.matchPattern(lowerLine, strings.ToLower(pattern)) {
|
||||
return m.extractPID(line)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// matchPattern checks if a line matches a pattern, supporting wildcards
|
||||
func (m *Manager) matchPattern(line, pattern string) bool {
|
||||
switch {
|
||||
case strings.HasPrefix(pattern, "*") && strings.HasSuffix(pattern, "*"):
|
||||
search := pattern[1 : len(pattern)-1]
|
||||
return strings.Contains(line, search)
|
||||
case strings.HasPrefix(pattern, "*"):
|
||||
return strings.HasSuffix(line, pattern[1:])
|
||||
case strings.HasSuffix(pattern, "*"):
|
||||
return strings.HasPrefix(line, pattern[:len(pattern)-1])
|
||||
default:
|
||||
return line == pattern
|
||||
}
|
||||
}
|
||||
|
||||
// extractPID extracts process ID from a process list line based on OS format
|
||||
func (m *Manager) extractPID(line string) string {
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
parts := strings.Split(line, ",")
|
||||
if len(parts) >= 2 {
|
||||
return strings.Trim(parts[1], "\"")
|
||||
}
|
||||
case "darwin", "linux":
|
||||
parts := strings.Fields(line)
|
||||
if len(parts) >= 1 {
|
||||
return parts[0]
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// killProcess forcefully terminates a process by PID
|
||||
func (m *Manager) killProcess(pid string) error {
|
||||
cmd := m.getKillCommand(pid)
|
||||
if cmd == nil {
|
||||
return fmt.Errorf("unsupported operating system: %s", runtime.GOOS)
|
||||
}
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
// getKillCommand returns the appropriate command to kill a process based on OS
|
||||
func (m *Manager) getKillCommand(pid string) *exec.Cmd {
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
return exec.Command("taskkill", "/F", "/PID", pid)
|
||||
case "darwin", "linux":
|
||||
return exec.Command("kill", "-9", pid)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
94
internal/ui/display.go
Normal file
@ -0,0 +1,94 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
)
|
||||
|
||||
// Display handles UI operations for terminal output
|
||||
type Display struct {
|
||||
spinner *Spinner
|
||||
}
|
||||
|
||||
// NewDisplay creates a new display instance with an optional spinner
|
||||
func NewDisplay(spinner *Spinner) *Display {
|
||||
if spinner == nil {
|
||||
spinner = NewSpinner(nil)
|
||||
}
|
||||
return &Display{spinner: spinner}
|
||||
}
|
||||
|
||||
// Terminal Operations
|
||||
|
||||
// ClearScreen clears the terminal screen based on OS
|
||||
func (d *Display) ClearScreen() error {
|
||||
var cmd *exec.Cmd
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
cmd = exec.Command("cmd", "/c", "cls")
|
||||
default:
|
||||
cmd = exec.Command("clear")
|
||||
}
|
||||
cmd.Stdout = os.Stdout
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
// Progress Indicator
|
||||
|
||||
// ShowProgress displays a progress message with a spinner
|
||||
func (d *Display) ShowProgress(message string) {
|
||||
d.spinner.SetMessage(message)
|
||||
d.spinner.Start()
|
||||
}
|
||||
|
||||
// StopProgress stops the progress spinner
|
||||
func (d *Display) StopProgress() {
|
||||
d.spinner.Stop()
|
||||
}
|
||||
|
||||
// Message Display
|
||||
|
||||
// ShowSuccess displays success messages in green
|
||||
func (d *Display) ShowSuccess(messages ...string) {
|
||||
green := color.New(color.FgGreen)
|
||||
for _, msg := range messages {
|
||||
green.Println(msg)
|
||||
}
|
||||
}
|
||||
|
||||
// ShowInfo displays an info message in cyan
|
||||
func (d *Display) ShowInfo(message string) {
|
||||
cyan := color.New(color.FgCyan)
|
||||
cyan.Println(message)
|
||||
}
|
||||
|
||||
// ShowError displays an error message in red
|
||||
func (d *Display) ShowError(message string) {
|
||||
red := color.New(color.FgRed)
|
||||
red.Println(message)
|
||||
}
|
||||
|
||||
// ShowPrivilegeError displays privilege error messages with instructions
|
||||
func (d *Display) ShowPrivilegeError(messages ...string) {
|
||||
red := color.New(color.FgRed, color.Bold)
|
||||
yellow := color.New(color.FgYellow)
|
||||
|
||||
// Main error message
|
||||
red.Println(messages[0])
|
||||
fmt.Println()
|
||||
|
||||
// Additional instructions
|
||||
for _, msg := range messages[1:] {
|
||||
if strings.Contains(msg, "%s") {
|
||||
exe, _ := os.Executable()
|
||||
yellow.Printf(msg+"\n", exe)
|
||||
} else {
|
||||
yellow.Println(msg)
|
||||
}
|
||||
}
|
||||
}
|
20
internal/ui/logo.go
Normal file
@ -0,0 +1,20 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"github.com/fatih/color"
|
||||
)
|
||||
|
||||
const cyberpunkLogo = `
|
||||
██████╗██╗ ██╗██████╗ ███████╗ ██████╗ ██████╗
|
||||
██╔════╝██║ ██║██╔══██╗██╔════╝██╔═══██╗██╔══██╗
|
||||
██║ ██║ ██║██████╔╝███████╗██║ ██║██████╔╝
|
||||
██║ ██║ ██║██╔══██╗╚════██║██║ ██║██╔══██╗
|
||||
╚██████╗╚██████╔╝██║ ██║███████║╚██████╔╝██║ ██║
|
||||
╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝
|
||||
`
|
||||
|
||||
// ShowLogo displays the application logo
|
||||
func (d *Display) ShowLogo() {
|
||||
cyan := color.New(color.FgCyan, color.Bold)
|
||||
cyan.Println(cyberpunkLogo)
|
||||
}
|
122
internal/ui/spinner.go
Normal file
@ -0,0 +1,122 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/fatih/color"
|
||||
)
|
||||
|
||||
// SpinnerConfig defines spinner configuration
|
||||
type SpinnerConfig struct {
|
||||
Frames []string // Animation frames for the spinner
|
||||
Delay time.Duration // Delay between frame updates
|
||||
}
|
||||
|
||||
// DefaultSpinnerConfig returns the default spinner configuration
|
||||
func DefaultSpinnerConfig() *SpinnerConfig {
|
||||
return &SpinnerConfig{
|
||||
Frames: []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"},
|
||||
Delay: 100 * time.Millisecond,
|
||||
}
|
||||
}
|
||||
|
||||
// Spinner represents a progress spinner
|
||||
type Spinner struct {
|
||||
config *SpinnerConfig
|
||||
message string
|
||||
current int
|
||||
active bool
|
||||
stopCh chan struct{}
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// NewSpinner creates a new spinner with the given configuration
|
||||
func NewSpinner(config *SpinnerConfig) *Spinner {
|
||||
if config == nil {
|
||||
config = DefaultSpinnerConfig()
|
||||
}
|
||||
return &Spinner{
|
||||
config: config,
|
||||
stopCh: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
// State management
|
||||
|
||||
// SetMessage sets the spinner message
|
||||
func (s *Spinner) SetMessage(message string) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.message = message
|
||||
}
|
||||
|
||||
// IsActive returns whether the spinner is currently active
|
||||
func (s *Spinner) IsActive() bool {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
return s.active
|
||||
}
|
||||
|
||||
// Control methods
|
||||
|
||||
// Start begins the spinner animation
|
||||
func (s *Spinner) Start() {
|
||||
s.mu.Lock()
|
||||
if s.active {
|
||||
s.mu.Unlock()
|
||||
return
|
||||
}
|
||||
s.active = true
|
||||
s.mu.Unlock()
|
||||
|
||||
go s.run()
|
||||
}
|
||||
|
||||
// Stop halts the spinner animation
|
||||
func (s *Spinner) Stop() {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
if !s.active {
|
||||
return
|
||||
}
|
||||
|
||||
s.active = false
|
||||
close(s.stopCh)
|
||||
s.stopCh = make(chan struct{})
|
||||
fmt.Print("\r") // Clear the spinner line
|
||||
}
|
||||
|
||||
// Internal methods
|
||||
|
||||
func (s *Spinner) run() {
|
||||
ticker := time.NewTicker(s.config.Delay)
|
||||
defer ticker.Stop()
|
||||
|
||||
cyan := color.New(color.FgCyan, color.Bold)
|
||||
message := s.message
|
||||
|
||||
// Print initial state
|
||||
fmt.Printf("\r %s %s", cyan.Sprint(s.config.Frames[0]), message)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-s.stopCh:
|
||||
return
|
||||
case <-ticker.C:
|
||||
s.mu.RLock()
|
||||
if !s.active {
|
||||
s.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
frame := s.config.Frames[s.current%len(s.config.Frames)]
|
||||
s.current++
|
||||
s.mu.RUnlock()
|
||||
|
||||
fmt.Printf("\r %s", cyan.Sprint(frame))
|
||||
fmt.Printf("\033[%dG%s", 4, message) // Move cursor and print message
|
||||
}
|
||||
}
|
||||
}
|
949
main.go
@ -1,949 +0,0 @@
|
||||
package main
|
||||
|
||||
// Core imports / 核心导入
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/fatih/color"
|
||||
)
|
||||
|
||||
// Types and Constants / 类型和常量
|
||||
type Language string
|
||||
|
||||
const (
|
||||
// Language options / 语言选项
|
||||
CN Language = "cn"
|
||||
EN Language = "en"
|
||||
|
||||
// Version / 版本号
|
||||
Version = "2.5.0"
|
||||
|
||||
// Error types / 错误类型
|
||||
ErrPermission = "permission_error"
|
||||
ErrConfig = "config_error"
|
||||
ErrProcess = "process_error"
|
||||
ErrSystem = "system_error"
|
||||
)
|
||||
|
||||
// Configuration Structures / 配置结构
|
||||
type (
|
||||
// TextResource stores multilingual text / 存储多语言文本
|
||||
TextResource struct {
|
||||
SuccessMessage string
|
||||
RestartMessage string
|
||||
ReadingConfig string
|
||||
GeneratingIds string
|
||||
PressEnterToExit string
|
||||
ErrorPrefix string
|
||||
PrivilegeError string
|
||||
RunAsAdmin string
|
||||
RunWithSudo string
|
||||
SudoExample string
|
||||
ConfigLocation string
|
||||
CheckingProcesses string
|
||||
ClosingProcesses string
|
||||
ProcessesClosed string
|
||||
PleaseWait string
|
||||
}
|
||||
|
||||
// StorageConfig optimized storage configuration / 优化的存储配置
|
||||
StorageConfig struct {
|
||||
TelemetryMacMachineId string `json:"telemetry.macMachineId"`
|
||||
TelemetryMachineId string `json:"telemetry.machineId"`
|
||||
TelemetryDevDeviceId string `json:"telemetry.devDeviceId"`
|
||||
TelemetrySqmId string `json:"telemetry.sqmId"`
|
||||
}
|
||||
// AppError defines error types / 定义错误类型
|
||||
AppError struct {
|
||||
Type string
|
||||
Op string
|
||||
Path string
|
||||
Err error
|
||||
Context map[string]interface{}
|
||||
}
|
||||
|
||||
// Config structures / 配置结构
|
||||
Config struct {
|
||||
Storage StorageConfig
|
||||
UI UIConfig
|
||||
System SystemConfig
|
||||
}
|
||||
|
||||
UIConfig struct {
|
||||
Language Language
|
||||
Theme string
|
||||
Spinner SpinnerConfig
|
||||
}
|
||||
|
||||
SystemConfig struct {
|
||||
RetryAttempts int
|
||||
RetryDelay time.Duration
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
// SpinnerConfig defines spinner configuration / 定义进度条配置
|
||||
SpinnerConfig struct {
|
||||
Frames []string
|
||||
Delay time.Duration
|
||||
}
|
||||
|
||||
// ProgressSpinner for showing progress animation / 用于显示进度动画
|
||||
ProgressSpinner struct {
|
||||
frames []string
|
||||
current int
|
||||
message string
|
||||
}
|
||||
)
|
||||
|
||||
// Global Variables / 全局变量
|
||||
var (
|
||||
currentLanguage = CN // Default to Chinese / 默认为中文
|
||||
|
||||
texts = map[Language]TextResource{
|
||||
CN: {
|
||||
SuccessMessage: "[√] 配置文件已成功更新!",
|
||||
RestartMessage: "[!] 请手动重启 Cursor 以使更新生效",
|
||||
ReadingConfig: "正在读取配置文件...",
|
||||
GeneratingIds: "正在生成新的标识符...",
|
||||
PressEnterToExit: "按回车键退出程序...",
|
||||
ErrorPrefix: "程序发生严重错误: %v",
|
||||
PrivilegeError: "\n[!] 错误:需要管理员权限",
|
||||
RunAsAdmin: "请右键点击程序,选择「以管理员身份运行」",
|
||||
RunWithSudo: "请使用 sudo 命令运行此程序",
|
||||
SudoExample: "示例: sudo %s",
|
||||
ConfigLocation: "配置文件位置:",
|
||||
CheckingProcesses: "正在检查运行中的 Cursor 实例...",
|
||||
ClosingProcesses: "正在关闭 Cursor 实例...",
|
||||
ProcessesClosed: "所有 Cursor 实例已关闭",
|
||||
PleaseWait: "请稍候...",
|
||||
},
|
||||
EN: {
|
||||
SuccessMessage: "[√] Configuration file updated successfully!",
|
||||
RestartMessage: "[!] Please restart Cursor manually for changes to take effect",
|
||||
ReadingConfig: "Reading configuration file...",
|
||||
GeneratingIds: "Generating new identifiers...",
|
||||
PressEnterToExit: "Press Enter to exit...",
|
||||
ErrorPrefix: "Program encountered a serious error: %v",
|
||||
PrivilegeError: "\n[!] Error: Administrator privileges required",
|
||||
RunAsAdmin: "Please right-click and select 'Run as Administrator'",
|
||||
RunWithSudo: "Please run this program with sudo",
|
||||
SudoExample: "Example: sudo %s",
|
||||
ConfigLocation: "Config file location:",
|
||||
CheckingProcesses: "Checking for running Cursor instances...",
|
||||
ClosingProcesses: "Closing Cursor instances...",
|
||||
ProcessesClosed: "All Cursor instances have been closed",
|
||||
PleaseWait: "Please wait...",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// Error Implementation / 错误实现
|
||||
func (e *AppError) Error() string {
|
||||
if e.Context != nil {
|
||||
return fmt.Sprintf("[%s] %s: %v (context: %v)", e.Type, e.Op, e.Err, e.Context)
|
||||
}
|
||||
return fmt.Sprintf("[%s] %s: %v", e.Type, e.Op, e.Err)
|
||||
}
|
||||
|
||||
// Configuration Functions / 配置函数
|
||||
func NewStorageConfig(oldConfig *StorageConfig) *StorageConfig { // Modified to take old config
|
||||
newConfig := &StorageConfig{
|
||||
TelemetryMacMachineId: generateMachineId(),
|
||||
TelemetryMachineId: generateMachineId(),
|
||||
TelemetryDevDeviceId: generateDevDeviceId(),
|
||||
}
|
||||
|
||||
if oldConfig != nil {
|
||||
newConfig.TelemetrySqmId = oldConfig.TelemetrySqmId
|
||||
} else {
|
||||
newConfig.TelemetrySqmId = generateMachineId()
|
||||
}
|
||||
|
||||
if newConfig.TelemetrySqmId == "" {
|
||||
newConfig.TelemetrySqmId = generateMachineId()
|
||||
}
|
||||
|
||||
return newConfig
|
||||
}
|
||||
|
||||
// ID Generation Functions / ID生成函数
|
||||
func generateMachineId() string {
|
||||
data := make([]byte, 32)
|
||||
if _, err := rand.Read(data); err != nil {
|
||||
panic(fmt.Errorf("failed to generate random data: %v", err))
|
||||
}
|
||||
hash := sha256.Sum256(data)
|
||||
return hex.EncodeToString(hash[:])
|
||||
}
|
||||
|
||||
func generateDevDeviceId() string {
|
||||
uuid := make([]byte, 16)
|
||||
if _, err := rand.Read(uuid); err != nil {
|
||||
panic(fmt.Errorf("failed to generate UUID: %v", err))
|
||||
}
|
||||
uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4
|
||||
uuid[8] = (uuid[8] & 0x3f) | 0x80 // RFC 4122 variant
|
||||
return fmt.Sprintf("%x-%x-%x-%x-%x",
|
||||
uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:16])
|
||||
}
|
||||
|
||||
// File Operations / 文件操作
|
||||
func getConfigPath(username string) (string, error) { // Modified to take username
|
||||
var configDir string
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
configDir = filepath.Join(os.Getenv("APPDATA"), "Cursor", "User", "globalStorage")
|
||||
case "darwin":
|
||||
homeDir := filepath.Join("/home/", username)
|
||||
configDir = filepath.Join(homeDir, "Library", "Application Support", "Cursor", "User", "globalStorage")
|
||||
case "linux":
|
||||
homeDir := filepath.Join("/home/", username)
|
||||
configDir = filepath.Join(homeDir, ".config", "Cursor", "User", "globalStorage")
|
||||
default:
|
||||
return "", fmt.Errorf("unsupported operating system: %s", runtime.GOOS)
|
||||
}
|
||||
return filepath.Join(configDir, "storage.json"), nil
|
||||
}
|
||||
|
||||
func saveConfig(config *StorageConfig, username string) error { // Modified to take username
|
||||
configPath, err := getConfigPath(username)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
content, err := json.MarshalIndent(config, "", " ")
|
||||
if err != nil {
|
||||
return &AppError{
|
||||
Type: ErrSystem,
|
||||
Op: "generate JSON",
|
||||
Path: "",
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
// Create parent directories with proper permissions
|
||||
dir := filepath.Dir(configPath)
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
return &AppError{
|
||||
Type: ErrSystem,
|
||||
Op: "create directory",
|
||||
Path: dir,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
// First ensure we can write to the file
|
||||
if err := os.Chmod(configPath, 0666); err != nil && !os.IsNotExist(err) {
|
||||
return &AppError{
|
||||
Type: ErrSystem,
|
||||
Op: "modify file permissions",
|
||||
Path: configPath,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
// Write to temporary file first
|
||||
tmpPath := configPath + ".tmp"
|
||||
if err := os.WriteFile(tmpPath, content, 0666); err != nil {
|
||||
return &AppError{
|
||||
Type: ErrSystem,
|
||||
Op: "write temporary file",
|
||||
Path: tmpPath,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure proper permissions on temporary file
|
||||
if err := os.Chmod(tmpPath, 0444); err != nil {
|
||||
os.Remove(tmpPath)
|
||||
return &AppError{
|
||||
Type: ErrSystem,
|
||||
Op: "set temporary file permissions",
|
||||
Path: tmpPath,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
// Atomic rename
|
||||
if err := os.Rename(tmpPath, configPath); err != nil {
|
||||
os.Remove(tmpPath)
|
||||
return &AppError{
|
||||
Type: ErrSystem,
|
||||
Op: "rename file",
|
||||
Path: configPath,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
// Sync the directory to ensure changes are written to disk
|
||||
if dir, err := os.Open(filepath.Dir(configPath)); err == nil {
|
||||
dir.Sync()
|
||||
dir.Close()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func readExistingConfig(username string) (*StorageConfig, error) { // Modified to take username
|
||||
configPath, err := getConfigPath(username)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(configPath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var config StorageConfig
|
||||
if err := json.Unmarshal(data, &config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
// Process Management / 进程管理
|
||||
type ProcessManager struct {
|
||||
config *SystemConfig
|
||||
}
|
||||
|
||||
func (pm *ProcessManager) killCursorProcesses() error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), pm.config.Timeout)
|
||||
defer cancel()
|
||||
|
||||
for attempt := 0; attempt < pm.config.RetryAttempts; attempt++ {
|
||||
if err := pm.killProcess(ctx); err != nil {
|
||||
time.Sleep(pm.config.RetryDelay)
|
||||
continue
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return &AppError{
|
||||
Type: ErrProcess,
|
||||
Op: "kill_processes",
|
||||
Err: errors.New("failed to kill all Cursor processes after retries"),
|
||||
}
|
||||
}
|
||||
|
||||
func (pm *ProcessManager) killProcess(ctx context.Context) error {
|
||||
if runtime.GOOS == "windows" {
|
||||
return pm.killWindowsProcess(ctx)
|
||||
}
|
||||
return pm.killUnixProcess(ctx)
|
||||
}
|
||||
|
||||
func (pm *ProcessManager) killWindowsProcess(ctx context.Context) error {
|
||||
exec.CommandContext(ctx, "taskkill", "/IM", "Cursor.exe").Run()
|
||||
time.Sleep(pm.config.RetryDelay)
|
||||
exec.CommandContext(ctx, "taskkill", "/F", "/IM", "Cursor.exe").Run()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pm *ProcessManager) killUnixProcess(ctx context.Context) error {
|
||||
// Search for the process by it's executable name (AppRun) in ps output
|
||||
cmd := exec.CommandContext(ctx, "ps", "aux")
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute ps command: %w", err)
|
||||
}
|
||||
|
||||
lines := strings.Split(string(output), "\n")
|
||||
for _, line := range lines {
|
||||
if strings.Contains(line, "AppRun") {
|
||||
parts := strings.Fields(line)
|
||||
if len(parts) > 1 {
|
||||
pid := parts[1]
|
||||
if err := pm.forceKillProcess(ctx, pid); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handle lowercase
|
||||
if strings.Contains(line, "apprun") {
|
||||
parts := strings.Fields(line)
|
||||
if len(parts) > 1 {
|
||||
pid := parts[1]
|
||||
if err := pm.forceKillProcess(ctx, pid); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// helper function to kill process by pid
|
||||
func (pm *ProcessManager) forceKillProcess(ctx context.Context, pid string) error {
|
||||
// First try graceful termination
|
||||
if err := exec.CommandContext(ctx, "kill", pid).Run(); err == nil {
|
||||
// Wait for processes to terminate gracefully
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
|
||||
// Force kill if still running
|
||||
if err := exec.CommandContext(ctx, "kill", "-9", pid).Run(); err != nil {
|
||||
return fmt.Errorf("failed to force kill process %s: %w", pid, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkCursorRunning() bool {
|
||||
cmd := exec.Command("ps", "aux")
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
lines := strings.Split(string(output), "\n")
|
||||
for _, line := range lines {
|
||||
if strings.Contains(line, "AppRun") || strings.Contains(line, "apprun") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// UI Components / UI组件
|
||||
type UI struct {
|
||||
config *UIConfig
|
||||
spinner *ProgressSpinner
|
||||
}
|
||||
|
||||
func NewUI(config *UIConfig) *UI {
|
||||
return &UI{
|
||||
config: config,
|
||||
spinner: NewProgressSpinner(""),
|
||||
}
|
||||
}
|
||||
|
||||
func (ui *UI) showProgress(message string) {
|
||||
ui.spinner.message = message
|
||||
ui.spinner.Start()
|
||||
defer ui.spinner.Stop()
|
||||
|
||||
ticker := time.NewTicker(ui.config.Spinner.Delay)
|
||||
defer ticker.Stop()
|
||||
|
||||
for i := 0; i < 15; i++ {
|
||||
<-ticker.C
|
||||
ui.spinner.Spin()
|
||||
}
|
||||
}
|
||||
|
||||
// Display Functions / 显示函数
|
||||
func showSuccess() {
|
||||
text := texts[currentLanguage]
|
||||
successColor := color.New(color.FgGreen, color.Bold)
|
||||
warningColor := color.New(color.FgYellow, color.Bold)
|
||||
pathColor := color.New(color.FgCyan)
|
||||
|
||||
// Clear any previous output
|
||||
fmt.Println()
|
||||
|
||||
if currentLanguage == EN {
|
||||
// English messages with extra spacing
|
||||
successColor.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
|
||||
successColor.Printf("%s\n", text.SuccessMessage)
|
||||
fmt.Println()
|
||||
warningColor.Printf("%s\n", text.RestartMessage)
|
||||
successColor.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
|
||||
} else {
|
||||
// Chinese messages with extra spacing
|
||||
successColor.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
|
||||
successColor.Printf("%s\n", text.SuccessMessage)
|
||||
fmt.Println()
|
||||
warningColor.Printf("%s\n", text.RestartMessage)
|
||||
successColor.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
|
||||
}
|
||||
|
||||
// Add spacing before config location
|
||||
fmt.Println()
|
||||
|
||||
username := os.Getenv("SUDO_USER")
|
||||
if username == "" {
|
||||
user, err := user.Current()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
username = user.Username
|
||||
}
|
||||
if configPath, err := getConfigPath(username); err == nil {
|
||||
pathColor.Printf("%s\n%s\n", text.ConfigLocation, configPath)
|
||||
}
|
||||
}
|
||||
|
||||
func showPrivilegeError() {
|
||||
text := texts[currentLanguage]
|
||||
red := color.New(color.FgRed, color.Bold)
|
||||
yellow := color.New(color.FgYellow)
|
||||
|
||||
if currentLanguage == EN {
|
||||
red.Println(text.PrivilegeError)
|
||||
if runtime.GOOS == "windows" {
|
||||
yellow.Println(text.RunAsAdmin)
|
||||
} else {
|
||||
yellow.Printf("%s\n%s\n", text.RunWithSudo, fmt.Sprintf(text.SudoExample, os.Args[0]))
|
||||
}
|
||||
} else {
|
||||
red.Printf("\n%s\n", text.PrivilegeError)
|
||||
if runtime.GOOS == "windows" {
|
||||
yellow.Printf("%s\n", text.RunAsAdmin)
|
||||
} else {
|
||||
yellow.Printf("%s\n%s\n", text.RunWithSudo, fmt.Sprintf(text.SudoExample, os.Args[0]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// System Functions / 系统函数
|
||||
func checkAdminPrivileges() (bool, error) {
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
// 使用更可靠的方法检查Windows管理员权限
|
||||
cmd := exec.Command("net", "session")
|
||||
err := cmd.Run()
|
||||
if err == nil {
|
||||
return true, nil
|
||||
}
|
||||
// 如果命令执行失败,说明没有管理员权限
|
||||
return false, nil
|
||||
|
||||
case "darwin", "linux":
|
||||
currentUser, err := user.Current()
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to get current user: %v", err)
|
||||
}
|
||||
return currentUser.Uid == "0", nil
|
||||
|
||||
default:
|
||||
return false, fmt.Errorf("unsupported operating system: %s", runtime.GOOS)
|
||||
}
|
||||
}
|
||||
|
||||
func detectLanguage() Language {
|
||||
// Check common environment variables
|
||||
for _, envVar := range []string{"LANG", "LANGUAGE", "LC_ALL"} {
|
||||
if lang := os.Getenv(envVar); lang != "" {
|
||||
if strings.Contains(strings.ToLower(lang), "zh") {
|
||||
return CN
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Windows-specific language check
|
||||
if runtime.GOOS == "windows" {
|
||||
cmd := exec.Command("powershell", "-Command",
|
||||
"[System.Globalization.CultureInfo]::CurrentUICulture.Name")
|
||||
output, err := cmd.Output()
|
||||
if err == nil {
|
||||
lang := strings.ToLower(strings.TrimSpace(string(output)))
|
||||
if strings.HasPrefix(lang, "zh") {
|
||||
return CN
|
||||
}
|
||||
}
|
||||
|
||||
// Check Windows locale
|
||||
cmd = exec.Command("wmic", "os", "get", "locale")
|
||||
output, err = cmd.Output()
|
||||
if err == nil && strings.Contains(string(output), "2052") {
|
||||
return CN
|
||||
}
|
||||
}
|
||||
|
||||
// Check Unix locale
|
||||
if runtime.GOOS != "windows" {
|
||||
cmd := exec.Command("locale")
|
||||
output, err := cmd.Output()
|
||||
if err == nil && strings.Contains(strings.ToLower(string(output)), "zh_cn") {
|
||||
return CN
|
||||
}
|
||||
}
|
||||
|
||||
return EN
|
||||
}
|
||||
|
||||
func selfElevate() error {
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
// Set automated mode for the elevated process
|
||||
os.Setenv("AUTOMATED_MODE", "1")
|
||||
|
||||
verb := "runas"
|
||||
exe, _ := os.Executable()
|
||||
cwd, _ := os.Getwd()
|
||||
args := strings.Join(os.Args[1:], " ")
|
||||
|
||||
cmd := exec.Command("cmd", "/C", "start", verb, exe, args)
|
||||
cmd.Dir = cwd
|
||||
return cmd.Run()
|
||||
|
||||
case "darwin", "linux":
|
||||
// Set automated mode for the elevated process
|
||||
os.Setenv("AUTOMATED_MODE", "1")
|
||||
|
||||
exe, err := os.Executable()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd := exec.Command("sudo", append([]string{exe}, os.Args[1:]...)...)
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
return cmd.Run()
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unsupported operating system: %s", runtime.GOOS)
|
||||
}
|
||||
}
|
||||
|
||||
// Utility Functions / 实用函数
|
||||
func handleError(err error) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
logger := log.New(os.Stderr, "", log.LstdFlags)
|
||||
|
||||
switch e := err.(type) {
|
||||
case *AppError:
|
||||
logger.Printf("[ERROR] %v\n", e)
|
||||
if e.Type == ErrPermission {
|
||||
showPrivilegeError()
|
||||
}
|
||||
default:
|
||||
logger.Printf("[ERROR] Unexpected error: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func waitExit() {
|
||||
// Skip waiting in automated mode
|
||||
if os.Getenv("AUTOMATED_MODE") == "1" {
|
||||
return
|
||||
}
|
||||
|
||||
if currentLanguage == EN {
|
||||
fmt.Println("\nPress Enter to exit...")
|
||||
} else {
|
||||
fmt.Println("\n按回车键退出程序...")
|
||||
}
|
||||
os.Stdout.Sync()
|
||||
bufio.NewReader(os.Stdin).ReadString('\n')
|
||||
}
|
||||
|
||||
// Add this new function near the other process management functions
|
||||
func ensureCursorClosed() error {
|
||||
maxAttempts := 3
|
||||
text := texts[currentLanguage]
|
||||
|
||||
showProcessStatus(text.CheckingProcesses)
|
||||
|
||||
for attempt := 1; attempt <= maxAttempts; attempt++ {
|
||||
if !checkCursorRunning() {
|
||||
showProcessStatus(text.ProcessesClosed)
|
||||
fmt.Println() // New line after status
|
||||
return nil
|
||||
}
|
||||
|
||||
if currentLanguage == EN {
|
||||
showProcessStatus(fmt.Sprintf("Please close Cursor before continuing. Attempt %d/%d\n%s",
|
||||
attempt, maxAttempts, text.PleaseWait))
|
||||
} else {
|
||||
showProcessStatus(fmt.Sprintf("请在继续之前关闭 Cursor。尝试 %d/%d\n%s",
|
||||
attempt, maxAttempts, text.PleaseWait))
|
||||
}
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
|
||||
return errors.New("cursor is still running")
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Initialize error recovery
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
log.Printf("Panic recovered: %v\n", r)
|
||||
debug.PrintStack()
|
||||
waitExit()
|
||||
}
|
||||
}()
|
||||
|
||||
var username string
|
||||
if username = os.Getenv("SUDO_USER"); username == "" {
|
||||
user, err := user.Current()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
username = user.Username
|
||||
}
|
||||
log.Println("Current user: ", username)
|
||||
|
||||
// Initialize configuration
|
||||
ui := NewUI(&UIConfig{
|
||||
Language: detectLanguage(),
|
||||
Theme: "default",
|
||||
Spinner: SpinnerConfig{
|
||||
Frames: []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"},
|
||||
Delay: 100 * time.Millisecond,
|
||||
},
|
||||
})
|
||||
|
||||
// Check privileges
|
||||
os.Stdout.Sync()
|
||||
currentLanguage = detectLanguage()
|
||||
log.Println("Current language: ", currentLanguage)
|
||||
isAdmin, err := checkAdminPrivileges()
|
||||
if err != nil {
|
||||
handleError(err)
|
||||
waitExit()
|
||||
return
|
||||
}
|
||||
|
||||
if !isAdmin && runtime.GOOS == "windows" {
|
||||
if currentLanguage == EN {
|
||||
fmt.Println("\nRequesting administrator privileges...")
|
||||
} else {
|
||||
fmt.Println("\n请求管理员权限...")
|
||||
}
|
||||
if err := selfElevate(); err != nil {
|
||||
handleError(err)
|
||||
showPrivilegeError()
|
||||
waitExit()
|
||||
return
|
||||
}
|
||||
return
|
||||
} else if !isAdmin {
|
||||
showPrivilegeError()
|
||||
waitExit()
|
||||
return
|
||||
}
|
||||
|
||||
// Ensure all Cursor instances are closed
|
||||
if err := ensureCursorClosed(); err != nil {
|
||||
if currentLanguage == EN {
|
||||
fmt.Println("\nError: Please close Cursor manually before running this program.")
|
||||
} else {
|
||||
fmt.Println("\n错误:请在运行此程序之前手动关闭 Cursor。")
|
||||
}
|
||||
waitExit()
|
||||
return
|
||||
}
|
||||
|
||||
// Process management
|
||||
pm := &ProcessManager{
|
||||
config: &SystemConfig{
|
||||
RetryAttempts: 3,
|
||||
RetryDelay: time.Second,
|
||||
Timeout: 30 * time.Second,
|
||||
},
|
||||
}
|
||||
if checkCursorRunning() {
|
||||
text := texts[currentLanguage]
|
||||
showProcessStatus(text.ClosingProcesses)
|
||||
|
||||
if err := pm.killCursorProcesses(); err != nil {
|
||||
fmt.Println() // New line after status
|
||||
if currentLanguage == EN {
|
||||
fmt.Println("Warning: Could not close all Cursor instances. Please close them manually.")
|
||||
} else {
|
||||
fmt.Println("警告:无法关闭所有 Cursor 实例,请手动关闭。")
|
||||
}
|
||||
waitExit()
|
||||
return
|
||||
}
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
if checkCursorRunning() {
|
||||
fmt.Println() // New line after status
|
||||
if currentLanguage == EN {
|
||||
fmt.Println("\nWarning: Cursor is still running. Please close it manually.")
|
||||
} else {
|
||||
fmt.Println("\n警告:Cursor 仍在运行,请手动关闭。")
|
||||
}
|
||||
waitExit()
|
||||
return
|
||||
}
|
||||
|
||||
showProcessStatus(text.ProcessesClosed)
|
||||
fmt.Println() // New line after status
|
||||
}
|
||||
|
||||
// Clear screen and show banner
|
||||
clearScreen()
|
||||
printCyberpunkBanner()
|
||||
|
||||
// Read and update configuration
|
||||
oldConfig, err := readExistingConfig(username) // add username parameter
|
||||
if err != nil {
|
||||
oldConfig = nil
|
||||
}
|
||||
|
||||
storageConfig, err := loadAndUpdateConfig(ui, username) // add username parameter
|
||||
if err != nil {
|
||||
handleError(err)
|
||||
waitExit()
|
||||
return
|
||||
}
|
||||
|
||||
// Show changes and save
|
||||
showIdComparison(oldConfig, storageConfig)
|
||||
|
||||
if err := saveConfig(storageConfig, username); err != nil { // add username parameter
|
||||
handleError(err)
|
||||
waitExit()
|
||||
return
|
||||
}
|
||||
|
||||
// Show success and exit
|
||||
showSuccess()
|
||||
if currentLanguage == EN {
|
||||
fmt.Println("\nOperation completed!")
|
||||
} else {
|
||||
fmt.Println("\n操作完成!")
|
||||
}
|
||||
|
||||
// Check if running in automated mode
|
||||
if os.Getenv("AUTOMATED_MODE") == "1" {
|
||||
return
|
||||
}
|
||||
|
||||
waitExit()
|
||||
}
|
||||
|
||||
// Progress spinner functions / 进度条函数
|
||||
func NewProgressSpinner(message string) *ProgressSpinner {
|
||||
return &ProgressSpinner{
|
||||
frames: []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"},
|
||||
message: message,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ProgressSpinner) Spin() {
|
||||
frame := s.frames[s.current%len(s.frames)]
|
||||
s.current++
|
||||
fmt.Printf("\r%s %s", color.CyanString(frame), s.message)
|
||||
}
|
||||
|
||||
func (s *ProgressSpinner) Stop() {
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func (s *ProgressSpinner) Start() {
|
||||
s.current = 0
|
||||
}
|
||||
|
||||
// Display utility functions / 显示工具函数
|
||||
func clearScreen() {
|
||||
var cmd *exec.Cmd
|
||||
if runtime.GOOS == "windows" {
|
||||
cmd = exec.Command("cmd", "/c", "cls")
|
||||
} else {
|
||||
cmd = exec.Command("clear")
|
||||
}
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Run()
|
||||
}
|
||||
|
||||
func printCyberpunkBanner() {
|
||||
cyan := color.New(color.FgCyan, color.Bold)
|
||||
yellow := color.New(color.FgYellow, color.Bold)
|
||||
magenta := color.New(color.FgMagenta, color.Bold)
|
||||
green := color.New(color.FgGreen, color.Bold)
|
||||
|
||||
banner := `
|
||||
██████╗██╗ ██╗██████╗ ███████╗ ██████╗ ██████╗
|
||||
██╔════╝██║ ██║██╔══██╗██╔════╝██╔═══██╗██╔══██╗
|
||||
██║ ██║ ██║██████╔╝███████╗██║ ██║█████╔╝
|
||||
██║ ██║ ██║██╔══██╗╚════██║██║ ██║██╔══██╗
|
||||
╚██████╗╚██████╔╝██║ ██║███████║╚██████╔╝██║ ██║
|
||||
╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝
|
||||
`
|
||||
cyan.Println(banner)
|
||||
yellow.Println("\t\t>> Cursor ID Modifier v1.0 <<")
|
||||
magenta.Println("\t\t [ By Pancake Fruit Rolled Shark Chili ]")
|
||||
|
||||
langText := "当前语言/Language: "
|
||||
if currentLanguage == CN {
|
||||
langText += "简体中文"
|
||||
} else {
|
||||
langText += "English"
|
||||
}
|
||||
green.Printf("\n\t\t %s\n\n", langText)
|
||||
}
|
||||
|
||||
func showIdComparison(oldConfig *StorageConfig, newConfig *StorageConfig) {
|
||||
cyan := color.New(color.FgCyan)
|
||||
yellow := color.New(color.FgYellow)
|
||||
|
||||
fmt.Println("\n=== ID Modification Comparison / ID 修改对比 ===")
|
||||
|
||||
if oldConfig != nil {
|
||||
cyan.Println("\n[Original IDs / 原始 ID]")
|
||||
yellow.Printf("Machine ID: %s\n", oldConfig.TelemetryMachineId)
|
||||
yellow.Printf("Mac Machine ID: %s\n", oldConfig.TelemetryMacMachineId)
|
||||
yellow.Printf("Dev Device ID: %s\n", oldConfig.TelemetryDevDeviceId)
|
||||
}
|
||||
|
||||
cyan.Println("\n[Newly Generated IDs / 新生成 ID]")
|
||||
yellow.Printf("Machine ID: %s\n", newConfig.TelemetryMachineId)
|
||||
yellow.Printf("Mac Machine ID: %s\n", newConfig.TelemetryMacMachineId)
|
||||
yellow.Printf("Dev Device ID: %s\n", newConfig.TelemetryDevDeviceId)
|
||||
yellow.Printf("SQM ID: %s\n", newConfig.TelemetrySqmId)
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
// Configuration functions / 配置函数
|
||||
func loadAndUpdateConfig(ui *UI, username string) (*StorageConfig, error) { // add username parameter
|
||||
configPath, err := getConfigPath(username) // add username parameter
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
text := texts[currentLanguage]
|
||||
ui.showProgress(text.ReadingConfig)
|
||||
|
||||
oldConfig, err := readExistingConfig(username) // add username parameter
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return nil, &AppError{
|
||||
Type: ErrSystem,
|
||||
Op: "read config file",
|
||||
Path: configPath,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
ui.showProgress(text.GeneratingIds)
|
||||
return NewStorageConfig(oldConfig), nil
|
||||
}
|
||||
|
||||
// Add a new function to show process status
|
||||
func showProcessStatus(message string) {
|
||||
cyan := color.New(color.FgCyan)
|
||||
fmt.Printf("\r%s", strings.Repeat(" ", 80)) // Clear line
|
||||
fmt.Printf("\r%s", cyan.Sprint("⚡ "+message))
|
||||
}
|
116
pkg/idgen/generator.go
Normal file
@ -0,0 +1,116 @@
|
||||
package idgen
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Generator handles secure ID generation for machines and devices
|
||||
type Generator struct {
|
||||
bufferPool sync.Pool
|
||||
}
|
||||
|
||||
// NewGenerator creates a new ID generator
|
||||
func NewGenerator() *Generator {
|
||||
return &Generator{
|
||||
bufferPool: sync.Pool{
|
||||
New: func() interface{} {
|
||||
return make([]byte, 64)
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Constants for ID generation
|
||||
const (
|
||||
machineIDPrefix = "auth0|user_"
|
||||
uuidFormat = "%s-%s-%s-%s-%s"
|
||||
)
|
||||
|
||||
// generateRandomHex generates a random hex string of specified length
|
||||
func (g *Generator) generateRandomHex(length int) (string, error) {
|
||||
buffer := g.bufferPool.Get().([]byte)
|
||||
defer g.bufferPool.Put(buffer)
|
||||
|
||||
if _, err := rand.Read(buffer[:length]); err != nil {
|
||||
return "", fmt.Errorf("failed to generate random bytes: %w", err)
|
||||
}
|
||||
return hex.EncodeToString(buffer[:length]), nil
|
||||
}
|
||||
|
||||
// GenerateMachineID generates a new machine ID with auth0|user_ prefix
|
||||
func (g *Generator) GenerateMachineID() (string, error) {
|
||||
randomPart, err := g.generateRandomHex(32) // 生成64字符的十六进制
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%x%s", []byte(machineIDPrefix), randomPart), nil
|
||||
}
|
||||
|
||||
// GenerateMacMachineID generates a new 64-byte MAC machine ID
|
||||
func (g *Generator) GenerateMacMachineID() (string, error) {
|
||||
return g.generateRandomHex(32) // 生成64字符的十六进制
|
||||
}
|
||||
|
||||
// GenerateDeviceID generates a new device ID in UUID format
|
||||
func (g *Generator) GenerateDeviceID() (string, error) {
|
||||
id, err := g.generateRandomHex(16)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fmt.Sprintf(uuidFormat,
|
||||
id[0:8], id[8:12], id[12:16], id[16:20], id[20:32]), nil
|
||||
}
|
||||
|
||||
// GenerateSQMID generates a new SQM ID in UUID format (with braces)
|
||||
func (g *Generator) GenerateSQMID() (string, error) {
|
||||
id, err := g.GenerateDeviceID()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fmt.Sprintf("{%s}", id), nil
|
||||
}
|
||||
|
||||
// ValidateID validates the format of various ID types
|
||||
func (g *Generator) ValidateID(id string, idType string) bool {
|
||||
switch idType {
|
||||
case "machineID", "macMachineID":
|
||||
return len(id) == 64 && isHexString(id)
|
||||
case "deviceID":
|
||||
return isValidUUID(id)
|
||||
case "sqmID":
|
||||
if len(id) < 2 || id[0] != '{' || id[len(id)-1] != '}' {
|
||||
return false
|
||||
}
|
||||
return isValidUUID(id[1 : len(id)-1])
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
func isHexString(s string) bool {
|
||||
_, err := hex.DecodeString(s)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func isValidUUID(uuid string) bool {
|
||||
if len(uuid) != 36 {
|
||||
return false
|
||||
}
|
||||
for i, r := range uuid {
|
||||
if i == 8 || i == 13 || i == 18 || i == 23 {
|
||||
if r != '-' {
|
||||
return false
|
||||
}
|
||||
continue
|
||||
}
|
||||
if !((r >= '0' && r <= '9') || (r >= 'a' && r <= 'f') || (r >= 'A' && r <= 'F')) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
375
process_cursor_links.py
Normal file
@ -0,0 +1,375 @@
|
||||
import csv
|
||||
from dataclasses import dataclass
|
||||
from typing import List
|
||||
import json
|
||||
|
||||
@dataclass
|
||||
class CursorVersion:
|
||||
version: str
|
||||
build_id: str
|
||||
|
||||
def get_download_links(self) -> dict:
|
||||
base_url = f"https://downloader.cursor.sh/builds/{self.build_id}"
|
||||
return {
|
||||
"windows": {
|
||||
"x64": f"{base_url}/windows/nsis/x64",
|
||||
"arm64": f"{base_url}/windows/nsis/arm64"
|
||||
},
|
||||
"mac": {
|
||||
"universal": f"{base_url}/mac/installer/universal",
|
||||
"arm64": f"{base_url}/mac/installer/arm64",
|
||||
"x64": f"{base_url}/mac/installer/x64"
|
||||
},
|
||||
"linux": {
|
||||
"x64": f"{base_url}/linux/appImage/x64"
|
||||
}
|
||||
}
|
||||
|
||||
def parse_versions(data: str) -> List[CursorVersion]:
|
||||
versions = []
|
||||
for line in data.strip().split('\n'):
|
||||
if not line:
|
||||
continue
|
||||
version, build_id = line.strip().split(',')
|
||||
versions.append(CursorVersion(version, build_id))
|
||||
return versions
|
||||
|
||||
def generate_markdown(versions: List[CursorVersion]) -> str:
|
||||
md = """# 🖥️ Windows
|
||||
|
||||
## x64
|
||||
<details>
|
||||
<summary style="font-size:1.2em">📦 Windows x64 安装包</summary>
|
||||
|
||||
| 版本 | 下载链接 |
|
||||
|------|----------|
|
||||
"""
|
||||
|
||||
# Windows x64
|
||||
for version in versions:
|
||||
links = version.get_download_links()
|
||||
md += f"| {version.version} | [下载]({links['windows']['x64']}) |\n"
|
||||
|
||||
md += """
|
||||
</details>
|
||||
|
||||
## ARM64
|
||||
<details>
|
||||
<summary style="font-size:1.2em">📱 Windows ARM64 安装包</summary>
|
||||
|
||||
| 版本 | 下载链接 |
|
||||
|------|----------|
|
||||
"""
|
||||
|
||||
# Windows ARM64
|
||||
for version in versions:
|
||||
links = version.get_download_links()
|
||||
md += f"| {version.version} | [下载]({links['windows']['arm64']}) |\n"
|
||||
|
||||
md += """
|
||||
</details>
|
||||
|
||||
# 🍎 macOS
|
||||
|
||||
## Universal
|
||||
<details>
|
||||
<summary style="font-size:1.2em">🎯 macOS Universal 安装包</summary>
|
||||
|
||||
| 版本 | 下载链接 |
|
||||
|------|----------|
|
||||
"""
|
||||
|
||||
# macOS Universal
|
||||
for version in versions:
|
||||
links = version.get_download_links()
|
||||
md += f"| {version.version} | [下载]({links['mac']['universal']}) |\n"
|
||||
|
||||
md += """
|
||||
</details>
|
||||
|
||||
## ARM64
|
||||
<details>
|
||||
<summary style="font-size:1.2em">💪 macOS ARM64 安装包</summary>
|
||||
|
||||
| 版本 | 下载链接 |
|
||||
|------|----------|
|
||||
"""
|
||||
|
||||
# macOS ARM64
|
||||
for version in versions:
|
||||
links = version.get_download_links()
|
||||
md += f"| {version.version} | [下载]({links['mac']['arm64']}) |\n"
|
||||
|
||||
md += """
|
||||
</details>
|
||||
|
||||
## Intel
|
||||
<details>
|
||||
<summary style="font-size:1.2em">💻 macOS Intel 安装包</summary>
|
||||
|
||||
| 版本 | 下载链接 |
|
||||
|------|----------|
|
||||
"""
|
||||
|
||||
# macOS Intel
|
||||
for version in versions:
|
||||
links = version.get_download_links()
|
||||
md += f"| {version.version} | [下载]({links['mac']['x64']}) |\n"
|
||||
|
||||
md += """
|
||||
</details>
|
||||
|
||||
# 🐧 Linux
|
||||
|
||||
## x64
|
||||
<details>
|
||||
<summary style="font-size:1.2em">🎮 Linux x64 AppImage</summary>
|
||||
|
||||
| 版本 | 下载链接 |
|
||||
|------|----------|
|
||||
"""
|
||||
|
||||
# Linux x64
|
||||
for version in versions:
|
||||
links = version.get_download_links()
|
||||
md += f"| {version.version} | [下载]({links['linux']['x64']}) |\n"
|
||||
|
||||
md += """
|
||||
</details>
|
||||
|
||||
<style>
|
||||
details {
|
||||
margin: 1em 0;
|
||||
padding: 0.5em 1em;
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
summary {
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
margin: -0.5em -1em;
|
||||
padding: 0.5em 1em;
|
||||
}
|
||||
|
||||
summary:hover {
|
||||
background: #f1f3f5;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding: 0.5em;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
tr:hover {
|
||||
background: #f1f3f5;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #0366d6;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
"""
|
||||
return md
|
||||
|
||||
def main():
|
||||
# 示例数据
|
||||
data = """
|
||||
0.45.11,250207y6nbaw5qc
|
||||
0.45.10,250205buadkzpea
|
||||
0.45.9,250202tgstl42dt
|
||||
0.45.8,250201b44xw1x2k
|
||||
0.45.7,250130nr6eorv84
|
||||
0.45.6,25013021lv9say3
|
||||
0.45.5,250128loaeyulq8
|
||||
0.45.4,250126vgr3vztvj
|
||||
0.45.3,250124b0rcj0qql
|
||||
0.45.2,250123mhituoa6o
|
||||
0.45.1,2501213ljml5byg
|
||||
0.45.0,250120dh9ezx9pg
|
||||
0.44.11,250103fqxdt5u9z
|
||||
0.44.10,250102ys80vtnud
|
||||
0.44.9,2412268nc6pfzgo
|
||||
0.44.8,241222ooktny8mh
|
||||
0.44.7,2412219nhracv01
|
||||
0.44.6,2412214pmryneua
|
||||
0.44.5,241220s3ux0e1tv
|
||||
0.44.4,241219117fcvexy
|
||||
0.44.3,241218sybfbogmq
|
||||
0.44.2,241218ntls52u8v
|
||||
0.44.0,2412187f9v0nffu
|
||||
0.43.6,241206z7j6me2e2
|
||||
0.43.5,241127pdg4cnbu2
|
||||
0.43.4,241126w13goyvrs
|
||||
0.43.3,2411246yqzx1jmm
|
||||
0.43.1,241124gsiwb66nc
|
||||
0.42.5,24111460bf2loz1
|
||||
0.42.4,2410291z3bdg1dy
|
||||
0.42.3,241016kxu9umuir
|
||||
0.42.2,2410127mj66lvaq
|
||||
0.42.1,241011i66p9fuvm
|
||||
0.42.0,241009fij7nohn5
|
||||
0.41.3,240925fkhcqg263
|
||||
0.41.2,240921llnho65ov
|
||||
0.41.1,2409189xe3envg5
|
||||
0.40.4,2409052yfcjagw2
|
||||
0.40.3,240829epqamqp7h
|
||||
0.40.2,240828c021k3aib
|
||||
0.40.1,2408245thnycuzj
|
||||
0.40.0,24082202sreugb2
|
||||
0.39.6,240819ih4ta2fye
|
||||
0.39.5,240814y9rhzmu7h
|
||||
0.39.4,240810elmeg3seq
|
||||
0.39.3,2408092hoyaxt9m
|
||||
0.39.2,240808phaxh4b5r
|
||||
0.39.1,240807g919tr4ly
|
||||
0.39.0,240802cdixtv9a6
|
||||
0.38.1,240725f0ti25os7
|
||||
0.38.0,240723790oxe4a2
|
||||
0.37.1,240714yrr3gmv3k
|
||||
0.36.2,2407077n6pzboby
|
||||
0.36.1,240706uekt2eaft
|
||||
0.36.0,240703xqkjv5aqa
|
||||
0.35.1,240621pc2f7rl8a
|
||||
0.35.0,240608cv11mfsjl
|
||||
0.34.6,240606kgzq24cfb
|
||||
0.34.6,240605r495newcf
|
||||
0.34.5,240602rq6xovt3a
|
||||
0.34.4,2406014h0rgjghe
|
||||
0.34.3,240529baisuyd2e
|
||||
0.34.2,240528whh1qyo9h
|
||||
0.34.1,24052838ygfselt
|
||||
0.34.0,240527xus72jmkj
|
||||
0.33.4,240511kb8wt1tms
|
||||
0.33.3,2405103lx8342ta
|
||||
0.33.2,240510dwmw395qe
|
||||
0.33.1,2405039a9h2fqc9
|
||||
0.33.0,240503hyjsnhazo
|
||||
0.32.8,240428d499o6zja
|
||||
0.32.7,240427w5guozr0l
|
||||
0.32.2,240417ab4wag7sx
|
||||
0.32.1,2404152czor73fk
|
||||
0.32.0,240412ugli06ue0
|
||||
0.31.3,240402rq154jw46
|
||||
0.31.1,240402pkwfm2ps6
|
||||
0.31.0,2404018j7z0xv2g
|
||||
0.30.5,240327tmd2ozdc7
|
||||
0.30.4,240325dezy8ziab
|
||||
0.30.3,2403229gtuhto9g
|
||||
0.30.2,240322gzqjm3p0d
|
||||
0.30.1,2403212w1ejubt8
|
||||
0.30.0,240320tpx86e7hk
|
||||
0.29.1,2403027twmz0d1t
|
||||
0.29.0,240301kpqvacw2h
|
||||
0.28.1,240226tstim4evd
|
||||
0.28.0,240224g2d7jazcq
|
||||
0.27.4,240219qdbagglqz
|
||||
0.27.3,240218dxhc6y8os
|
||||
0.27.2,240216kkzl9nhxi
|
||||
0.27.1,240215l4ooehnyl
|
||||
0.27.0,240215at6ewkd59
|
||||
0.26.2,240212o6r9qxtcg
|
||||
0.26.1,2402107t904hing
|
||||
0.26.0,240210k8is5xr6v
|
||||
0.25.3,240207aacboj1k8
|
||||
0.25.2,240206p3708uc9z
|
||||
0.25.1,2402033t030rprh
|
||||
0.25.0,240203kh86t91q8
|
||||
0.24.4,240129iecm3e33w
|
||||
0.24.3,2401289dx79qsc0
|
||||
0.24.1,240127cad17436d
|
||||
0.24.0,240126wp9irhmza
|
||||
0.23.9,240124dsmraeml3
|
||||
0.23.8,240123fnn1hj1fg
|
||||
0.23.7,240123xsfe7ywcv
|
||||
0.23.6,240121m1740elox
|
||||
0.23.5,2401215utj6tx6q
|
||||
0.23.4,240121f4qy6ba2y
|
||||
0.23.3,2401201und3ytom
|
||||
0.23.2,240120an2k2hf1i
|
||||
0.23.1,240119fgzxwudn9
|
||||
0.22.2,24011721vsch1l1
|
||||
0.22.1,2401083eyk8kmzc
|
||||
0.22.0,240107qk62kvva3
|
||||
0.21.1,231230h0vi6srww
|
||||
0.21.0,231229ezidnxiu3
|
||||
0.20.2,231219aksf83aad
|
||||
0.20.1,231218ywfaxax09
|
||||
0.20.0,231216nsyfew5j1
|
||||
0.19.1,2312156z2ric57n
|
||||
0.19.0,231214per9qal2p
|
||||
0.18.8,2312098ffjr3ign
|
||||
0.18.7,23120880aolip2i
|
||||
0.18.6,231207ueqazwde8
|
||||
0.18.5,231206jzy2n2sbi
|
||||
0.18.4,2312033zjv5fqai
|
||||
0.18.3,231203k2vnkxq2m
|
||||
0.18.1,23120176kaer07t
|
||||
0.17.0,231127p7iyxn8rg
|
||||
0.16.0,231116rek2xuq6a
|
||||
0.15.5,231115a5mv63u9f
|
||||
0.15.4,23111469e1i3xyi
|
||||
0.15.3,231113b0yv3uqem
|
||||
0.15.2,231113ah0kuf3pf
|
||||
0.15.1,231111yanyyovap
|
||||
0.15.0,231110mdkomczmw
|
||||
0.14.1,231109xitrgihlk
|
||||
0.14.0,231102m6tuamwbx
|
||||
0.13.4,231029rso7pso8l
|
||||
0.13.3,231025uihnjkh9v
|
||||
0.13.2,231024w4iv7xlm6
|
||||
0.13.1,231022f3j0ubckv
|
||||
0.13.0,231022ptw6i4j42
|
||||
0.12.3,231008c5ursm0oj"""
|
||||
|
||||
versions = parse_versions(data)
|
||||
|
||||
# 生成 Markdown 文件
|
||||
markdown_content = generate_markdown(versions)
|
||||
with open('Cursor历史.md', 'w', encoding='utf-8') as f:
|
||||
f.write(markdown_content)
|
||||
|
||||
# 创建结果数据结构
|
||||
result = {
|
||||
"versions": []
|
||||
}
|
||||
|
||||
# 处理每个版本
|
||||
for version in versions:
|
||||
version_info = {
|
||||
"version": version.version,
|
||||
"build_id": version.build_id,
|
||||
"downloads": version.get_download_links()
|
||||
}
|
||||
result["versions"].append(version_info)
|
||||
|
||||
# 保存为JSON文件
|
||||
with open('cursor_downloads.json', 'w', encoding='utf-8') as f:
|
||||
json.dump(result, f, indent=2, ensure_ascii=False)
|
||||
|
||||
# 同时生成CSV格式的下载链接
|
||||
with open('cursor_downloads.csv', 'w', newline='', encoding='utf-8') as f:
|
||||
writer = csv.writer(f)
|
||||
writer.writerow(['Version', 'Platform', 'Architecture', 'Download URL'])
|
||||
|
||||
for version in versions:
|
||||
links = version.get_download_links()
|
||||
for platform, archs in links.items():
|
||||
for arch, url in archs.items():
|
||||
writer.writerow([version.version, platform, arch, url])
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,99 +1,74 @@
|
||||
@echo off
|
||||
setlocal EnableDelayedExpansion
|
||||
|
||||
:: 设置版本信息
|
||||
set VERSION=2.5.0
|
||||
:: Build optimization flags
|
||||
set "OPTIMIZATION_FLAGS=-trimpath -ldflags=\"-s -w\""
|
||||
set "BUILD_JOBS=4"
|
||||
|
||||
:: 设置颜色代码
|
||||
:: Messages / 消息
|
||||
set "EN_MESSAGES[0]=Starting build process for version"
|
||||
set "EN_MESSAGES[1]=Using optimization flags:"
|
||||
set "EN_MESSAGES[2]=Cleaning old builds..."
|
||||
set "EN_MESSAGES[3]=Cleanup completed"
|
||||
set "EN_MESSAGES[4]=Starting builds for all platforms..."
|
||||
set "EN_MESSAGES[5]=Building for"
|
||||
set "EN_MESSAGES[6]=Build successful:"
|
||||
set "EN_MESSAGES[7]=All builds completed!"
|
||||
|
||||
:: Colors
|
||||
set "GREEN=[32m"
|
||||
set "RED=[31m"
|
||||
set "YELLOW=[33m"
|
||||
set "RESET=[0m"
|
||||
|
||||
:: 设置编译优化标志
|
||||
set "LDFLAGS=-s -w"
|
||||
set "BUILDMODE=pie"
|
||||
set "GCFLAGS=-N -l"
|
||||
|
||||
:: 设置 CGO
|
||||
set CGO_ENABLED=0
|
||||
|
||||
:: 显示编译信息
|
||||
echo %YELLOW%开始构建 version %VERSION%%RESET%
|
||||
echo %YELLOW%使用优化标志: LDFLAGS=%LDFLAGS%, BUILDMODE=%BUILDMODE%%RESET%
|
||||
echo %YELLOW%CGO_ENABLED=%CGO_ENABLED%%RESET%
|
||||
|
||||
:: 清理旧的构建文件
|
||||
echo %YELLOW%清理旧的构建文件...%RESET%
|
||||
:: Cleanup function
|
||||
:cleanup
|
||||
if exist "..\bin" (
|
||||
rd /s /q "..\bin"
|
||||
echo %GREEN%清理完成%RESET%
|
||||
) else (
|
||||
echo %YELLOW%bin 目录不存在,无需清理%RESET%
|
||||
echo %GREEN%!EN_MESSAGES[3]!%RESET%
|
||||
)
|
||||
|
||||
:: 创建输出目录
|
||||
mkdir "..\bin" 2>nul
|
||||
|
||||
:: 定义目标平台数组
|
||||
set platforms[0].os=windows
|
||||
set platforms[0].arch=amd64
|
||||
set platforms[0].ext=.exe
|
||||
set platforms[0].suffix=
|
||||
:: Build function with optimizations
|
||||
:build
|
||||
set "os=%~1"
|
||||
set "arch=%~2"
|
||||
set "ext="
|
||||
if "%os%"=="windows" set "ext=.exe"
|
||||
|
||||
set platforms[1].os=darwin
|
||||
set platforms[1].arch=amd64
|
||||
set platforms[1].ext=
|
||||
set platforms[1].suffix=_intel
|
||||
echo %GREEN%!EN_MESSAGES[5]! %os%/%arch%%RESET%
|
||||
|
||||
set platforms[2].os=darwin
|
||||
set platforms[2].arch=arm64
|
||||
set platforms[2].ext=
|
||||
set platforms[2].suffix=_m1
|
||||
set "CGO_ENABLED=0"
|
||||
set "GOOS=%os%"
|
||||
set "GOARCH=%arch%"
|
||||
|
||||
set platforms[3].os=linux
|
||||
set platforms[3].arch=amd64
|
||||
set platforms[3].ext=
|
||||
set platforms[3].suffix=
|
||||
start /b cmd /c "go build -trimpath -ldflags=\"-s -w\" -o ..\bin\%os%\%arch%\cursor-id-modifier%ext% -a -installsuffix cgo -mod=readonly ..\cmd\cursor-id-modifier"
|
||||
exit /b 0
|
||||
|
||||
:: 设置开始时间
|
||||
set start_time=%time%
|
||||
:: Main execution
|
||||
echo %GREEN%!EN_MESSAGES[0]!%RESET%
|
||||
echo %GREEN%!EN_MESSAGES[1]! %OPTIMIZATION_FLAGS%%RESET%
|
||||
|
||||
:: 编译所有目标
|
||||
echo 开始编译所有平台...
|
||||
call :cleanup
|
||||
|
||||
for /L %%i in (0,1,3) do (
|
||||
set "os=!platforms[%%i].os!"
|
||||
set "arch=!platforms[%%i].arch!"
|
||||
set "ext=!platforms[%%i].ext!"
|
||||
set "suffix=!platforms[%%i].suffix!"
|
||||
|
||||
echo.
|
||||
echo Building for !os! !arch!...
|
||||
|
||||
set GOOS=!os!
|
||||
set GOARCH=!arch!
|
||||
|
||||
:: 构建输出文件名
|
||||
set "outfile=..\bin\cursor_id_modifier_v%VERSION%_!os!_!arch!!suffix!!ext!"
|
||||
|
||||
:: 执行构建
|
||||
go build -trimpath -buildmode=%BUILDMODE% -ldflags="%LDFLAGS%" -gcflags="%GCFLAGS%" -o "!outfile!" ..\main.go
|
||||
|
||||
if !errorlevel! equ 0 (
|
||||
echo %GREEN%Build successful: !outfile!%RESET%
|
||||
) else (
|
||||
echo %RED%Build failed for !os! !arch!%RESET%
|
||||
echo %GREEN%!EN_MESSAGES[4]!%RESET%
|
||||
|
||||
:: Start builds in parallel
|
||||
set "pending=0"
|
||||
for %%o in (windows linux darwin) do (
|
||||
for %%a in (amd64 386) do (
|
||||
call :build %%o %%a
|
||||
set /a "pending+=1"
|
||||
if !pending! geq %BUILD_JOBS% (
|
||||
timeout /t 1 /nobreak >nul
|
||||
set "pending=0"
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
:: 计算总耗时
|
||||
set end_time=%time%
|
||||
set /a duration = %end_time:~0,2% * 3600 + %end_time:~3,2% * 60 + %end_time:~6,2% - (%start_time:~0,2% * 3600 + %start_time:~3,2% * 60 + %start_time:~6,2%)
|
||||
:: Wait for all builds to complete
|
||||
:wait_builds
|
||||
timeout /t 2 /nobreak >nul
|
||||
tasklist /fi "IMAGENAME eq go.exe" 2>nul | find "go.exe" >nul
|
||||
if not errorlevel 1 goto wait_builds
|
||||
|
||||
echo.
|
||||
echo %GREEN%所有构建完成! 总耗时: %duration% 秒%RESET%
|
||||
if exist "..\bin" dir /b "..\bin"
|
||||
|
||||
pause
|
||||
endlocal
|
||||
echo %GREEN%!EN_MESSAGES[7]!%RESET%
|
180
scripts/build_all.sh
Normal file → Executable file
@ -1,101 +1,143 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 设置颜色代码
|
||||
# 设置颜色代码 / Set color codes
|
||||
GREEN='\033[0;32m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m' # No Color
|
||||
NC='\033[0m' # No Color / 无颜色
|
||||
|
||||
# 版本信息
|
||||
VERSION="2.5.0"
|
||||
# Build optimization flags
|
||||
OPTIMIZATION_FLAGS="-trimpath -ldflags=\"-s -w\""
|
||||
PARALLEL_JOBS=$(nproc || echo "4") # Get number of CPU cores or default to 4
|
||||
|
||||
# 错误处理函数
|
||||
# Messages / 消息
|
||||
EN_MESSAGES=(
|
||||
"Starting build process for version"
|
||||
"Cleaning old builds..."
|
||||
"Creating bin directory..."
|
||||
"Failed to create bin directory"
|
||||
"Building for"
|
||||
"Successfully built:"
|
||||
"Failed to build for"
|
||||
"Build Summary:"
|
||||
"Successful builds:"
|
||||
"Failed builds:"
|
||||
"Generated files:"
|
||||
)
|
||||
|
||||
CN_MESSAGES=(
|
||||
"开始构建版本"
|
||||
"正在清理旧的构建文件..."
|
||||
"正在创建bin目录..."
|
||||
"创建bin目录失败"
|
||||
"正在构建"
|
||||
"构建成功:"
|
||||
"构建失败:"
|
||||
"构建摘要:"
|
||||
"成功构建数:"
|
||||
"失败构建数:"
|
||||
"生成的文件:"
|
||||
"构建过程被中断"
|
||||
"错误:"
|
||||
)
|
||||
|
||||
# 版本信息 / Version info
|
||||
VERSION="1.0.0"
|
||||
|
||||
# Detect system language / 检测系统语言
|
||||
detect_language() {
|
||||
if [[ $(locale | grep "LANG=zh_CN") ]]; then
|
||||
echo "cn"
|
||||
else
|
||||
echo "en"
|
||||
fi
|
||||
}
|
||||
|
||||
# Get message based on language / 根据语言获取消息
|
||||
get_message() {
|
||||
local index=$1
|
||||
local lang=$(detect_language)
|
||||
|
||||
if [[ "$lang" == "cn" ]]; then
|
||||
echo "${CN_MESSAGES[$index]}"
|
||||
else
|
||||
echo "${EN_MESSAGES[$index]}"
|
||||
fi
|
||||
}
|
||||
|
||||
# 错误处理函数 / Error handling function
|
||||
handle_error() {
|
||||
echo -e "${RED}Error: $1${NC}"
|
||||
echo -e "${RED}$(get_message 12) $1${NC}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 清理函数
|
||||
# 清理函数 / Cleanup function
|
||||
cleanup() {
|
||||
echo "Cleaning old builds..."
|
||||
rm -rf ../bin
|
||||
if [ -d "../bin" ]; then
|
||||
rm -rf ../bin
|
||||
echo -e "${GREEN}$(get_message 1)${NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
# 创建输出目录
|
||||
create_output_dir() {
|
||||
echo "Creating bin directory..."
|
||||
mkdir -p ../bin || handle_error "Failed to create bin directory"
|
||||
}
|
||||
|
||||
# 构建函数
|
||||
# Build function with optimizations
|
||||
build() {
|
||||
local os=$1
|
||||
local arch=$2
|
||||
local suffix=$3
|
||||
local ext=""
|
||||
[ "$os" = "windows" ] && ext=".exe"
|
||||
|
||||
echo -e "\nBuilding for $os ($arch)..."
|
||||
echo -e "${GREEN}$(get_message 4) $os/$arch${NC}"
|
||||
|
||||
output_name="../bin/cursor_id_modifier_v${VERSION}_${os}_${arch}${suffix}"
|
||||
|
||||
GOOS=$os GOARCH=$arch go build -o "$output_name" ../main.go
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo -e "${GREEN}✓ Successfully built: ${output_name}${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ Failed to build for $os $arch${NC}"
|
||||
return 1
|
||||
fi
|
||||
GOOS=$os GOARCH=$arch CGO_ENABLED=0 go build \
|
||||
-trimpath \
|
||||
-ldflags="-s -w" \
|
||||
-o "../bin/$os/$arch/cursor-id-modifier$ext" \
|
||||
-a -installsuffix cgo \
|
||||
-mod=readonly \
|
||||
../cmd/cursor-id-modifier &
|
||||
}
|
||||
|
||||
# 主函数
|
||||
main() {
|
||||
# 显示构建信息
|
||||
echo "Starting build process for version ${VERSION}"
|
||||
# Parallel build execution
|
||||
build_all() {
|
||||
local builds=0
|
||||
local max_parallel=$PARALLEL_JOBS
|
||||
|
||||
# 清理旧文件
|
||||
cleanup
|
||||
|
||||
# 创建输出目录
|
||||
create_output_dir
|
||||
|
||||
# 定义构建目标
|
||||
# Define build targets
|
||||
declare -A targets=(
|
||||
["windows_amd64"]=".exe"
|
||||
["darwin_amd64"]=""
|
||||
["darwin_arm64"]=""
|
||||
["linux_amd64"]=""
|
||||
["linux/amd64"]=1
|
||||
["linux/386"]=1
|
||||
["linux/arm64"]=1
|
||||
["windows/amd64"]=1
|
||||
["windows/386"]=1
|
||||
["darwin/amd64"]=1
|
||||
["darwin/arm64"]=1
|
||||
)
|
||||
|
||||
# 构建计数器
|
||||
local success_count=0
|
||||
local fail_count=0
|
||||
|
||||
# 遍历所有目标进行构建
|
||||
for target in "${!targets[@]}"; do
|
||||
os=${target%_*}
|
||||
arch=${target#*_}
|
||||
suffix=${targets[$target]}
|
||||
IFS='/' read -r os arch <<< "$target"
|
||||
build "$os" "$arch"
|
||||
|
||||
if build "$os" "$arch" "$suffix"; then
|
||||
((success_count++))
|
||||
else
|
||||
((fail_count++))
|
||||
((builds++))
|
||||
|
||||
if ((builds >= max_parallel)); then
|
||||
wait
|
||||
builds=0
|
||||
fi
|
||||
done
|
||||
|
||||
# 显示构建结果
|
||||
echo -e "\nBuild Summary:"
|
||||
echo -e "${GREEN}Successful builds: $success_count${NC}"
|
||||
if [ $fail_count -gt 0 ]; then
|
||||
echo -e "${RED}Failed builds: $fail_count${NC}"
|
||||
fi
|
||||
|
||||
# 显示生成的文件列表
|
||||
echo -e "\nGenerated files:"
|
||||
ls -1 ../bin
|
||||
# Wait for remaining builds
|
||||
wait
|
||||
}
|
||||
|
||||
# 捕获错误信号
|
||||
trap 'echo -e "\n${RED}Build process interrupted${NC}"; exit 1' INT TERM
|
||||
# Main execution
|
||||
main() {
|
||||
cleanup
|
||||
mkdir -p ../bin || { echo -e "${RED}$(get_message 3)${NC}"; exit 1; }
|
||||
build_all
|
||||
echo -e "${GREEN}Build completed successfully${NC}"
|
||||
}
|
||||
|
||||
# 执行主函数
|
||||
main
|
||||
# 捕获错误信号 / Catch error signals
|
||||
trap 'echo -e "\n${RED}$(get_message 11)${NC}"; exit 1' INT TERM
|
||||
|
||||
# 执行主函数 / Execute main function
|
||||
main
|
@ -1,6 +0,0 @@
|
||||
#!/bin/bash
|
||||
echo "Building for Linux..."
|
||||
export GOOS=linux
|
||||
export GOARCH=amd64
|
||||
go build -o ../bin/cursor_id_modifier_linux ../main.go
|
||||
echo "Build complete: ../bin/cursor_id_modifier_linux"
|
@ -1,13 +0,0 @@
|
||||
#!/bin/bash
|
||||
echo "Building for macOS..."
|
||||
export GOOS=darwin
|
||||
export GOARCH=amd64
|
||||
go build -o ../bin/cursor_id_modifier_mac ../main.go
|
||||
echo "Build complete: ../bin/cursor_id_modifier_mac"
|
||||
|
||||
# Build for Apple Silicon
|
||||
echo "Building for macOS ARM64..."
|
||||
export GOOS=darwin
|
||||
export GOARCH=arm64
|
||||
go build -o ../bin/cursor_id_modifier_mac_arm64 ../main.go
|
||||
echo "Build complete: ../bin/cursor_id_modifier_mac_arm64"
|
@ -1,6 +0,0 @@
|
||||
@echo off
|
||||
echo Building for Windows...
|
||||
set GOOS=windows
|
||||
set GOARCH=amd64
|
||||
go build -o ../bin/cursor_id_modifier.exe ../main.go
|
||||
echo Build complete: ../bin/cursor_id_modifier.exe
|
318
scripts/cursor_id_modifier.pot
Normal file
@ -0,0 +1,318 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: cursor_id_modifier\n"
|
||||
"POT-Creation-Date: 2025-04-25 12:00+0000\n"
|
||||
"PO-Revision-Date: 2025-04-25 12:00+0000\n"
|
||||
"Language-Team: None\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "Error: No translation file found for domain 'cursor_id_modifier' in {}/zh_CN/LC_MESSAGES/"
|
||||
msgstr ""
|
||||
|
||||
msgid "========== Cursor ID modification tool log start {} =========="
|
||||
msgstr ""
|
||||
|
||||
msgid "[INFO] {} {}"
|
||||
msgstr ""
|
||||
|
||||
msgid "[WARN] {} {}"
|
||||
msgstr ""
|
||||
|
||||
msgid "[ERROR] {} {}"
|
||||
msgstr ""
|
||||
|
||||
msgid "[DEBUG] {} {}"
|
||||
msgstr ""
|
||||
|
||||
msgid "[CMD] {} Executing command: {}"
|
||||
msgstr ""
|
||||
|
||||
msgid "[CMD] {}:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Unable to get username"
|
||||
msgstr ""
|
||||
|
||||
msgid "Finding Cursor installation path..."
|
||||
msgstr ""
|
||||
|
||||
msgid "Found Cursor installation path: {}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Found Cursor via which: {}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Cursor executable not found, will try using config directory"
|
||||
msgstr ""
|
||||
|
||||
msgid "Found Cursor via search: {}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Finding Cursor resource directory..."
|
||||
msgstr ""
|
||||
|
||||
msgid "Found Cursor resource directory: {}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Found resource directory via binary path: {}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Cursor resource directory not found"
|
||||
msgstr ""
|
||||
|
||||
msgid "Please run this script with sudo"
|
||||
msgstr ""
|
||||
|
||||
msgid "Example: sudo {}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Checking Cursor processes..."
|
||||
msgstr ""
|
||||
|
||||
msgid "Getting process details for {}:"
|
||||
msgstr ""
|
||||
|
||||
msgid "No running Cursor processes found"
|
||||
msgstr ""
|
||||
|
||||
msgid "Found running Cursor processes"
|
||||
msgstr ""
|
||||
|
||||
msgid "Attempting to terminate Cursor processes..."
|
||||
msgstr ""
|
||||
|
||||
msgid "Attempting to forcefully terminate processes..."
|
||||
msgstr ""
|
||||
|
||||
msgid "Waiting for processes to terminate, attempt {}/{}..."
|
||||
msgstr ""
|
||||
|
||||
msgid "Cursor processes successfully terminated"
|
||||
msgstr ""
|
||||
|
||||
msgid "Unable to terminate Cursor processes after {} attempts"
|
||||
msgstr ""
|
||||
|
||||
msgid "Please manually terminate the processes and try again"
|
||||
msgstr ""
|
||||
|
||||
msgid "Configuration file does not exist, skipping backup"
|
||||
msgstr ""
|
||||
|
||||
msgid "Configuration backed up to: {}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Backup failed"
|
||||
msgstr ""
|
||||
|
||||
msgid "File does not exist: {}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Unable to modify file permissions: {}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Generated temporary file is empty"
|
||||
msgstr ""
|
||||
|
||||
msgid "Unable to write to file: {}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Machine code reset options"
|
||||
msgstr ""
|
||||
|
||||
msgid "Do you need to reset the machine code? (Usually, modifying JS files is sufficient):"
|
||||
msgstr ""
|
||||
|
||||
msgid "Don't reset - only modify JS files"
|
||||
msgstr ""
|
||||
|
||||
msgid "Reset - modify both config file and machine code"
|
||||
msgstr ""
|
||||
|
||||
msgid "[INPUT_DEBUG] Machine code reset option selected: {}"
|
||||
msgstr ""
|
||||
|
||||
msgid "You chose to reset the machine code"
|
||||
msgstr ""
|
||||
|
||||
msgid "Found existing configuration file: {}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setting new device and machine IDs..."
|
||||
msgstr ""
|
||||
|
||||
msgid "New device ID: {}"
|
||||
msgstr ""
|
||||
|
||||
msgid "New machine ID: {}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Configuration file modified successfully"
|
||||
msgstr ""
|
||||
|
||||
msgid "Configuration file modification failed"
|
||||
msgstr ""
|
||||
|
||||
msgid "Configuration file not found, this is normal, skipping ID modification"
|
||||
msgstr ""
|
||||
|
||||
msgid "You chose not to reset the machine code, will only modify JS files"
|
||||
msgstr ""
|
||||
|
||||
msgid "Configuration processing completed"
|
||||
msgstr ""
|
||||
|
||||
msgid "Finding Cursor's JS files..."
|
||||
msgstr ""
|
||||
|
||||
msgid "Searching for JS files in resource directory: {}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Found JS file: {}"
|
||||
msgstr ""
|
||||
|
||||
msgid "No JS files found in resource directory, trying other directories..."
|
||||
msgstr ""
|
||||
|
||||
msgid "Searching directory: {}"
|
||||
msgstr ""
|
||||
|
||||
msgid "No modifiable JS files found"
|
||||
msgstr ""
|
||||
|
||||
msgid "Found {} JS files to modify"
|
||||
msgstr ""
|
||||
|
||||
msgid "Starting to modify Cursor's JS files..."
|
||||
msgstr ""
|
||||
|
||||
msgid "Unable to find modifiable JS files"
|
||||
msgstr ""
|
||||
|
||||
msgid "Processing file: {}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Unable to create backup for file: {}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Found x-cursor-checksum setting code"
|
||||
msgstr ""
|
||||
|
||||
msgid "Successfully modified x-cursor-checksum setting code"
|
||||
msgstr ""
|
||||
|
||||
msgid "Failed to modify x-cursor-checksum setting code"
|
||||
msgstr ""
|
||||
|
||||
msgid "Found IOPlatformUUID keyword"
|
||||
msgstr ""
|
||||
|
||||
msgid "Successfully injected randomUUID call into a$ function"
|
||||
msgstr ""
|
||||
|
||||
msgid "Failed to modify a$ function"
|
||||
msgstr ""
|
||||
|
||||
msgid "Successfully injected randomUUID call into v5 function"
|
||||
msgstr ""
|
||||
|
||||
msgid "Failed to modify v5 function"
|
||||
msgstr ""
|
||||
|
||||
msgid "Completed universal modification"
|
||||
msgstr ""
|
||||
|
||||
msgid "File already contains custom injection code, skipping modification"
|
||||
msgstr ""
|
||||
|
||||
msgid "Completed most universal injection"
|
||||
msgstr ""
|
||||
|
||||
msgid "File has already been modified, skipping modification"
|
||||
msgstr ""
|
||||
|
||||
msgid "Failed to modify any JS files"
|
||||
msgstr ""
|
||||
|
||||
msgid "Successfully modified {} JS files"
|
||||
msgstr ""
|
||||
|
||||
msgid "Disabling Cursor auto-update..."
|
||||
msgstr ""
|
||||
|
||||
msgid "Found update configuration file: {}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Disabled update configuration file: {}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Found updater: {}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Disabled updater: {}"
|
||||
msgstr ""
|
||||
|
||||
msgid "No update configuration files or updaters found"
|
||||
msgstr ""
|
||||
|
||||
msgid "Successfully disabled auto-update"
|
||||
msgstr ""
|
||||
|
||||
msgid "You selected: {}"
|
||||
msgstr ""
|
||||
|
||||
msgid "This script only supports Linux systems"
|
||||
msgstr ""
|
||||
|
||||
msgid "Script started..."
|
||||
msgstr ""
|
||||
|
||||
msgid "System information: {}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Current user: {}"
|
||||
msgstr ""
|
||||
|
||||
msgid "System version information"
|
||||
msgstr ""
|
||||
|
||||
msgid "Cursor Linux startup tool"
|
||||
msgstr ""
|
||||
|
||||
msgid "Important notice"
|
||||
msgstr ""
|
||||
|
||||
msgid "This tool prioritizes modifying JS files, which is safer and more reliable"
|
||||
msgstr ""
|
||||
|
||||
msgid "Modifying Cursor JS files..."
|
||||
msgstr ""
|
||||
|
||||
msgid "JS files modified successfully!"
|
||||
msgstr ""
|
||||
|
||||
msgid "JS file modification failed, but configuration file modification may have succeeded"
|
||||
msgstr ""
|
||||
|
||||
msgid "If Cursor still indicates the device is disabled after restarting, please rerun this script"
|
||||
msgstr ""
|
||||
|
||||
msgid "Please restart Cursor to apply the new configuration"
|
||||
msgstr ""
|
||||
|
||||
msgid "Follow the WeChat public account [Pancake AI] to discuss more Cursor tips and AI knowledge (script is free, join the group via the public account for more tips and experts)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Script execution completed"
|
||||
msgstr ""
|
||||
|
||||
msgid "========== Cursor ID modification tool log end {} =========="
|
||||
msgstr ""
|
||||
|
||||
msgid "Detailed log saved to: {}"
|
||||
msgstr ""
|
||||
|
||||
msgid "If you encounter issues, please provide this log file to the developer for troubleshooting"
|
||||
msgstr ""
|
1298
scripts/cursor_id_modifier.py
Normal file
9
scripts/git-actions.sh
Executable file
@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
REPO_DIR="$PWD"
|
||||
LOCALES_DIR="$REPO_DIR/locales"
|
||||
msginit -i cursor_id_modifier.pot -o $LOCALES_DIR/en_US/LC_MESSAGES/cursor_id_modifier.po -l en_US
|
||||
for lang in en_US zh_CN; do
|
||||
cd $LOCALES_DIR/$lang/LC_MESSAGES
|
||||
msgfmt -o cursor_id_modifier.mo cursor_id_modifier.po
|
||||
done
|
||||
|
193
scripts/install.ps1
Normal file
@ -0,0 +1,193 @@
|
||||
# Check for admin rights and handle elevation
|
||||
$isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")
|
||||
if (-NOT $isAdmin) {
|
||||
# Detect PowerShell version and path
|
||||
$pwshPath = if (Get-Command "pwsh" -ErrorAction SilentlyContinue) {
|
||||
(Get-Command "pwsh").Source # PowerShell 7+
|
||||
} elseif (Test-Path "$env:ProgramFiles\PowerShell\7\pwsh.exe") {
|
||||
"$env:ProgramFiles\PowerShell\7\pwsh.exe"
|
||||
} else {
|
||||
"powershell.exe" # Windows PowerShell
|
||||
}
|
||||
|
||||
try {
|
||||
Write-Host "`nRequesting administrator privileges..." -ForegroundColor Cyan
|
||||
$scriptPath = $MyInvocation.MyCommand.Path
|
||||
$argList = "-NoProfile -ExecutionPolicy Bypass -File `"$scriptPath`""
|
||||
Start-Process -FilePath $pwshPath -Verb RunAs -ArgumentList $argList -Wait
|
||||
exit
|
||||
}
|
||||
catch {
|
||||
Write-Host "`nError: Administrator privileges required" -ForegroundColor Red
|
||||
Write-Host "Please run this script from an Administrator PowerShell window" -ForegroundColor Yellow
|
||||
Write-Host "`nTo do this:" -ForegroundColor Cyan
|
||||
Write-Host "1. Press Win + X" -ForegroundColor White
|
||||
Write-Host "2. Click 'Windows Terminal (Admin)' or 'PowerShell (Admin)'" -ForegroundColor White
|
||||
Write-Host "3. Run the installation command again" -ForegroundColor White
|
||||
Write-Host "`nPress enter to exit..."
|
||||
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown')
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
# Set TLS to 1.2
|
||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||
|
||||
# Create temporary directory
|
||||
$TmpDir = Join-Path $env:TEMP ([System.Guid]::NewGuid().ToString())
|
||||
New-Item -ItemType Directory -Path $TmpDir | Out-Null
|
||||
|
||||
# Cleanup function
|
||||
function Cleanup {
|
||||
if (Test-Path $TmpDir) {
|
||||
Remove-Item -Recurse -Force $TmpDir
|
||||
}
|
||||
}
|
||||
|
||||
# Error handler
|
||||
trap {
|
||||
Write-Host "Error: $_" -ForegroundColor Red
|
||||
Cleanup
|
||||
Write-Host "Press enter to exit..."
|
||||
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown')
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Detect system architecture
|
||||
function Get-SystemArch {
|
||||
if ([Environment]::Is64BitOperatingSystem) {
|
||||
return "x86_64"
|
||||
} else {
|
||||
return "i386"
|
||||
}
|
||||
}
|
||||
|
||||
# Download with progress
|
||||
function Get-FileWithProgress {
|
||||
param (
|
||||
[string]$Url,
|
||||
[string]$OutputFile
|
||||
)
|
||||
|
||||
try {
|
||||
$webClient = New-Object System.Net.WebClient
|
||||
$webClient.Headers.Add("User-Agent", "PowerShell Script")
|
||||
|
||||
$webClient.DownloadFile($Url, $OutputFile)
|
||||
return $true
|
||||
}
|
||||
catch {
|
||||
Write-Host "Failed to download: $_" -ForegroundColor Red
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
# Main installation function
|
||||
function Install-CursorModifier {
|
||||
Write-Host "Starting installation..." -ForegroundColor Cyan
|
||||
|
||||
# Detect architecture
|
||||
$arch = Get-SystemArch
|
||||
Write-Host "Detected architecture: $arch" -ForegroundColor Green
|
||||
|
||||
# Set installation directory
|
||||
$InstallDir = "$env:ProgramFiles\CursorModifier"
|
||||
if (!(Test-Path $InstallDir)) {
|
||||
New-Item -ItemType Directory -Path $InstallDir | Out-Null
|
||||
}
|
||||
|
||||
# Get latest release
|
||||
try {
|
||||
$latestRelease = Invoke-RestMethod -Uri "https://api.github.com/repos/yuaotian/go-cursor-help/releases/latest"
|
||||
Write-Host "Found latest release: $($latestRelease.tag_name)" -ForegroundColor Cyan
|
||||
|
||||
# Look for Windows binary with our architecture
|
||||
$version = $latestRelease.tag_name.TrimStart('v')
|
||||
Write-Host "Version: $version" -ForegroundColor Cyan
|
||||
$possibleNames = @(
|
||||
"cursor-id-modifier_${version}_windows_x86_64.exe",
|
||||
"cursor-id-modifier_${version}_windows_$($arch).exe"
|
||||
)
|
||||
|
||||
$asset = $null
|
||||
foreach ($name in $possibleNames) {
|
||||
Write-Host "Checking for asset: $name" -ForegroundColor Cyan
|
||||
$asset = $latestRelease.assets | Where-Object { $_.name -eq $name }
|
||||
if ($asset) {
|
||||
Write-Host "Found matching asset: $($asset.name)" -ForegroundColor Green
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (!$asset) {
|
||||
Write-Host "`nAvailable assets:" -ForegroundColor Yellow
|
||||
$latestRelease.assets | ForEach-Object { Write-Host "- $($_.name)" }
|
||||
throw "Could not find appropriate Windows binary for $arch architecture"
|
||||
}
|
||||
|
||||
$downloadUrl = $asset.browser_download_url
|
||||
}
|
||||
catch {
|
||||
Write-Host "Failed to get latest release: $_" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Download binary
|
||||
Write-Host "`nDownloading latest release..." -ForegroundColor Cyan
|
||||
$binaryPath = Join-Path $TmpDir "cursor-id-modifier.exe"
|
||||
|
||||
if (!(Get-FileWithProgress -Url $downloadUrl -OutputFile $binaryPath)) {
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Install binary
|
||||
Write-Host "Installing..." -ForegroundColor Cyan
|
||||
try {
|
||||
Copy-Item -Path $binaryPath -Destination "$InstallDir\cursor-id-modifier.exe" -Force
|
||||
|
||||
# Add to PATH if not already present
|
||||
$currentPath = [Environment]::GetEnvironmentVariable("Path", "Machine")
|
||||
if ($currentPath -notlike "*$InstallDir*") {
|
||||
[Environment]::SetEnvironmentVariable("Path", "$currentPath;$InstallDir", "Machine")
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-Host "Failed to install: $_" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host "Installation completed successfully!" -ForegroundColor Green
|
||||
Write-Host "Running cursor-id-modifier..." -ForegroundColor Cyan
|
||||
|
||||
# Run the program
|
||||
try {
|
||||
& "$InstallDir\cursor-id-modifier.exe"
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host "Failed to run cursor-id-modifier" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-Host "Failed to run cursor-id-modifier: $_" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
# Run installation
|
||||
try {
|
||||
Install-CursorModifier
|
||||
}
|
||||
catch {
|
||||
Write-Host "Installation failed: $_" -ForegroundColor Red
|
||||
Cleanup
|
||||
Write-Host "Press enter to exit..."
|
||||
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown')
|
||||
exit 1
|
||||
}
|
||||
finally {
|
||||
Cleanup
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host "Press enter to exit..." -ForegroundColor Green
|
||||
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown')
|
||||
}
|
||||
}
|
127
scripts/install.sh
Executable file
@ -0,0 +1,127 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;36m'
|
||||
YELLOW='\033[0;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Temporary directory for downloads
|
||||
TMP_DIR=$(mktemp -d)
|
||||
trap 'rm -rf "$TMP_DIR"' EXIT
|
||||
|
||||
# Check for required commands
|
||||
check_requirements() {
|
||||
if ! command -v curl >/dev/null 2>&1; then
|
||||
echo -e "${RED}Error: curl is required${NC}"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Detect system information
|
||||
detect_system() {
|
||||
local os arch suffix
|
||||
|
||||
case "$(uname -s)" in
|
||||
Linux*) os="linux";;
|
||||
Darwin*) os="darwin";;
|
||||
*) echo -e "${RED}Unsupported OS${NC}"; exit 1;;
|
||||
esac
|
||||
|
||||
case "$(uname -m)" in
|
||||
x86_64)
|
||||
arch="x86_64"
|
||||
;;
|
||||
aarch64|arm64)
|
||||
arch="arm64"
|
||||
;;
|
||||
i386|i686)
|
||||
arch="i386"
|
||||
;;
|
||||
*) echo -e "${RED}Unsupported architecture${NC}"; exit 1;;
|
||||
esac
|
||||
|
||||
echo "$os $arch"
|
||||
}
|
||||
|
||||
# Download with progress
|
||||
download() {
|
||||
local url="$1"
|
||||
local output="$2"
|
||||
curl -#L "$url" -o "$output"
|
||||
}
|
||||
|
||||
# Check and create installation directory
|
||||
setup_install_dir() {
|
||||
local install_dir="$1"
|
||||
|
||||
if [ ! -d "$install_dir" ]; then
|
||||
mkdir -p "$install_dir" || {
|
||||
echo -e "${RED}Failed to create installation directory${NC}"
|
||||
exit 1
|
||||
}
|
||||
fi
|
||||
}
|
||||
|
||||
# Main installation function
|
||||
main() {
|
||||
check_requirements
|
||||
|
||||
echo -e "${BLUE}Starting installation...${NC}"
|
||||
|
||||
# Detect system
|
||||
read -r OS ARCH SUFFIX <<< "$(detect_system)"
|
||||
echo -e "${GREEN}Detected: $OS $ARCH${NC}"
|
||||
|
||||
# Set installation directory
|
||||
INSTALL_DIR="/usr/local/bin"
|
||||
|
||||
# Setup installation directory
|
||||
setup_install_dir "$INSTALL_DIR"
|
||||
|
||||
# Get latest release info
|
||||
echo -e "${BLUE}Fetching latest release information...${NC}"
|
||||
LATEST_URL="https://api.github.com/repos/yuaotian/go-cursor-help/releases/latest"
|
||||
|
||||
# Get latest version and remove 'v' prefix
|
||||
VERSION=$(curl -s "$LATEST_URL" | grep "tag_name" | cut -d'"' -f4 | sed 's/^v//')
|
||||
|
||||
# Construct binary name
|
||||
BINARY_NAME="cursor-id-modifier_${VERSION}_${OS}_${ARCH}"
|
||||
echo -e "${BLUE}Looking for asset: $BINARY_NAME${NC}"
|
||||
|
||||
# Get download URL directly
|
||||
DOWNLOAD_URL=$(curl -s "$LATEST_URL" | grep -o "\"browser_download_url\": \"[^\"]*${BINARY_NAME}[^\"]*\"" | cut -d'"' -f4)
|
||||
|
||||
if [ -z "$DOWNLOAD_URL" ]; then
|
||||
echo -e "${RED}Error: Could not find appropriate binary for $OS $ARCH${NC}"
|
||||
echo -e "${YELLOW}Available assets:${NC}"
|
||||
curl -s "$LATEST_URL" | grep "browser_download_url" | cut -d'"' -f4
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}Found matching asset: $BINARY_NAME${NC}"
|
||||
echo -e "${BLUE}Downloading from: $DOWNLOAD_URL${NC}"
|
||||
|
||||
download "$DOWNLOAD_URL" "$TMP_DIR/cursor-id-modifier"
|
||||
|
||||
# Install binary
|
||||
echo -e "${BLUE}Installing...${NC}"
|
||||
chmod +x "$TMP_DIR/cursor-id-modifier"
|
||||
sudo mv "$TMP_DIR/cursor-id-modifier" "$INSTALL_DIR/"
|
||||
|
||||
echo -e "${GREEN}Installation completed successfully!${NC}"
|
||||
echo -e "${BLUE}Running cursor-id-modifier...${NC}"
|
||||
|
||||
# Run the program with sudo, preserving environment variables
|
||||
export AUTOMATED_MODE=1
|
||||
if ! sudo -E cursor-id-modifier; then
|
||||
echo -e "${RED}Failed to run cursor-id-modifier${NC}"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
main
|
1276
scripts/run/cursor_linux_id_modifier.sh
Executable file
1401
scripts/run/cursor_mac_free_trial_reset.sh
Normal file
2905
scripts/run/cursor_mac_id_modifier.sh
Normal file
688
scripts/run/cursor_mac_id_modifier_new.sh
Normal file
@ -0,0 +1,688 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ========================================
|
||||
# Cursor macOS 机器码修改脚本 (重构精简版)
|
||||
# ========================================
|
||||
#
|
||||
# 🎯 重构目标:
|
||||
# - 简化脚本复杂度,从3158行压缩到约900行
|
||||
# - 自动化权限修复,解决EACCES权限错误
|
||||
# - 减少用户交互步骤,提升执行效率
|
||||
# - 保持所有原有功能完整性
|
||||
#
|
||||
# 🚀 执行流程说明:
|
||||
# 1. 环境检测和权限预修复
|
||||
# 2. 用户选择执行模式(仅修改 vs 完整重置)
|
||||
# 3. 自动执行所有必要步骤
|
||||
# 4. 智能设备识别绕过(MAC地址或JS内核修改)
|
||||
# 5. 自动权限修复和验证
|
||||
#
|
||||
# ========================================
|
||||
|
||||
set -e
|
||||
|
||||
# ==================== 核心配置 ====================
|
||||
LOG_FILE="/tmp/cursor_reset_$(date +%Y%m%d_%H%M%S).log"
|
||||
CURSOR_APP_PATH="/Applications/Cursor.app"
|
||||
STORAGE_FILE="$HOME/Library/Application Support/Cursor/User/globalStorage/storage.json"
|
||||
BACKUP_DIR="$HOME/Library/Application Support/Cursor/User/globalStorage/backups"
|
||||
|
||||
# 颜色定义
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
# ==================== 统一工具函数 ====================
|
||||
|
||||
# 初始化日志
|
||||
init_log() {
|
||||
echo "========== Cursor重构脚本执行日志 $(date) ==========" > "$LOG_FILE"
|
||||
chmod 644 "$LOG_FILE"
|
||||
}
|
||||
|
||||
# 精简日志函数
|
||||
log() {
|
||||
local level="$1"
|
||||
local msg="$2"
|
||||
local color=""
|
||||
|
||||
case "$level" in
|
||||
"INFO") color="$GREEN" ;;
|
||||
"WARN") color="$YELLOW" ;;
|
||||
"ERROR") color="$RED" ;;
|
||||
*) color="$BLUE" ;;
|
||||
esac
|
||||
|
||||
echo -e "${color}[$level]${NC} $msg"
|
||||
echo "[$level] $(date '+%H:%M:%S') $msg" >> "$LOG_FILE"
|
||||
}
|
||||
|
||||
# 统一权限管理器 - 解决所有权限问题
|
||||
fix_permissions() {
|
||||
log "INFO" "🔧 执行统一权限修复..."
|
||||
|
||||
local cursor_support="$HOME/Library/Application Support/Cursor"
|
||||
local cursor_home="$HOME/.cursor"
|
||||
|
||||
# 创建必要目录结构
|
||||
local dirs=(
|
||||
"$cursor_support"
|
||||
"$cursor_support/User"
|
||||
"$cursor_support/User/globalStorage"
|
||||
"$cursor_support/logs"
|
||||
"$cursor_support/CachedData"
|
||||
"$cursor_home"
|
||||
"$cursor_home/extensions"
|
||||
)
|
||||
|
||||
for dir in "${dirs[@]}"; do
|
||||
mkdir -p "$dir" 2>/dev/null || true
|
||||
done
|
||||
|
||||
# 执行核心权限修复命令
|
||||
if sudo chown -R "$(whoami)" "$cursor_support" 2>/dev/null && \
|
||||
sudo chown -R "$(whoami)" "$cursor_home" 2>/dev/null && \
|
||||
chmod -R u+w "$cursor_support" 2>/dev/null && \
|
||||
chmod -R u+w "$cursor_home/extensions" 2>/dev/null; then
|
||||
log "INFO" "✅ 权限修复成功"
|
||||
return 0
|
||||
else
|
||||
log "ERROR" "❌ 权限修复失败"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 环境检测器
|
||||
detect_environment() {
|
||||
log "INFO" "🔍 检测系统环境..."
|
||||
|
||||
# 检测macOS版本和硬件
|
||||
MACOS_VERSION=$(sw_vers -productVersion)
|
||||
HARDWARE_TYPE=$(uname -m)
|
||||
|
||||
if [[ "$HARDWARE_TYPE" == "arm64" ]]; then
|
||||
HARDWARE_TYPE="Apple Silicon"
|
||||
else
|
||||
HARDWARE_TYPE="Intel"
|
||||
fi
|
||||
|
||||
# 检测兼容性
|
||||
local macos_major=$(echo "$MACOS_VERSION" | cut -d. -f1)
|
||||
if [[ $macos_major -ge 14 ]] || [[ "$HARDWARE_TYPE" == "Apple Silicon" ]]; then
|
||||
MAC_COMPATIBLE=false
|
||||
log "WARN" "⚠️ 检测到MAC地址修改受限环境: macOS $MACOS_VERSION ($HARDWARE_TYPE)"
|
||||
else
|
||||
MAC_COMPATIBLE=true
|
||||
log "INFO" "✅ 环境兼容性检查通过"
|
||||
fi
|
||||
|
||||
# 检查Python3
|
||||
if ! command -v python3 >/dev/null 2>&1; then
|
||||
log "ERROR" "❌ 未找到Python3,请安装: brew install python3"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 检查Cursor应用
|
||||
if [ ! -d "$CURSOR_APP_PATH" ]; then
|
||||
log "ERROR" "❌ 未找到Cursor应用: $CURSOR_APP_PATH"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log "INFO" "✅ 环境检测完成: macOS $MACOS_VERSION ($HARDWARE_TYPE)"
|
||||
return 0
|
||||
}
|
||||
|
||||
# 进程管理器
|
||||
manage_cursor_process() {
|
||||
local action="$1" # kill 或 start
|
||||
|
||||
case "$action" in
|
||||
"kill")
|
||||
log "INFO" "🔄 关闭Cursor进程..."
|
||||
pkill -f "Cursor" 2>/dev/null || true
|
||||
sleep 2
|
||||
|
||||
# 验证是否关闭
|
||||
if pgrep -f "Cursor" >/dev/null; then
|
||||
pkill -9 -f "Cursor" 2>/dev/null || true
|
||||
sleep 2
|
||||
fi
|
||||
log "INFO" "✅ Cursor进程已关闭"
|
||||
;;
|
||||
"start")
|
||||
log "INFO" "🚀 启动Cursor..."
|
||||
"$CURSOR_APP_PATH/Contents/MacOS/Cursor" > /dev/null 2>&1 &
|
||||
sleep 15
|
||||
log "INFO" "✅ Cursor已启动"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# 错误处理器
|
||||
handle_error() {
|
||||
local error_msg="$1"
|
||||
local recovery_action="$2"
|
||||
|
||||
log "ERROR" "❌ 错误: $error_msg"
|
||||
|
||||
if [ -n "$recovery_action" ]; then
|
||||
log "INFO" "🔄 尝试恢复: $recovery_action"
|
||||
eval "$recovery_action"
|
||||
fi
|
||||
|
||||
log "INFO" "💡 如需帮助,请查看日志: $LOG_FILE"
|
||||
}
|
||||
|
||||
# ==================== 功能模块 ====================
|
||||
|
||||
# 机器码修改器
|
||||
modify_machine_code() {
|
||||
log "INFO" "🛠️ 开始修改机器码配置..."
|
||||
|
||||
# 检查配置文件
|
||||
if [ ! -f "$STORAGE_FILE" ]; then
|
||||
log "ERROR" "❌ 配置文件不存在,请先启动Cursor生成配置"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 创建备份
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
local backup_file="$BACKUP_DIR/storage.json.backup_$(date +%Y%m%d_%H%M%S)"
|
||||
cp "$STORAGE_FILE" "$backup_file" || {
|
||||
log "ERROR" "❌ 备份失败"
|
||||
return 1
|
||||
}
|
||||
|
||||
# 生成新ID
|
||||
local machine_id="auth0|user_$(openssl rand -hex 16)"
|
||||
local mac_machine_id=$(uuidgen | tr '[:upper:]' '[:lower:]')
|
||||
local device_id=$(uuidgen | tr '[:upper:]' '[:lower:]')
|
||||
local sqm_id="{$(uuidgen | tr '[:lower:]' '[:upper:]')}"
|
||||
|
||||
# 修改配置文件
|
||||
local python_result=$(python3 -c "
|
||||
import json
|
||||
import sys
|
||||
|
||||
try:
|
||||
with open('$STORAGE_FILE', 'r', encoding='utf-8') as f:
|
||||
config = json.load(f)
|
||||
|
||||
config['telemetry.machineId'] = '$machine_id'
|
||||
config['telemetry.macMachineId'] = '$mac_machine_id'
|
||||
config['telemetry.devDeviceId'] = '$device_id'
|
||||
config['telemetry.sqmId'] = '$sqm_id'
|
||||
|
||||
with open('$STORAGE_FILE', 'w', encoding='utf-8') as f:
|
||||
json.dump(config, f, indent=2, ensure_ascii=False)
|
||||
|
||||
print('SUCCESS')
|
||||
except Exception as e:
|
||||
print(f'ERROR: {e}')
|
||||
sys.exit(1)
|
||||
" 2>&1)
|
||||
|
||||
if echo "$python_result" | grep -q "SUCCESS"; then
|
||||
# 设置只读保护
|
||||
chmod 444 "$STORAGE_FILE" 2>/dev/null || true
|
||||
log "INFO" "✅ 机器码修改成功"
|
||||
log "INFO" "💾 备份保存至: $(basename "$backup_file")"
|
||||
return 0
|
||||
else
|
||||
log "ERROR" "❌ 机器码修改失败: $python_result"
|
||||
cp "$backup_file" "$STORAGE_FILE" 2>/dev/null || true
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 智能设备绕过器 - 根据环境自动选择最佳方案
|
||||
bypass_device_detection() {
|
||||
log "INFO" "🔧 开始智能设备识别绕过..."
|
||||
|
||||
# 根据环境兼容性选择方案
|
||||
if [ "$MAC_COMPATIBLE" = false ]; then
|
||||
log "INFO" "💡 检测到MAC地址修改受限,使用JS内核修改方案"
|
||||
return modify_js_kernel
|
||||
else
|
||||
log "INFO" "💡 尝试MAC地址修改方案"
|
||||
if modify_mac_address; then
|
||||
return 0
|
||||
else
|
||||
log "WARN" "⚠️ MAC地址修改失败,切换到JS内核修改"
|
||||
return modify_js_kernel
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# MAC地址修改器(简化版)
|
||||
modify_mac_address() {
|
||||
log "INFO" "🌐 开始MAC地址修改..."
|
||||
|
||||
# 获取活动网络接口
|
||||
local interfaces=()
|
||||
while IFS= read -r line; do
|
||||
if [[ $line == "Hardware Port: Wi-Fi" || $line == "Hardware Port: Ethernet" ]]; then
|
||||
read -r dev_line
|
||||
local device=$(echo "$dev_line" | awk '{print $2}')
|
||||
if [ -n "$device" ] && ifconfig "$device" 2>/dev/null | grep -q "status: active"; then
|
||||
interfaces+=("$device")
|
||||
fi
|
||||
fi
|
||||
done < <(networksetup -listallhardwareports)
|
||||
|
||||
if [ ${#interfaces[@]} -eq 0 ]; then
|
||||
log "WARN" "⚠️ 未找到活动网络接口"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local success_count=0
|
||||
for interface in "${interfaces[@]}"; do
|
||||
log "INFO" "🔧 处理接口: $interface"
|
||||
|
||||
# 生成新MAC地址
|
||||
local new_mac=$(printf '%02x:%02x:%02x:%02x:%02x:%02x' \
|
||||
$(( (RANDOM & 0xFC) | 0x02 )) $((RANDOM%256)) $((RANDOM%256)) \
|
||||
$((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256)))
|
||||
|
||||
# 尝试修改MAC地址
|
||||
if sudo ifconfig "$interface" down 2>/dev/null && \
|
||||
sleep 2 && \
|
||||
sudo ifconfig "$interface" ether "$new_mac" 2>/dev/null && \
|
||||
sudo ifconfig "$interface" up 2>/dev/null; then
|
||||
log "INFO" "✅ 接口 $interface MAC地址修改成功: $new_mac"
|
||||
((success_count++))
|
||||
else
|
||||
log "WARN" "⚠️ 接口 $interface MAC地址修改失败"
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
|
||||
if [ $success_count -gt 0 ]; then
|
||||
log "INFO" "✅ MAC地址修改完成 ($success_count/${#interfaces[@]} 成功)"
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# JS内核修改器(简化版)
|
||||
modify_js_kernel() {
|
||||
log "INFO" "🔧 开始JS内核修改..."
|
||||
|
||||
# 关闭Cursor
|
||||
manage_cursor_process "kill"
|
||||
|
||||
# 目标JS文件
|
||||
local js_files=(
|
||||
"$CURSOR_APP_PATH/Contents/Resources/app/out/vs/workbench/api/node/extensionHostProcess.js"
|
||||
"$CURSOR_APP_PATH/Contents/Resources/app/out/main.js"
|
||||
)
|
||||
|
||||
# 检查是否需要修改
|
||||
local need_modify=false
|
||||
for file in "${js_files[@]}"; do
|
||||
if [ -f "$file" ] && ! grep -q "return crypto.randomUUID()" "$file" 2>/dev/null; then
|
||||
need_modify=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$need_modify" = false ]; then
|
||||
log "INFO" "✅ JS文件已修改,跳过"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# 创建备份
|
||||
local backup_app="/tmp/Cursor.app.backup_$(date +%Y%m%d_%H%M%S)"
|
||||
cp -R "$CURSOR_APP_PATH" "$backup_app" || {
|
||||
log "ERROR" "❌ 创建备份失败"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 生成设备ID
|
||||
local new_uuid=$(uuidgen | tr '[:upper:]' '[:lower:]')
|
||||
local machine_id="auth0|user_$(openssl rand -hex 16)"
|
||||
|
||||
# 修改JS文件
|
||||
local modified_count=0
|
||||
for file in "${js_files[@]}"; do
|
||||
if [ ! -f "$file" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
log "INFO" "📝 处理文件: $(basename "$file")"
|
||||
|
||||
# 创建注入代码
|
||||
local inject_code="
|
||||
// Cursor设备标识符劫持 - $(date +%Y%m%d%H%M%S)
|
||||
import crypto from 'crypto';
|
||||
const originalRandomUUID = crypto.randomUUID;
|
||||
crypto.randomUUID = function() { return '$new_uuid'; };
|
||||
globalThis.getMachineId = function() { return '$machine_id'; };
|
||||
console.log('Cursor设备标识符已劫持');
|
||||
"
|
||||
|
||||
# 注入代码
|
||||
echo "$inject_code" > "${file}.new"
|
||||
cat "$file" >> "${file}.new"
|
||||
mv "${file}.new" "$file"
|
||||
|
||||
((modified_count++))
|
||||
log "INFO" "✅ 文件修改成功: $(basename "$file")"
|
||||
done
|
||||
|
||||
if [ $modified_count -gt 0 ]; then
|
||||
# 重新签名
|
||||
if codesign --sign - --force --deep "$CURSOR_APP_PATH" 2>/dev/null; then
|
||||
log "INFO" "✅ JS内核修改完成 ($modified_count 个文件)"
|
||||
return 0
|
||||
else
|
||||
log "WARN" "⚠️ 签名失败,但修改已完成"
|
||||
return 0
|
||||
fi
|
||||
else
|
||||
log "ERROR" "❌ 未修改任何文件"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 环境重置器
|
||||
reset_environment() {
|
||||
log "INFO" "🗑️ 开始环境重置..."
|
||||
|
||||
# 关闭Cursor
|
||||
manage_cursor_process "kill"
|
||||
|
||||
# 删除目标文件夹
|
||||
local folders=(
|
||||
"$HOME/Library/Application Support/Cursor"
|
||||
"$HOME/.cursor"
|
||||
)
|
||||
|
||||
local deleted_count=0
|
||||
for folder in "${folders[@]}"; do
|
||||
if [ -d "$folder" ]; then
|
||||
if rm -rf "$folder"; then
|
||||
log "INFO" "✅ 已删除: $folder"
|
||||
((deleted_count++))
|
||||
else
|
||||
log "ERROR" "❌ 删除失败: $folder"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# 修复权限
|
||||
fix_permissions
|
||||
|
||||
log "INFO" "✅ 环境重置完成 (删除 $deleted_count 个文件夹)"
|
||||
return 0
|
||||
}
|
||||
|
||||
# 禁用自动更新
|
||||
disable_auto_update() {
|
||||
log "INFO" "🚫 禁用自动更新..."
|
||||
|
||||
local app_update_yml="$CURSOR_APP_PATH/Contents/Resources/app-update.yml"
|
||||
local updater_path="$HOME/Library/Application Support/Caches/cursor-updater"
|
||||
|
||||
# 禁用app-update.yml
|
||||
if [ -f "$app_update_yml" ]; then
|
||||
sudo cp "$app_update_yml" "${app_update_yml}.bak" 2>/dev/null || true
|
||||
sudo bash -c "echo '' > \"$app_update_yml\"" 2>/dev/null || true
|
||||
sudo chmod 444 "$app_update_yml" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# 禁用cursor-updater
|
||||
sudo rm -rf "$updater_path" 2>/dev/null || true
|
||||
sudo touch "$updater_path" 2>/dev/null || true
|
||||
sudo chmod 444 "$updater_path" 2>/dev/null || true
|
||||
|
||||
log "INFO" "✅ 自动更新已禁用"
|
||||
}
|
||||
|
||||
# 修复应用签名问题
|
||||
fix_app_signature() {
|
||||
log "INFO" "🔧 修复应用签名..."
|
||||
|
||||
# 移除隔离属性
|
||||
sudo find "$CURSOR_APP_PATH" -print0 2>/dev/null | \
|
||||
xargs -0 sudo xattr -d com.apple.quarantine 2>/dev/null || true
|
||||
|
||||
# 重新签名
|
||||
sudo codesign --force --deep --sign - "$CURSOR_APP_PATH" 2>/dev/null || true
|
||||
|
||||
log "INFO" "✅ 应用签名修复完成"
|
||||
}
|
||||
|
||||
# ==================== 主执行流程 ====================
|
||||
|
||||
# 快速模式 - 仅修改机器码
|
||||
quick_mode() {
|
||||
log "INFO" "🚀 执行快速模式(仅修改机器码)..."
|
||||
|
||||
# 检查环境
|
||||
if ! detect_environment; then
|
||||
handle_error "环境检测失败" "exit 1"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 预修复权限
|
||||
fix_permissions
|
||||
|
||||
# 修改机器码
|
||||
if ! modify_machine_code; then
|
||||
handle_error "机器码修改失败" "exit 1"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 设备绕过
|
||||
bypass_device_detection || log "WARN" "⚠️ 设备绕过失败,但机器码修改已完成"
|
||||
|
||||
# 禁用更新
|
||||
disable_auto_update
|
||||
|
||||
# 修复签名
|
||||
fix_app_signature
|
||||
|
||||
# 最终权限修复
|
||||
fix_permissions
|
||||
|
||||
log "INFO" "🎉 快速模式执行完成!"
|
||||
return 0
|
||||
}
|
||||
|
||||
# 完整模式 - 重置环境+修改机器码
|
||||
full_mode() {
|
||||
log "INFO" "🚀 执行完整模式(重置环境+修改机器码)..."
|
||||
|
||||
# 检查环境
|
||||
if ! detect_environment; then
|
||||
handle_error "环境检测失败" "exit 1"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 环境重置
|
||||
if ! reset_environment; then
|
||||
handle_error "环境重置失败" "exit 1"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 启动Cursor生成配置
|
||||
manage_cursor_process "start"
|
||||
|
||||
# 等待配置文件生成
|
||||
local config_wait=0
|
||||
while [ ! -f "$STORAGE_FILE" ] && [ $config_wait -lt 30 ]; do
|
||||
sleep 2
|
||||
((config_wait += 2))
|
||||
log "INFO" "⏳ 等待配置文件生成... ($config_wait/30秒)"
|
||||
done
|
||||
|
||||
# 关闭Cursor
|
||||
manage_cursor_process "kill"
|
||||
|
||||
# 修改机器码
|
||||
if ! modify_machine_code; then
|
||||
handle_error "机器码修改失败" "exit 1"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 设备绕过
|
||||
bypass_device_detection || log "WARN" "⚠️ 设备绕过失败,但机器码修改已完成"
|
||||
|
||||
# 禁用更新
|
||||
disable_auto_update
|
||||
|
||||
# 修复签名
|
||||
fix_app_signature
|
||||
|
||||
# 最终权限修复
|
||||
fix_permissions
|
||||
|
||||
log "INFO" "🎉 完整模式执行完成!"
|
||||
return 0
|
||||
}
|
||||
|
||||
# ==================== 用户界面 ====================
|
||||
|
||||
# 显示Logo和信息
|
||||
show_header() {
|
||||
clear
|
||||
echo -e "
|
||||
██████╗██╗ ██╗██████╗ ███████╗ ██████╗ ██████╗
|
||||
██╔════╝██║ ██║██╔══██╗██╔════╝██╔═══██╗██╔══██╗
|
||||
██║ ██║ ██║██████╔╝███████╗██║ ██║██████╔╝
|
||||
██║ ██║ ██║██╔══██╗╚════██║██║ ██║██╔══██╗
|
||||
╚██████╗╚██████╔╝██║ ██║███████║╚██████╔╝██║ ██║
|
||||
╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝
|
||||
"
|
||||
echo -e "${BLUE}================================${NC}"
|
||||
echo -e "${GREEN}🚀 Cursor 机器码修改工具 (重构版) ${NC}"
|
||||
echo -e "${YELLOW}📱 关注公众号【煎饼果子卷AI】 ${NC}"
|
||||
echo -e "${BLUE}================================${NC}"
|
||||
echo
|
||||
echo -e "${YELLOW}💡 [免费工具]${NC} 如果对您有帮助,请关注公众号支持开发者"
|
||||
echo
|
||||
}
|
||||
|
||||
# 用户选择菜单
|
||||
user_menu() {
|
||||
echo -e "${GREEN}🎯 [选择模式]${NC} 请选择执行模式:"
|
||||
echo
|
||||
echo -e "${BLUE} 1️⃣ 快速模式 - 仅修改机器码${NC}"
|
||||
echo -e "${YELLOW} • 保留现有配置和数据${NC}"
|
||||
echo -e "${YELLOW} • 执行时间约30秒${NC}"
|
||||
echo -e "${YELLOW} • 自动权限修复${NC}"
|
||||
echo
|
||||
echo -e "${BLUE} 2️⃣ 完整模式 - 重置环境+修改机器码${NC}"
|
||||
echo -e "${RED} • 删除所有Cursor配置(请备份)${NC}"
|
||||
echo -e "${YELLOW} • 执行时间约90秒${NC}"
|
||||
echo -e "${YELLOW} • 彻底重置试用状态${NC}"
|
||||
echo
|
||||
|
||||
while true; do
|
||||
read -p "请输入选择 (1 或 2): " choice
|
||||
case "$choice" in
|
||||
1)
|
||||
log "INFO" "✅ 用户选择:快速模式"
|
||||
return 1
|
||||
;;
|
||||
2)
|
||||
echo -e "${RED}⚠️ [警告]${NC} 完整模式将删除所有Cursor配置!"
|
||||
read -p "确认执行?(输入 yes 确认): " confirm
|
||||
if [ "$confirm" = "yes" ]; then
|
||||
log "INFO" "✅ 用户选择:完整模式"
|
||||
return 2
|
||||
else
|
||||
echo -e "${YELLOW}👋 [取消]${NC} 请重新选择"
|
||||
continue
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo -e "${RED}❌ [错误]${NC} 无效选择,请输入 1 或 2"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
# 显示完成信息
|
||||
show_completion() {
|
||||
echo
|
||||
echo -e "${GREEN}================================${NC}"
|
||||
echo -e "${BLUE} 🎯 执行完成总结 ${NC}"
|
||||
echo -e "${GREEN}================================${NC}"
|
||||
echo -e "${GREEN}✅ 机器码配置: 已修改${NC}"
|
||||
echo -e "${GREEN}✅ 设备识别绕过: 已完成${NC}"
|
||||
echo -e "${GREEN}✅ 自动更新: 已禁用${NC}"
|
||||
echo -e "${GREEN}✅ 权限修复: 已完成${NC}"
|
||||
echo -e "${GREEN}✅ 应用签名: 已修复${NC}"
|
||||
echo -e "${GREEN}================================${NC}"
|
||||
echo
|
||||
echo -e "${YELLOW}📱 关注公众号【煎饼果子卷AI】获取更多Cursor技巧${NC}"
|
||||
echo
|
||||
echo -e "${BLUE}🚀 [下一步]${NC} 现在可以启动Cursor使用了!"
|
||||
echo -e "${BLUE}📄 [日志]${NC} 详细日志保存在: $LOG_FILE"
|
||||
echo
|
||||
}
|
||||
|
||||
# ==================== 主函数 ====================
|
||||
|
||||
main() {
|
||||
# 检查权限
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo -e "${RED}❌ [错误]${NC} 请使用 sudo 运行此脚本"
|
||||
echo "示例: sudo $0"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 检查macOS
|
||||
if [[ $(uname) != "Darwin" ]]; then
|
||||
echo -e "${RED}❌ [错误]${NC} 本脚本仅支持 macOS 系统"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 初始化
|
||||
init_log
|
||||
log "INFO" "🚀 Cursor重构脚本启动..."
|
||||
|
||||
# 预修复权限
|
||||
fix_permissions
|
||||
|
||||
# 显示界面
|
||||
show_header
|
||||
|
||||
# 用户选择
|
||||
user_menu
|
||||
local mode=$?
|
||||
|
||||
echo
|
||||
log "INFO" "🚀 开始执行,请稍候..."
|
||||
echo
|
||||
|
||||
# 执行对应模式
|
||||
case $mode in
|
||||
1)
|
||||
if quick_mode; then
|
||||
show_completion
|
||||
exit 0
|
||||
else
|
||||
log "ERROR" "❌ 快速模式执行失败"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
2)
|
||||
if full_mode; then
|
||||
show_completion
|
||||
exit 0
|
||||
else
|
||||
log "ERROR" "❌ 完整模式执行失败"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# 执行主函数
|
||||
main "$@"
|
1679
scripts/run/cursor_win_id_modifier.ps1
Normal file
607
scripts/run/cursor_win_id_modifier_old.ps1
Normal file
@ -0,0 +1,607 @@
|
||||
# 设置输出编码为 UTF-8
|
||||
$OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
|
||||
# 颜色定义
|
||||
$RED = "`e[31m"
|
||||
$GREEN = "`e[32m"
|
||||
$YELLOW = "`e[33m"
|
||||
$BLUE = "`e[34m"
|
||||
$NC = "`e[0m"
|
||||
|
||||
# 配置文件路径
|
||||
$STORAGE_FILE = "$env:APPDATA\Cursor\User\globalStorage\storage.json"
|
||||
$BACKUP_DIR = "$env:APPDATA\Cursor\User\globalStorage\backups"
|
||||
|
||||
# 新增 Cursor 初始化函数
|
||||
function Cursor-初始化 {
|
||||
Write-Host "$GREEN[信息]$NC 正在执行 Cursor 初始化清理..."
|
||||
$BASE_PATH = "$env:APPDATA\Cursor\User"
|
||||
|
||||
$filesToDelete = @(
|
||||
(Join-Path -Path $BASE_PATH -ChildPath "globalStorage\\state.vscdb"),
|
||||
(Join-Path -Path $BASE_PATH -ChildPath "globalStorage\\state.vscdb.backup")
|
||||
)
|
||||
|
||||
$folderToCleanContents = Join-Path -Path $BASE_PATH -ChildPath "History"
|
||||
$folderToDeleteCompletely = Join-Path -Path $BASE_PATH -ChildPath "workspaceStorage"
|
||||
|
||||
Write-Host "$BLUE[调试]$NC 基础路径: $BASE_PATH"
|
||||
|
||||
# 删除指定文件
|
||||
foreach ($file in $filesToDelete) {
|
||||
Write-Host "$BLUE[调试]$NC 检查文件: $file"
|
||||
if (Test-Path $file) {
|
||||
try {
|
||||
Remove-Item -Path $file -Force -ErrorAction Stop
|
||||
Write-Host "$GREEN[成功]$NC 已删除文件: $file"
|
||||
}
|
||||
catch {
|
||||
Write-Host "$RED[错误]$NC 删除文件 $file 失败: $($_.Exception.Message)"
|
||||
}
|
||||
} else {
|
||||
Write-Host "$YELLOW[警告]$NC 文件不存在,跳过删除: $file"
|
||||
}
|
||||
}
|
||||
|
||||
# 清空指定文件夹内容
|
||||
Write-Host "$BLUE[调试]$NC 检查待清空文件夹: $folderToCleanContents"
|
||||
if (Test-Path $folderToCleanContents) {
|
||||
try {
|
||||
# 获取子项进行删除,以避免删除 History 文件夹本身
|
||||
Get-ChildItem -Path $folderToCleanContents -Recurse | Remove-Item -Recurse -Force -ErrorAction Stop
|
||||
Write-Host "$GREEN[成功]$NC 已清空文件夹内容: $folderToCleanContents"
|
||||
}
|
||||
catch {
|
||||
Write-Host "$RED[错误]$NC 清空文件夹 $folderToCleanContents 内容失败: $($_.Exception.Message)"
|
||||
}
|
||||
} else {
|
||||
Write-Host "$YELLOW[警告]$NC 文件夹不存在,跳过清空: $folderToCleanContents"
|
||||
}
|
||||
|
||||
# 删除指定文件夹及其内容
|
||||
Write-Host "$BLUE[调试]$NC 检查待删除文件夹: $folderToDeleteCompletely"
|
||||
if (Test-Path $folderToDeleteCompletely) {
|
||||
try {
|
||||
Remove-Item -Path $folderToDeleteCompletely -Recurse -Force -ErrorAction Stop
|
||||
Write-Host "$GREEN[成功]$NC 已删除文件夹: $folderToDeleteCompletely"
|
||||
}
|
||||
catch {
|
||||
Write-Host "$RED[错误]$NC 删除文件夹 $folderToDeleteCompletely 失败: $($_.Exception.Message)"
|
||||
}
|
||||
} else {
|
||||
Write-Host "$YELLOW[警告]$NC 文件夹不存在,跳过删除: $folderToDeleteCompletely"
|
||||
}
|
||||
|
||||
Write-Host "$GREEN[信息]$NC Cursor 初始化清理完成。"
|
||||
Write-Host "" # 添加空行以改善输出格式
|
||||
}
|
||||
|
||||
# 检查管理员权限
|
||||
function Test-Administrator {
|
||||
$user = [Security.Principal.WindowsIdentity]::GetCurrent()
|
||||
$principal = New-Object Security.Principal.WindowsPrincipal($user)
|
||||
return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
|
||||
}
|
||||
|
||||
if (-not (Test-Administrator)) {
|
||||
Write-Host "$RED[错误]$NC 请以管理员身份运行此脚本"
|
||||
Write-Host "请右键点击脚本,选择'以管理员身份运行'"
|
||||
Read-Host "按回车键退出"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 显示 Logo
|
||||
Clear-Host
|
||||
Write-Host @"
|
||||
|
||||
██████╗██╗ ██╗██████╗ ███████╗ ██████╗ ██████╗
|
||||
██╔════╝██║ ██║██╔══██╗██╔════╝██╔═══██╗██╔══██╗
|
||||
██║ ██║ ██║██████╔╝███████╗██║ ██║██████╔╝
|
||||
██║ ██║ ██║██╔══██╗╚════██║██║ ██║██╔══██╗
|
||||
╚██████╗╚██████╔╝██║ ██║███████║╚██████╔╝██║ ██║
|
||||
╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝
|
||||
|
||||
"@
|
||||
Write-Host "$BLUE================================$NC"
|
||||
Write-Host "$GREEN Cursor 设备ID 修改工具 $NC"
|
||||
Write-Host "$YELLOW 关注公众号【煎饼果子卷AI】 $NC"
|
||||
Write-Host "$YELLOW 一起交流更多Cursor技巧和AI知识(脚本免费、关注公众号加群有更多技巧和大佬) $NC"
|
||||
Write-Host "$YELLOW [重要提示] 本工具免费,如果对您有帮助,请关注公众号【煎饼果子卷AI】 $NC"
|
||||
Write-Host ""
|
||||
Write-Host "$YELLOW [小小广告] 出售CursorPro教育号一年质保三个月,有需要找我(86),WeChat:JavaRookie666 $NC"
|
||||
Write-Host "$BLUE================================$NC"
|
||||
|
||||
# 获取并显示 Cursor 版本
|
||||
function Get-CursorVersion {
|
||||
try {
|
||||
# 主要检测路径
|
||||
$packagePath = "$env:LOCALAPPDATA\\Programs\\cursor\\resources\\app\\package.json"
|
||||
|
||||
if (Test-Path $packagePath) {
|
||||
$packageJson = Get-Content $packagePath -Raw | ConvertFrom-Json
|
||||
if ($packageJson.version) {
|
||||
Write-Host "$GREEN[信息]$NC 当前安装的 Cursor 版本: v$($packageJson.version)"
|
||||
return $packageJson.version
|
||||
}
|
||||
}
|
||||
|
||||
# 备用路径检测
|
||||
$altPath = "$env:LOCALAPPDATA\\cursor\\resources\\app\\package.json"
|
||||
if (Test-Path $altPath) {
|
||||
$packageJson = Get-Content $altPath -Raw | ConvertFrom-Json
|
||||
if ($packageJson.version) {
|
||||
Write-Host "$GREEN[信息]$NC 当前安装的 Cursor 版本: v$($packageJson.version)"
|
||||
return $packageJson.version
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "$YELLOW[警告]$NC 无法检测到 Cursor 版本"
|
||||
Write-Host "$YELLOW[提示]$NC 请确保 Cursor 已正确安装"
|
||||
return $null
|
||||
}
|
||||
catch {
|
||||
Write-Host "$RED[错误]$NC 获取 Cursor 版本失败: $_"
|
||||
return $null
|
||||
}
|
||||
}
|
||||
|
||||
# 获取并显示版本信息
|
||||
$cursorVersion = Get-CursorVersion
|
||||
Write-Host ""
|
||||
|
||||
Write-Host "$YELLOW[重要提示]$NC 最新的 1.0.x (以支持)"
|
||||
Write-Host ""
|
||||
|
||||
# 检查并关闭 Cursor 进程
|
||||
Write-Host "$GREEN[信息]$NC 检查 Cursor 进程..."
|
||||
|
||||
function Get-ProcessDetails {
|
||||
param($processName)
|
||||
Write-Host "$BLUE[调试]$NC 正在获取 $processName 进程详细信息:"
|
||||
Get-WmiObject Win32_Process -Filter "name='$processName'" |
|
||||
Select-Object ProcessId, ExecutablePath, CommandLine |
|
||||
Format-List
|
||||
}
|
||||
|
||||
# 定义最大重试次数和等待时间
|
||||
$MAX_RETRIES = 5
|
||||
$WAIT_TIME = 1
|
||||
|
||||
# 处理进程关闭
|
||||
function Close-CursorProcess {
|
||||
param($processName)
|
||||
|
||||
$process = Get-Process -Name $processName -ErrorAction SilentlyContinue
|
||||
if ($process) {
|
||||
Write-Host "$YELLOW[警告]$NC 发现 $processName 正在运行"
|
||||
Get-ProcessDetails $processName
|
||||
|
||||
Write-Host "$YELLOW[警告]$NC 尝试关闭 $processName..."
|
||||
Stop-Process -Name $processName -Force
|
||||
|
||||
$retryCount = 0
|
||||
while ($retryCount -lt $MAX_RETRIES) {
|
||||
$process = Get-Process -Name $processName -ErrorAction SilentlyContinue
|
||||
if (-not $process) { break }
|
||||
|
||||
$retryCount++
|
||||
if ($retryCount -ge $MAX_RETRIES) {
|
||||
Write-Host "$RED[错误]$NC 在 $MAX_RETRIES 次尝试后仍无法关闭 $processName"
|
||||
Get-ProcessDetails $processName
|
||||
Write-Host "$RED[错误]$NC 请手动关闭进程后重试"
|
||||
Read-Host "按回车键退出"
|
||||
exit 1
|
||||
}
|
||||
Write-Host "$YELLOW[警告]$NC 等待进程关闭,尝试 $retryCount/$MAX_RETRIES..."
|
||||
Start-Sleep -Seconds $WAIT_TIME
|
||||
}
|
||||
Write-Host "$GREEN[信息]$NC $processName 已成功关闭"
|
||||
}
|
||||
}
|
||||
|
||||
# 关闭所有 Cursor 进程
|
||||
Close-CursorProcess "Cursor"
|
||||
Close-CursorProcess "cursor"
|
||||
|
||||
# 执行 Cursor 初始化清理
|
||||
# Cursor-初始化
|
||||
|
||||
# 创建备份目录
|
||||
if (-not (Test-Path $BACKUP_DIR)) {
|
||||
New-Item -ItemType Directory -Path $BACKUP_DIR | Out-Null
|
||||
}
|
||||
|
||||
# 备份现有配置
|
||||
if (Test-Path $STORAGE_FILE) {
|
||||
Write-Host "$GREEN[信息]$NC 正在备份配置文件..."
|
||||
$backupName = "storage.json.backup_$(Get-Date -Format 'yyyyMMdd_HHmmss')"
|
||||
Copy-Item $STORAGE_FILE "$BACKUP_DIR\$backupName"
|
||||
}
|
||||
|
||||
# 生成新的 ID
|
||||
Write-Host "$GREEN[信息]$NC 正在生成新的 ID..."
|
||||
|
||||
# 在颜色定义后添加此函数
|
||||
function Get-RandomHex {
|
||||
param (
|
||||
[int]$length
|
||||
)
|
||||
|
||||
$bytes = New-Object byte[] ($length)
|
||||
$rng = [System.Security.Cryptography.RNGCryptoServiceProvider]::new()
|
||||
$rng.GetBytes($bytes)
|
||||
$hexString = [System.BitConverter]::ToString($bytes) -replace '-',''
|
||||
$rng.Dispose()
|
||||
return $hexString
|
||||
}
|
||||
|
||||
# 改进 ID 生成函数
|
||||
function New-StandardMachineId {
|
||||
$template = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"
|
||||
$result = $template -replace '[xy]', {
|
||||
param($match)
|
||||
$r = [Random]::new().Next(16)
|
||||
$v = if ($match.Value -eq "x") { $r } else { ($r -band 0x3) -bor 0x8 }
|
||||
return $v.ToString("x")
|
||||
}
|
||||
return $result
|
||||
}
|
||||
|
||||
# 在生成 ID 时使用新函数
|
||||
$MAC_MACHINE_ID = New-StandardMachineId
|
||||
$UUID = [System.Guid]::NewGuid().ToString()
|
||||
# 将 auth0|user_ 转换为字节数组的十六进制
|
||||
$prefixBytes = [System.Text.Encoding]::UTF8.GetBytes("auth0|user_")
|
||||
$prefixHex = -join ($prefixBytes | ForEach-Object { '{0:x2}' -f $_ })
|
||||
# 生成32字节(64个十六进制字符)的随机数作为 machineId 的随机部分
|
||||
$randomPart = Get-RandomHex -length 32
|
||||
$MACHINE_ID = "$prefixHex$randomPart"
|
||||
$SQM_ID = "{$([System.Guid]::NewGuid().ToString().ToUpper())}"
|
||||
|
||||
# 在Update-MachineGuid函数前添加权限检查
|
||||
if (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
|
||||
Write-Host "$RED[错误]$NC 请使用管理员权限运行此脚本"
|
||||
Start-Process powershell "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" -Verb RunAs
|
||||
exit
|
||||
}
|
||||
|
||||
function Update-MachineGuid {
|
||||
try {
|
||||
# 检查注册表路径是否存在,不存在则创建
|
||||
$registryPath = "HKLM:\SOFTWARE\Microsoft\Cryptography"
|
||||
if (-not (Test-Path $registryPath)) {
|
||||
Write-Host "$YELLOW[警告]$NC 注册表路径不存在: $registryPath,正在创建..."
|
||||
New-Item -Path $registryPath -Force | Out-Null
|
||||
Write-Host "$GREEN[信息]$NC 注册表路径创建成功"
|
||||
}
|
||||
|
||||
# 获取当前的 MachineGuid,如果不存在则使用空字符串作为默认值
|
||||
$originalGuid = ""
|
||||
try {
|
||||
$currentGuid = Get-ItemProperty -Path $registryPath -Name MachineGuid -ErrorAction SilentlyContinue
|
||||
if ($currentGuid) {
|
||||
$originalGuid = $currentGuid.MachineGuid
|
||||
Write-Host "$GREEN[信息]$NC 当前注册表值:"
|
||||
Write-Host "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography"
|
||||
Write-Host " MachineGuid REG_SZ $originalGuid"
|
||||
} else {
|
||||
Write-Host "$YELLOW[警告]$NC MachineGuid 值不存在,将创建新值"
|
||||
}
|
||||
} catch {
|
||||
Write-Host "$YELLOW[警告]$NC 获取 MachineGuid 失败: $($_.Exception.Message)"
|
||||
}
|
||||
|
||||
# 创建备份目录(如果不存在)
|
||||
if (-not (Test-Path $BACKUP_DIR)) {
|
||||
New-Item -ItemType Directory -Path $BACKUP_DIR -Force | Out-Null
|
||||
}
|
||||
|
||||
# 创建备份文件(仅当原始值存在时)
|
||||
if ($originalGuid) {
|
||||
$backupFile = "$BACKUP_DIR\MachineGuid_$(Get-Date -Format 'yyyyMMdd_HHmmss').reg"
|
||||
$backupResult = Start-Process "reg.exe" -ArgumentList "export", "`"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography`"", "`"$backupFile`"" -NoNewWindow -Wait -PassThru
|
||||
|
||||
if ($backupResult.ExitCode -eq 0) {
|
||||
Write-Host "$GREEN[信息]$NC 注册表项已备份到:$backupFile"
|
||||
} else {
|
||||
Write-Host "$YELLOW[警告]$NC 备份创建失败,继续执行..."
|
||||
}
|
||||
}
|
||||
|
||||
# 生成新GUID
|
||||
$newGuid = [System.Guid]::NewGuid().ToString()
|
||||
|
||||
# 更新或创建注册表值
|
||||
Set-ItemProperty -Path $registryPath -Name MachineGuid -Value $newGuid -Force -ErrorAction Stop
|
||||
|
||||
# 验证更新
|
||||
$verifyGuid = (Get-ItemProperty -Path $registryPath -Name MachineGuid -ErrorAction Stop).MachineGuid
|
||||
if ($verifyGuid -ne $newGuid) {
|
||||
throw "注册表验证失败:更新后的值 ($verifyGuid) 与预期值 ($newGuid) 不匹配"
|
||||
}
|
||||
|
||||
Write-Host "$GREEN[信息]$NC 注册表更新成功:"
|
||||
Write-Host "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography"
|
||||
Write-Host " MachineGuid REG_SZ $newGuid"
|
||||
return $true
|
||||
}
|
||||
catch {
|
||||
Write-Host "$RED[错误]$NC 注册表操作失败:$($_.Exception.Message)"
|
||||
|
||||
# 尝试恢复备份(如果存在)
|
||||
if (($backupFile -ne $null) -and (Test-Path $backupFile)) {
|
||||
Write-Host "$YELLOW[恢复]$NC 正在从备份恢复..."
|
||||
$restoreResult = Start-Process "reg.exe" -ArgumentList "import", "`"$backupFile`"" -NoNewWindow -Wait -PassThru
|
||||
|
||||
if ($restoreResult.ExitCode -eq 0) {
|
||||
Write-Host "$GREEN[恢复成功]$NC 已还原原始注册表值"
|
||||
} else {
|
||||
Write-Host "$RED[错误]$NC 恢复失败,请手动导入备份文件:$backupFile"
|
||||
}
|
||||
} else {
|
||||
Write-Host "$YELLOW[警告]$NC 未找到备份文件或备份创建失败,无法自动恢复"
|
||||
}
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
# 创建或更新配置文件
|
||||
Write-Host "$GREEN[信息]$NC 正在更新配置..."
|
||||
|
||||
try {
|
||||
# 检查配置文件是否存在
|
||||
if (-not (Test-Path $STORAGE_FILE)) {
|
||||
Write-Host "$RED[错误]$NC 未找到配置文件: $STORAGE_FILE"
|
||||
Write-Host "$YELLOW[提示]$NC 请先安装并运行一次 Cursor 后再使用此脚本"
|
||||
Read-Host "按回车键退出"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 读取现有配置文件
|
||||
try {
|
||||
$originalContent = Get-Content $STORAGE_FILE -Raw -Encoding UTF8
|
||||
|
||||
# 将 JSON 字符串转换为 PowerShell 对象
|
||||
$config = $originalContent | ConvertFrom-Json
|
||||
|
||||
# 备份当前值
|
||||
$oldValues = @{
|
||||
'machineId' = $config.'telemetry.machineId'
|
||||
'macMachineId' = $config.'telemetry.macMachineId'
|
||||
'devDeviceId' = $config.'telemetry.devDeviceId'
|
||||
'sqmId' = $config.'telemetry.sqmId'
|
||||
}
|
||||
|
||||
# 更新特定的值
|
||||
$config.'telemetry.machineId' = $MACHINE_ID
|
||||
$config.'telemetry.macMachineId' = $MAC_MACHINE_ID
|
||||
$config.'telemetry.devDeviceId' = $UUID
|
||||
$config.'telemetry.sqmId' = $SQM_ID
|
||||
|
||||
# 将更新后的对象转换回 JSON 并保存
|
||||
$updatedJson = $config | ConvertTo-Json -Depth 10
|
||||
[System.IO.File]::WriteAllText(
|
||||
[System.IO.Path]::GetFullPath($STORAGE_FILE),
|
||||
$updatedJson,
|
||||
[System.Text.Encoding]::UTF8
|
||||
)
|
||||
Write-Host "$GREEN[信息]$NC 成功更新配置文件"
|
||||
} catch {
|
||||
# 如果出错,尝试恢复原始内容
|
||||
if ($originalContent) {
|
||||
[System.IO.File]::WriteAllText(
|
||||
[System.IO.Path]::GetFullPath($STORAGE_FILE),
|
||||
$originalContent,
|
||||
[System.Text.Encoding]::UTF8
|
||||
)
|
||||
}
|
||||
throw "处理 JSON 失败: $_"
|
||||
}
|
||||
# 直接执行更新 MachineGuid,不再询问
|
||||
Update-MachineGuid
|
||||
# 显示结果
|
||||
Write-Host ""
|
||||
Write-Host "$GREEN[信息]$NC 已更新配置:"
|
||||
Write-Host "$BLUE[调试]$NC machineId: $MACHINE_ID"
|
||||
Write-Host "$BLUE[调试]$NC macMachineId: $MAC_MACHINE_ID"
|
||||
Write-Host "$BLUE[调试]$NC devDeviceId: $UUID"
|
||||
Write-Host "$BLUE[调试]$NC sqmId: $SQM_ID"
|
||||
|
||||
# 显示文件树结构
|
||||
Write-Host ""
|
||||
Write-Host "$GREEN[信息]$NC 文件结构:"
|
||||
Write-Host "$BLUE$env:APPDATA\Cursor\User$NC"
|
||||
Write-Host "├── globalStorage"
|
||||
Write-Host "│ ├── storage.json (已修改)"
|
||||
Write-Host "│ └── backups"
|
||||
|
||||
# 列出备份文件
|
||||
$backupFiles = Get-ChildItem "$BACKUP_DIR\*" -ErrorAction SilentlyContinue
|
||||
if ($backupFiles) {
|
||||
foreach ($file in $backupFiles) {
|
||||
Write-Host "│ └── $($file.Name)"
|
||||
}
|
||||
} else {
|
||||
Write-Host "│ └── (空)"
|
||||
}
|
||||
|
||||
# 显示公众号信息
|
||||
Write-Host ""
|
||||
Write-Host "$GREEN================================$NC"
|
||||
Write-Host "$YELLOW 关注公众号【煎饼果子卷AI】一起交流更多Cursor技巧和AI知识(脚本免费、关注公众号加群有更多技巧和大佬) $NC"
|
||||
Write-Host "$GREEN================================$NC"
|
||||
Write-Host ""
|
||||
Write-Host "$GREEN[信息]$NC 请重启 Cursor 以应用新的配置"
|
||||
Write-Host ""
|
||||
|
||||
# 询问是否要禁用自动更新
|
||||
Write-Host ""
|
||||
Write-Host "$YELLOW[询问]$NC 是否要禁用 Cursor 自动更新功能?"
|
||||
Write-Host "0) 否 - 保持默认设置 (按回车键)"
|
||||
Write-Host "1) 是 - 禁用自动更新"
|
||||
$choice = Read-Host "请输入选项 (0)"
|
||||
|
||||
if ($choice -eq "1") {
|
||||
Write-Host ""
|
||||
Write-Host "$GREEN[信息]$NC 正在处理自动更新..."
|
||||
$updaterPath = "$env:LOCALAPPDATA\cursor-updater"
|
||||
|
||||
# 定义手动设置教程
|
||||
function Show-ManualGuide {
|
||||
Write-Host ""
|
||||
Write-Host "$YELLOW[警告]$NC 自动设置失败,请尝试手动操作:"
|
||||
Write-Host "$YELLOW手动禁用更新步骤:$NC"
|
||||
Write-Host "1. 以管理员身份打开 PowerShell"
|
||||
Write-Host "2. 复制粘贴以下命令:"
|
||||
Write-Host "$BLUE命令1 - 删除现有目录(如果存在):$NC"
|
||||
Write-Host "Remove-Item -Path `"$updaterPath`" -Force -Recurse -ErrorAction SilentlyContinue"
|
||||
Write-Host ""
|
||||
Write-Host "$BLUE命令2 - 创建阻止文件:$NC"
|
||||
Write-Host "New-Item -Path `"$updaterPath`" -ItemType File -Force | Out-Null"
|
||||
Write-Host ""
|
||||
Write-Host "$BLUE命令3 - 设置只读属性:$NC"
|
||||
Write-Host "Set-ItemProperty -Path `"$updaterPath`" -Name IsReadOnly -Value `$true"
|
||||
Write-Host ""
|
||||
Write-Host "$BLUE命令4 - 设置权限(可选):$NC"
|
||||
Write-Host "icacls `"$updaterPath`" /inheritance:r /grant:r `"`$($env:USERNAME):(R)`""
|
||||
Write-Host ""
|
||||
Write-Host "$YELLOW验证方法:$NC"
|
||||
Write-Host "1. 运行命令:Get-ItemProperty `"$updaterPath`""
|
||||
Write-Host "2. 确认 IsReadOnly 属性为 True"
|
||||
Write-Host "3. 运行命令:icacls `"$updaterPath`""
|
||||
Write-Host "4. 确认只有读取权限"
|
||||
Write-Host ""
|
||||
Write-Host "$YELLOW[提示]$NC 完成后请重启 Cursor"
|
||||
}
|
||||
|
||||
try {
|
||||
# 检查cursor-updater是否存在
|
||||
if (Test-Path $updaterPath) {
|
||||
# 如果是文件,说明已经创建了阻止更新
|
||||
if ((Get-Item $updaterPath) -is [System.IO.FileInfo]) {
|
||||
Write-Host "$GREEN[信息]$NC 已创建阻止更新文件,无需再次阻止"
|
||||
return
|
||||
}
|
||||
# 如果是目录,尝试删除
|
||||
else {
|
||||
try {
|
||||
Remove-Item -Path $updaterPath -Force -Recurse -ErrorAction Stop
|
||||
Write-Host "$GREEN[信息]$NC 成功删除 cursor-updater 目录"
|
||||
}
|
||||
catch {
|
||||
Write-Host "$RED[错误]$NC 删除 cursor-updater 目录失败"
|
||||
Show-ManualGuide
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# 创建阻止文件
|
||||
try {
|
||||
New-Item -Path $updaterPath -ItemType File -Force -ErrorAction Stop | Out-Null
|
||||
Write-Host "$GREEN[信息]$NC 成功创建阻止文件"
|
||||
}
|
||||
catch {
|
||||
Write-Host "$RED[错误]$NC 创建阻止文件失败"
|
||||
Show-ManualGuide
|
||||
return
|
||||
}
|
||||
|
||||
# 设置文件权限
|
||||
try {
|
||||
# 设置只读属性
|
||||
Set-ItemProperty -Path $updaterPath -Name IsReadOnly -Value $true -ErrorAction Stop
|
||||
|
||||
# 使用 icacls 设置权限
|
||||
$result = Start-Process "icacls.exe" -ArgumentList "`"$updaterPath`" /inheritance:r /grant:r `"$($env:USERNAME):(R)`"" -Wait -NoNewWindow -PassThru
|
||||
if ($result.ExitCode -ne 0) {
|
||||
throw "icacls 命令失败"
|
||||
}
|
||||
|
||||
Write-Host "$GREEN[信息]$NC 成功设置文件权限"
|
||||
}
|
||||
catch {
|
||||
Write-Host "$RED[错误]$NC 设置文件权限失败"
|
||||
Show-ManualGuide
|
||||
return
|
||||
}
|
||||
|
||||
# 验证设置
|
||||
try {
|
||||
$fileInfo = Get-ItemProperty $updaterPath
|
||||
if (-not $fileInfo.IsReadOnly) {
|
||||
Write-Host "$RED[错误]$NC 验证失败:文件权限设置可能未生效"
|
||||
Show-ManualGuide
|
||||
return
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-Host "$RED[错误]$NC 验证设置失败"
|
||||
Show-ManualGuide
|
||||
return
|
||||
}
|
||||
|
||||
Write-Host "$GREEN[信息]$NC 成功禁用自动更新"
|
||||
}
|
||||
catch {
|
||||
Write-Host "$RED[错误]$NC 发生未知错误: $_"
|
||||
Show-ManualGuide
|
||||
}
|
||||
}
|
||||
else {
|
||||
Write-Host "$GREEN[信息]$NC 保持默认设置,不进行更改"
|
||||
}
|
||||
|
||||
# 保留有效的注册表更新
|
||||
Update-MachineGuid
|
||||
|
||||
} catch {
|
||||
Write-Host "$RED[错误]$NC 主要操作失败: $_"
|
||||
Write-Host "$YELLOW[尝试]$NC 使用备选方法..."
|
||||
|
||||
try {
|
||||
# 备选方法:使用 Add-Content
|
||||
$tempFile = [System.IO.Path]::GetTempFileName()
|
||||
$config | ConvertTo-Json | Set-Content -Path $tempFile -Encoding UTF8
|
||||
Copy-Item -Path $tempFile -Destination $STORAGE_FILE -Force
|
||||
Remove-Item -Path $tempFile
|
||||
Write-Host "$GREEN[信息]$NC 使用备选方法成功写入配置"
|
||||
} catch {
|
||||
Write-Host "$RED[错误]$NC 所有尝试都失败了"
|
||||
Write-Host "错误详情: $_"
|
||||
Write-Host "目标文件: $STORAGE_FILE"
|
||||
Write-Host "请确保您有足够的权限访问该文件"
|
||||
Read-Host "按回车键退出"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Read-Host "按回车键退出"
|
||||
exit 0
|
||||
|
||||
# 在文件写入部分修改
|
||||
function Write-ConfigFile {
|
||||
param($config, $filePath)
|
||||
|
||||
try {
|
||||
# 使用 UTF8 无 BOM 编码
|
||||
$utf8NoBom = New-Object System.Text.UTF8Encoding $false
|
||||
$jsonContent = $config | ConvertTo-Json -Depth 10
|
||||
|
||||
# 统一使用 LF 换行符
|
||||
$jsonContent = $jsonContent.Replace("`r`n", "`n")
|
||||
|
||||
[System.IO.File]::WriteAllText(
|
||||
[System.IO.Path]::GetFullPath($filePath),
|
||||
$jsonContent,
|
||||
$utf8NoBom
|
||||
)
|
||||
|
||||
Write-Host "$GREEN[信息]$NC 成功写入配置文件(UTF8 无 BOM)"
|
||||
}
|
||||
catch {
|
||||
throw "写入配置文件失败: $_"
|
||||
}
|
||||
}
|