cursor-storage-reset 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cursor_storage_reset-1.0.0/LICENSE +21 -0
- cursor_storage_reset-1.0.0/PKG-INFO +159 -0
- cursor_storage_reset-1.0.0/README.md +124 -0
- cursor_storage_reset-1.0.0/pyproject.toml +71 -0
- cursor_storage_reset-1.0.0/setup.cfg +4 -0
- cursor_storage_reset-1.0.0/src/cursor_storage_reset/__init__.py +31 -0
- cursor_storage_reset-1.0.0/src/cursor_storage_reset/__main__.py +3 -0
- cursor_storage_reset-1.0.0/src/cursor_storage_reset/cli.py +71 -0
- cursor_storage_reset-1.0.0/src/cursor_storage_reset/exceptions.py +12 -0
- cursor_storage_reset-1.0.0/src/cursor_storage_reset/paths.py +51 -0
- cursor_storage_reset-1.0.0/src/cursor_storage_reset/py.typed +0 -0
- cursor_storage_reset-1.0.0/src/cursor_storage_reset/storage.py +92 -0
- cursor_storage_reset-1.0.0/src/cursor_storage_reset.egg-info/PKG-INFO +159 -0
- cursor_storage_reset-1.0.0/src/cursor_storage_reset.egg-info/SOURCES.txt +18 -0
- cursor_storage_reset-1.0.0/src/cursor_storage_reset.egg-info/dependency_links.txt +1 -0
- cursor_storage_reset-1.0.0/src/cursor_storage_reset.egg-info/entry_points.txt +2 -0
- cursor_storage_reset-1.0.0/src/cursor_storage_reset.egg-info/requires.txt +8 -0
- cursor_storage_reset-1.0.0/src/cursor_storage_reset.egg-info/top_level.txt +1 -0
- cursor_storage_reset-1.0.0/tests/test_paths.py +37 -0
- cursor_storage_reset-1.0.0/tests/test_storage.py +46 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Shivanshu814
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cursor-storage-reset
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Safely regenerate Cursor globalStorage telemetry fields in storage.json
|
|
5
|
+
Author: Shivanshu814
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/shivanshu814/cursor-storage-reset
|
|
8
|
+
Project-URL: Repository, https://github.com/shivanshu814/cursor-storage-reset
|
|
9
|
+
Project-URL: Issues, https://github.com/shivanshu814/cursor-storage-reset/issues
|
|
10
|
+
Keywords: cursor,storage,telemetry,privacy
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: MacOS :: MacOS X
|
|
15
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
16
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Typing :: Typed
|
|
24
|
+
Requires-Python: >=3.8
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
License-File: LICENSE
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest>=7.4; extra == "dev"
|
|
29
|
+
Requires-Dist: pytest-cov>=4.1; extra == "dev"
|
|
30
|
+
Requires-Dist: mypy>=1.8; extra == "dev"
|
|
31
|
+
Requires-Dist: ruff>=0.3; extra == "dev"
|
|
32
|
+
Requires-Dist: build>=1.0; extra == "dev"
|
|
33
|
+
Requires-Dist: twine>=5.0; extra == "dev"
|
|
34
|
+
Dynamic: license-file
|
|
35
|
+
|
|
36
|
+
# Cursor storage reset
|
|
37
|
+
|
|
38
|
+
[](https://opensource.org/licenses/MIT)
|
|
39
|
+
[](https://www.python.org/downloads/)
|
|
40
|
+
[](https://pypi.org/project/cursor-storage-reset/)
|
|
41
|
+
|
|
42
|
+
**Repository:** [github.com/shivanshu814/cursor-storage-reset](https://github.com/shivanshu814/cursor-storage-reset) · **Author:** [@shivanshu814](https://github.com/shivanshu814)
|
|
43
|
+
|
|
44
|
+
Small, tested, cross-platform utility. It regenerates these fields in Cursor’s `storage.json`:
|
|
45
|
+
|
|
46
|
+
- `telemetry.macMachineId`
|
|
47
|
+
- `telemetry.machineId`
|
|
48
|
+
- `telemetry.devDeviceId`
|
|
49
|
+
|
|
50
|
+
Writes use an **atomic replace** (temp file + `fsync`) so a crash mid-save is unlikely to leave truncated JSON. Keys outside the telemetry trio are preserved.
|
|
51
|
+
|
|
52
|
+
## Install
|
|
53
|
+
|
|
54
|
+
### From PyPI (recommended)
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
pip install cursor-storage-reset
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
PyPI project: [pypi.org/project/cursor-storage-reset](https://pypi.org/project/cursor-storage-reset/).
|
|
61
|
+
|
|
62
|
+
### From source
|
|
63
|
+
|
|
64
|
+
Clone (same name as on GitHub):
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
git clone https://github.com/shivanshu814/cursor-storage-reset.git
|
|
68
|
+
cd cursor-storage-reset
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
From the repository root (editable install for development):
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
python3 -m venv .venv
|
|
75
|
+
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
76
|
+
pip install -e ".[dev]"
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Or install without dev tools:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
pip install .
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
After install, the console entry point `cursor-storage-reset` is available.
|
|
86
|
+
|
|
87
|
+
## Usage
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# Use the default path for the current OS
|
|
91
|
+
cursor-storage-reset
|
|
92
|
+
|
|
93
|
+
# Custom file
|
|
94
|
+
cursor-storage-reset /path/to/storage.json
|
|
95
|
+
|
|
96
|
+
# Show the resolved default for this machine
|
|
97
|
+
cursor-storage-reset --print-default
|
|
98
|
+
|
|
99
|
+
python -m cursor_storage_reset --version
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Restart Cursor after a successful run.
|
|
103
|
+
|
|
104
|
+
### Default paths
|
|
105
|
+
|
|
106
|
+
| OS | Default `storage.json` |
|
|
107
|
+
|--------|-------------------------|
|
|
108
|
+
| macOS | `~/Library/Application Support/Cursor/User/globalStorage/storage.json` |
|
|
109
|
+
| Linux | `$XDG_CONFIG_HOME/Cursor/User/globalStorage/storage.json`, or `~/.config/...` if unset |
|
|
110
|
+
| Windows | `%APPDATA%\Cursor\User\globalStorage\storage.json` |
|
|
111
|
+
|
|
112
|
+
## Development
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
pip install -e ".[dev]"
|
|
116
|
+
pytest
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Maintainer: publish to PyPI
|
|
120
|
+
|
|
121
|
+
1. Create an account on [pypi.org](https://pypi.org) and an **API token** (scope: entire account or this project).
|
|
122
|
+
2. Build and upload (do **not** commit the token):
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
pip install build twine
|
|
126
|
+
python -m build
|
|
127
|
+
twine check dist/*
|
|
128
|
+
TWINE_USERNAME=__token__ TWINE_PASSWORD=pypi-YOUR_TOKEN_HERE twine upload dist/*
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Use [TestPyPI](https://test.pypi.org/) first if you want a dry run (`twine upload --repository testpypi dist/*` after configuring `~/.pypirc`).
|
|
132
|
+
|
|
133
|
+
**Without storing a token in GitHub:** enable [Trusted Publishing](https://docs.pypi.org/trusted-publishers/) on PyPI for project `cursor-storage-reset`, repository `shivanshu814/cursor-storage-reset`, workflow `publish-pypi.yml`. Then open **Actions → Publish Python package to PyPI → Run workflow**, or push a tag `v1.0.0` to trigger a release build.
|
|
134
|
+
|
|
135
|
+
## API
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
from pathlib import Path
|
|
139
|
+
from cursor_storage_reset import default_storage_path, refresh_telemetry_ids
|
|
140
|
+
|
|
141
|
+
refresh_telemetry_ids(Path("/explicit/storage.json"))
|
|
142
|
+
print(default_storage_path())
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Responsibility
|
|
146
|
+
|
|
147
|
+
This tool only edits **local** files you point it at. You must follow Cursor’s terms of service and applicable law. Use for legitimate purposes (e.g. privacy, troubleshooting your own setup).
|
|
148
|
+
|
|
149
|
+
## Layout
|
|
150
|
+
|
|
151
|
+
| Path | Role |
|
|
152
|
+
|------|------|
|
|
153
|
+
| `src/cursor_storage_reset/paths.py` | Pure path resolution (testable per OS) |
|
|
154
|
+
| `src/cursor_storage_reset/storage.py` | JSON load/save + atomic write + ID generation |
|
|
155
|
+
| `src/cursor_storage_reset/cli.py` | `argparse` CLI |
|
|
156
|
+
| `src/cursor_storage_reset/exceptions.py` | Narrow error types |
|
|
157
|
+
| `tests/` | Pytest coverage for paths + storage |
|
|
158
|
+
|
|
159
|
+
Legacy root-level `mac.py` / `linux.py` / `windows.py` scripts were removed in favor of one cross-platform CLI and `python -m cursor_storage_reset`.
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# Cursor storage reset
|
|
2
|
+
|
|
3
|
+
[](https://opensource.org/licenses/MIT)
|
|
4
|
+
[](https://www.python.org/downloads/)
|
|
5
|
+
[](https://pypi.org/project/cursor-storage-reset/)
|
|
6
|
+
|
|
7
|
+
**Repository:** [github.com/shivanshu814/cursor-storage-reset](https://github.com/shivanshu814/cursor-storage-reset) · **Author:** [@shivanshu814](https://github.com/shivanshu814)
|
|
8
|
+
|
|
9
|
+
Small, tested, cross-platform utility. It regenerates these fields in Cursor’s `storage.json`:
|
|
10
|
+
|
|
11
|
+
- `telemetry.macMachineId`
|
|
12
|
+
- `telemetry.machineId`
|
|
13
|
+
- `telemetry.devDeviceId`
|
|
14
|
+
|
|
15
|
+
Writes use an **atomic replace** (temp file + `fsync`) so a crash mid-save is unlikely to leave truncated JSON. Keys outside the telemetry trio are preserved.
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
### From PyPI (recommended)
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install cursor-storage-reset
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
PyPI project: [pypi.org/project/cursor-storage-reset](https://pypi.org/project/cursor-storage-reset/).
|
|
26
|
+
|
|
27
|
+
### From source
|
|
28
|
+
|
|
29
|
+
Clone (same name as on GitHub):
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
git clone https://github.com/shivanshu814/cursor-storage-reset.git
|
|
33
|
+
cd cursor-storage-reset
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
From the repository root (editable install for development):
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
python3 -m venv .venv
|
|
40
|
+
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
41
|
+
pip install -e ".[dev]"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Or install without dev tools:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pip install .
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
After install, the console entry point `cursor-storage-reset` is available.
|
|
51
|
+
|
|
52
|
+
## Usage
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# Use the default path for the current OS
|
|
56
|
+
cursor-storage-reset
|
|
57
|
+
|
|
58
|
+
# Custom file
|
|
59
|
+
cursor-storage-reset /path/to/storage.json
|
|
60
|
+
|
|
61
|
+
# Show the resolved default for this machine
|
|
62
|
+
cursor-storage-reset --print-default
|
|
63
|
+
|
|
64
|
+
python -m cursor_storage_reset --version
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Restart Cursor after a successful run.
|
|
68
|
+
|
|
69
|
+
### Default paths
|
|
70
|
+
|
|
71
|
+
| OS | Default `storage.json` |
|
|
72
|
+
|--------|-------------------------|
|
|
73
|
+
| macOS | `~/Library/Application Support/Cursor/User/globalStorage/storage.json` |
|
|
74
|
+
| Linux | `$XDG_CONFIG_HOME/Cursor/User/globalStorage/storage.json`, or `~/.config/...` if unset |
|
|
75
|
+
| Windows | `%APPDATA%\Cursor\User\globalStorage\storage.json` |
|
|
76
|
+
|
|
77
|
+
## Development
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
pip install -e ".[dev]"
|
|
81
|
+
pytest
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Maintainer: publish to PyPI
|
|
85
|
+
|
|
86
|
+
1. Create an account on [pypi.org](https://pypi.org) and an **API token** (scope: entire account or this project).
|
|
87
|
+
2. Build and upload (do **not** commit the token):
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
pip install build twine
|
|
91
|
+
python -m build
|
|
92
|
+
twine check dist/*
|
|
93
|
+
TWINE_USERNAME=__token__ TWINE_PASSWORD=pypi-YOUR_TOKEN_HERE twine upload dist/*
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Use [TestPyPI](https://test.pypi.org/) first if you want a dry run (`twine upload --repository testpypi dist/*` after configuring `~/.pypirc`).
|
|
97
|
+
|
|
98
|
+
**Without storing a token in GitHub:** enable [Trusted Publishing](https://docs.pypi.org/trusted-publishers/) on PyPI for project `cursor-storage-reset`, repository `shivanshu814/cursor-storage-reset`, workflow `publish-pypi.yml`. Then open **Actions → Publish Python package to PyPI → Run workflow**, or push a tag `v1.0.0` to trigger a release build.
|
|
99
|
+
|
|
100
|
+
## API
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
from pathlib import Path
|
|
104
|
+
from cursor_storage_reset import default_storage_path, refresh_telemetry_ids
|
|
105
|
+
|
|
106
|
+
refresh_telemetry_ids(Path("/explicit/storage.json"))
|
|
107
|
+
print(default_storage_path())
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Responsibility
|
|
111
|
+
|
|
112
|
+
This tool only edits **local** files you point it at. You must follow Cursor’s terms of service and applicable law. Use for legitimate purposes (e.g. privacy, troubleshooting your own setup).
|
|
113
|
+
|
|
114
|
+
## Layout
|
|
115
|
+
|
|
116
|
+
| Path | Role |
|
|
117
|
+
|------|------|
|
|
118
|
+
| `src/cursor_storage_reset/paths.py` | Pure path resolution (testable per OS) |
|
|
119
|
+
| `src/cursor_storage_reset/storage.py` | JSON load/save + atomic write + ID generation |
|
|
120
|
+
| `src/cursor_storage_reset/cli.py` | `argparse` CLI |
|
|
121
|
+
| `src/cursor_storage_reset/exceptions.py` | Narrow error types |
|
|
122
|
+
| `tests/` | Pytest coverage for paths + storage |
|
|
123
|
+
|
|
124
|
+
Legacy root-level `mac.py` / `linux.py` / `windows.py` scripts were removed in favor of one cross-platform CLI and `python -m cursor_storage_reset`.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "cursor-storage-reset"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Safely regenerate Cursor globalStorage telemetry fields in storage.json"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.8"
|
|
12
|
+
authors = [{ name = "Shivanshu814" }]
|
|
13
|
+
keywords = ["cursor", "storage", "telemetry", "privacy"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 5 - Production/Stable",
|
|
16
|
+
"Environment :: Console",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"Operating System :: MacOS :: MacOS X",
|
|
19
|
+
"Operating System :: Microsoft :: Windows",
|
|
20
|
+
"Operating System :: POSIX :: Linux",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Programming Language :: Python :: 3.8",
|
|
23
|
+
"Programming Language :: Python :: 3.9",
|
|
24
|
+
"Programming Language :: Python :: 3.10",
|
|
25
|
+
"Programming Language :: Python :: 3.11",
|
|
26
|
+
"Programming Language :: Python :: 3.12",
|
|
27
|
+
"Typing :: Typed",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[project.urls]
|
|
31
|
+
Homepage = "https://github.com/shivanshu814/cursor-storage-reset"
|
|
32
|
+
Repository = "https://github.com/shivanshu814/cursor-storage-reset"
|
|
33
|
+
Issues = "https://github.com/shivanshu814/cursor-storage-reset/issues"
|
|
34
|
+
|
|
35
|
+
[project.scripts]
|
|
36
|
+
cursor-storage-reset = "cursor_storage_reset.cli:main"
|
|
37
|
+
|
|
38
|
+
[project.optional-dependencies]
|
|
39
|
+
dev = [
|
|
40
|
+
"pytest>=7.4",
|
|
41
|
+
"pytest-cov>=4.1",
|
|
42
|
+
"mypy>=1.8",
|
|
43
|
+
"ruff>=0.3",
|
|
44
|
+
"build>=1.0",
|
|
45
|
+
"twine>=5.0",
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
[tool.setuptools.packages.find]
|
|
49
|
+
where = ["src"]
|
|
50
|
+
|
|
51
|
+
[tool.setuptools.package-data]
|
|
52
|
+
cursor_storage_reset = ["py.typed"]
|
|
53
|
+
|
|
54
|
+
[tool.pytest.ini_options]
|
|
55
|
+
testpaths = ["tests"]
|
|
56
|
+
addopts = "-ra -q"
|
|
57
|
+
|
|
58
|
+
[tool.coverage.run]
|
|
59
|
+
branch = true
|
|
60
|
+
source = ["cursor_storage_reset"]
|
|
61
|
+
|
|
62
|
+
[tool.mypy]
|
|
63
|
+
python_version = "3.8"
|
|
64
|
+
packages = ["cursor_storage_reset"]
|
|
65
|
+
strict = true
|
|
66
|
+
show_error_codes = true
|
|
67
|
+
|
|
68
|
+
[tool.ruff]
|
|
69
|
+
line-length = 100
|
|
70
|
+
target-version = "py38"
|
|
71
|
+
select = ["E", "F", "I", "UP"]
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Regenerate Cursor ``storage.json`` telemetry fields with atomic persistence.
|
|
3
|
+
|
|
4
|
+
Public API is intentionally small: path resolution, the refresh operation,
|
|
5
|
+
and version metadata for packaging.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from cursor_storage_reset.paths import default_storage_path, resolve_default_storage_path
|
|
11
|
+
from cursor_storage_reset.storage import TELEMETRY_KEYS, refresh_telemetry_ids
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"TELEMETRY_KEYS",
|
|
15
|
+
"default_storage_path",
|
|
16
|
+
"refresh_telemetry_ids",
|
|
17
|
+
"resolve_default_storage_path",
|
|
18
|
+
"__version__",
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _package_version() -> str:
|
|
23
|
+
try:
|
|
24
|
+
from importlib.metadata import version
|
|
25
|
+
|
|
26
|
+
return version("cursor-storage-reset")
|
|
27
|
+
except Exception:
|
|
28
|
+
return "1.0.0"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
__version__ = _package_version()
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""Command-line interface."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import sys
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from cursor_storage_reset import __version__
|
|
10
|
+
from cursor_storage_reset.exceptions import InvalidStorageFile
|
|
11
|
+
from cursor_storage_reset.paths import default_storage_path
|
|
12
|
+
from cursor_storage_reset.storage import refresh_telemetry_ids
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _build_parser() -> argparse.ArgumentParser:
|
|
16
|
+
parser = argparse.ArgumentParser(
|
|
17
|
+
description=(
|
|
18
|
+
"Regenerate Cursor globalStorage telemetry identifiers in storage.json "
|
|
19
|
+
"(atomic write; other keys preserved)."
|
|
20
|
+
),
|
|
21
|
+
)
|
|
22
|
+
parser.add_argument(
|
|
23
|
+
"storage_path",
|
|
24
|
+
nargs="?",
|
|
25
|
+
type=Path,
|
|
26
|
+
help="Path to storage.json (default: platform-specific Cursor location)",
|
|
27
|
+
)
|
|
28
|
+
parser.add_argument(
|
|
29
|
+
"--print-default",
|
|
30
|
+
action="store_true",
|
|
31
|
+
help="Print the default storage path for this platform and exit",
|
|
32
|
+
)
|
|
33
|
+
parser.add_argument(
|
|
34
|
+
"--version",
|
|
35
|
+
action="version",
|
|
36
|
+
version=f"%(prog)s {__version__}",
|
|
37
|
+
)
|
|
38
|
+
return parser
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def main(argv: list[str] | None = None) -> int:
|
|
42
|
+
args = _build_parser().parse_args(argv)
|
|
43
|
+
|
|
44
|
+
default_path = default_storage_path()
|
|
45
|
+
if args.print_default:
|
|
46
|
+
print(default_path)
|
|
47
|
+
return 0
|
|
48
|
+
|
|
49
|
+
path = args.storage_path.expanduser() if args.storage_path else default_path
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
updated = refresh_telemetry_ids(path)
|
|
53
|
+
except FileNotFoundError:
|
|
54
|
+
print(
|
|
55
|
+
"Could not find storage.json. Ensure Cursor is installed "
|
|
56
|
+
"or pass an explicit path.",
|
|
57
|
+
file=sys.stderr,
|
|
58
|
+
)
|
|
59
|
+
return 1
|
|
60
|
+
except InvalidStorageFile as exc:
|
|
61
|
+
print(str(exc), file=sys.stderr)
|
|
62
|
+
return 1
|
|
63
|
+
|
|
64
|
+
print("Success: Cursor telemetry identifiers were reset.")
|
|
65
|
+
print(f"Updated: {updated}")
|
|
66
|
+
print("Restart Cursor for changes to take effect.")
|
|
67
|
+
return 0
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
if __name__ == "__main__":
|
|
71
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""Domain-specific errors (kept narrow; stdlib errors used where they fit)."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class StorageResetError(RuntimeError):
|
|
8
|
+
"""Base class for recoverable storage refresh failures."""
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class InvalidStorageFile(StorageResetError):
|
|
12
|
+
"""Raised when ``storage.json`` is missing, unreadable, or not a JSON object."""
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Platform-aware resolution of Cursor's ``storage.json`` location.
|
|
3
|
+
|
|
4
|
+
Uses the same layout Cursor documents for user data (VS Code–style paths).
|
|
5
|
+
``XDG_CONFIG_HOME`` is honored on Linux/BSD; ``%APPDATA%`` on Windows.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
import sys
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Mapping
|
|
14
|
+
|
|
15
|
+
__all__ = ["default_storage_path", "resolve_default_storage_path"]
|
|
16
|
+
|
|
17
|
+
_CURSOR_STORAGE = Path("Cursor/User/globalStorage/storage.json")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def resolve_default_storage_path(
|
|
21
|
+
*,
|
|
22
|
+
platform: str,
|
|
23
|
+
home: Path,
|
|
24
|
+
environ: Mapping[str, str],
|
|
25
|
+
) -> Path:
|
|
26
|
+
"""
|
|
27
|
+
Pure path resolver for testing and nonstandard environments.
|
|
28
|
+
|
|
29
|
+
``platform`` follows ``sys.platform`` (``darwin``, ``win32``, ``linux``, …).
|
|
30
|
+
Platforms other than ``darwin`` and ``win32`` use the XDG-style config base.
|
|
31
|
+
"""
|
|
32
|
+
if platform == "darwin":
|
|
33
|
+
return home / "Library/Application Support" / _CURSOR_STORAGE
|
|
34
|
+
|
|
35
|
+
if platform == "win32":
|
|
36
|
+
appdata = str(environ.get("APPDATA", "") or "").strip()
|
|
37
|
+
root = Path(appdata) if appdata else home / "AppData" / "Roaming"
|
|
38
|
+
return root / _CURSOR_STORAGE
|
|
39
|
+
|
|
40
|
+
xdg = str(environ.get("XDG_CONFIG_HOME", "") or "").strip()
|
|
41
|
+
base = Path(xdg) if xdg else home / ".config"
|
|
42
|
+
return base / _CURSOR_STORAGE
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def default_storage_path() -> Path:
|
|
46
|
+
"""Default ``storage.json`` for this process (current platform + environment)."""
|
|
47
|
+
return resolve_default_storage_path(
|
|
48
|
+
platform=sys.platform,
|
|
49
|
+
home=Path.home(),
|
|
50
|
+
environ=os.environ,
|
|
51
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""Read/modify ``storage.json`` with atomic persistence."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
import secrets
|
|
8
|
+
import tempfile
|
|
9
|
+
import uuid
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any, Final, Mapping, MutableMapping
|
|
12
|
+
|
|
13
|
+
from cursor_storage_reset.exceptions import InvalidStorageFile
|
|
14
|
+
|
|
15
|
+
__all__ = ["TELEMETRY_KEYS", "refresh_telemetry_ids"]
|
|
16
|
+
|
|
17
|
+
TELEMETRY_KEYS: Final[tuple[str, ...]] = (
|
|
18
|
+
"telemetry.macMachineId",
|
|
19
|
+
"telemetry.machineId",
|
|
20
|
+
"telemetry.devDeviceId",
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
_TELEMETRY_HEX_ID_LENGTH = 64
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _random_hex_id(length_chars: int) -> str:
|
|
27
|
+
if length_chars <= 0 or length_chars % 2:
|
|
28
|
+
raise ValueError("length_chars must be a positive even integer")
|
|
29
|
+
return secrets.token_hex(length_chars // 2)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _load_storage(path: Path) -> MutableMapping[str, Any]:
|
|
33
|
+
try:
|
|
34
|
+
text = path.read_text(encoding="utf-8")
|
|
35
|
+
except OSError as exc:
|
|
36
|
+
raise InvalidStorageFile(f"cannot read storage file: {path}") from exc
|
|
37
|
+
try:
|
|
38
|
+
parsed = json.loads(text)
|
|
39
|
+
except json.JSONDecodeError as exc:
|
|
40
|
+
raise InvalidStorageFile(f"invalid JSON in storage file: {path}") from exc
|
|
41
|
+
if not isinstance(parsed, dict):
|
|
42
|
+
raise InvalidStorageFile(f"storage root must be a JSON object: {path}")
|
|
43
|
+
return parsed # type: ignore[return-value]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _atomic_write_json(path: Path, data: Mapping[str, Any]) -> None:
|
|
47
|
+
path = path.expanduser()
|
|
48
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
49
|
+
|
|
50
|
+
fd, tmp_name = tempfile.mkstemp(
|
|
51
|
+
prefix=f".{path.name}.",
|
|
52
|
+
suffix=".tmp",
|
|
53
|
+
dir=str(path.parent),
|
|
54
|
+
)
|
|
55
|
+
tmp_path = Path(tmp_name)
|
|
56
|
+
try:
|
|
57
|
+
with os.fdopen(fd, "w", encoding="utf-8") as handle:
|
|
58
|
+
json.dump(data, handle, indent=4)
|
|
59
|
+
handle.write("\n")
|
|
60
|
+
handle.flush()
|
|
61
|
+
os.fsync(handle.fileno())
|
|
62
|
+
os.replace(tmp_path, path)
|
|
63
|
+
except BaseException:
|
|
64
|
+
try:
|
|
65
|
+
tmp_path.unlink(missing_ok=True)
|
|
66
|
+
except AttributeError:
|
|
67
|
+
if tmp_path.exists():
|
|
68
|
+
tmp_path.unlink()
|
|
69
|
+
raise
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def refresh_telemetry_ids(storage_path: Path) -> Path:
|
|
73
|
+
"""
|
|
74
|
+
Regenerate telemetry identifiers in Cursor ``storage.json``.
|
|
75
|
+
|
|
76
|
+
Returns the resolved path written. Other keys are preserved.
|
|
77
|
+
|
|
78
|
+
Raises:
|
|
79
|
+
FileNotFoundError: if ``storage_path`` does not exist.
|
|
80
|
+
InvalidStorageFile: on read/parse errors.
|
|
81
|
+
"""
|
|
82
|
+
target = storage_path.expanduser().resolve()
|
|
83
|
+
if not target.is_file():
|
|
84
|
+
raise FileNotFoundError(str(target))
|
|
85
|
+
|
|
86
|
+
data = _load_storage(target)
|
|
87
|
+
data[TELEMETRY_KEYS[0]] = _random_hex_id(_TELEMETRY_HEX_ID_LENGTH)
|
|
88
|
+
data[TELEMETRY_KEYS[1]] = _random_hex_id(_TELEMETRY_HEX_ID_LENGTH)
|
|
89
|
+
data[TELEMETRY_KEYS[2]] = str(uuid.uuid4())
|
|
90
|
+
|
|
91
|
+
_atomic_write_json(target, data)
|
|
92
|
+
return target
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cursor-storage-reset
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Safely regenerate Cursor globalStorage telemetry fields in storage.json
|
|
5
|
+
Author: Shivanshu814
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/shivanshu814/cursor-storage-reset
|
|
8
|
+
Project-URL: Repository, https://github.com/shivanshu814/cursor-storage-reset
|
|
9
|
+
Project-URL: Issues, https://github.com/shivanshu814/cursor-storage-reset/issues
|
|
10
|
+
Keywords: cursor,storage,telemetry,privacy
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: MacOS :: MacOS X
|
|
15
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
16
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Typing :: Typed
|
|
24
|
+
Requires-Python: >=3.8
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
License-File: LICENSE
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest>=7.4; extra == "dev"
|
|
29
|
+
Requires-Dist: pytest-cov>=4.1; extra == "dev"
|
|
30
|
+
Requires-Dist: mypy>=1.8; extra == "dev"
|
|
31
|
+
Requires-Dist: ruff>=0.3; extra == "dev"
|
|
32
|
+
Requires-Dist: build>=1.0; extra == "dev"
|
|
33
|
+
Requires-Dist: twine>=5.0; extra == "dev"
|
|
34
|
+
Dynamic: license-file
|
|
35
|
+
|
|
36
|
+
# Cursor storage reset
|
|
37
|
+
|
|
38
|
+
[](https://opensource.org/licenses/MIT)
|
|
39
|
+
[](https://www.python.org/downloads/)
|
|
40
|
+
[](https://pypi.org/project/cursor-storage-reset/)
|
|
41
|
+
|
|
42
|
+
**Repository:** [github.com/shivanshu814/cursor-storage-reset](https://github.com/shivanshu814/cursor-storage-reset) · **Author:** [@shivanshu814](https://github.com/shivanshu814)
|
|
43
|
+
|
|
44
|
+
Small, tested, cross-platform utility. It regenerates these fields in Cursor’s `storage.json`:
|
|
45
|
+
|
|
46
|
+
- `telemetry.macMachineId`
|
|
47
|
+
- `telemetry.machineId`
|
|
48
|
+
- `telemetry.devDeviceId`
|
|
49
|
+
|
|
50
|
+
Writes use an **atomic replace** (temp file + `fsync`) so a crash mid-save is unlikely to leave truncated JSON. Keys outside the telemetry trio are preserved.
|
|
51
|
+
|
|
52
|
+
## Install
|
|
53
|
+
|
|
54
|
+
### From PyPI (recommended)
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
pip install cursor-storage-reset
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
PyPI project: [pypi.org/project/cursor-storage-reset](https://pypi.org/project/cursor-storage-reset/).
|
|
61
|
+
|
|
62
|
+
### From source
|
|
63
|
+
|
|
64
|
+
Clone (same name as on GitHub):
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
git clone https://github.com/shivanshu814/cursor-storage-reset.git
|
|
68
|
+
cd cursor-storage-reset
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
From the repository root (editable install for development):
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
python3 -m venv .venv
|
|
75
|
+
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
76
|
+
pip install -e ".[dev]"
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Or install without dev tools:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
pip install .
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
After install, the console entry point `cursor-storage-reset` is available.
|
|
86
|
+
|
|
87
|
+
## Usage
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# Use the default path for the current OS
|
|
91
|
+
cursor-storage-reset
|
|
92
|
+
|
|
93
|
+
# Custom file
|
|
94
|
+
cursor-storage-reset /path/to/storage.json
|
|
95
|
+
|
|
96
|
+
# Show the resolved default for this machine
|
|
97
|
+
cursor-storage-reset --print-default
|
|
98
|
+
|
|
99
|
+
python -m cursor_storage_reset --version
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Restart Cursor after a successful run.
|
|
103
|
+
|
|
104
|
+
### Default paths
|
|
105
|
+
|
|
106
|
+
| OS | Default `storage.json` |
|
|
107
|
+
|--------|-------------------------|
|
|
108
|
+
| macOS | `~/Library/Application Support/Cursor/User/globalStorage/storage.json` |
|
|
109
|
+
| Linux | `$XDG_CONFIG_HOME/Cursor/User/globalStorage/storage.json`, or `~/.config/...` if unset |
|
|
110
|
+
| Windows | `%APPDATA%\Cursor\User\globalStorage\storage.json` |
|
|
111
|
+
|
|
112
|
+
## Development
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
pip install -e ".[dev]"
|
|
116
|
+
pytest
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Maintainer: publish to PyPI
|
|
120
|
+
|
|
121
|
+
1. Create an account on [pypi.org](https://pypi.org) and an **API token** (scope: entire account or this project).
|
|
122
|
+
2. Build and upload (do **not** commit the token):
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
pip install build twine
|
|
126
|
+
python -m build
|
|
127
|
+
twine check dist/*
|
|
128
|
+
TWINE_USERNAME=__token__ TWINE_PASSWORD=pypi-YOUR_TOKEN_HERE twine upload dist/*
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Use [TestPyPI](https://test.pypi.org/) first if you want a dry run (`twine upload --repository testpypi dist/*` after configuring `~/.pypirc`).
|
|
132
|
+
|
|
133
|
+
**Without storing a token in GitHub:** enable [Trusted Publishing](https://docs.pypi.org/trusted-publishers/) on PyPI for project `cursor-storage-reset`, repository `shivanshu814/cursor-storage-reset`, workflow `publish-pypi.yml`. Then open **Actions → Publish Python package to PyPI → Run workflow**, or push a tag `v1.0.0` to trigger a release build.
|
|
134
|
+
|
|
135
|
+
## API
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
from pathlib import Path
|
|
139
|
+
from cursor_storage_reset import default_storage_path, refresh_telemetry_ids
|
|
140
|
+
|
|
141
|
+
refresh_telemetry_ids(Path("/explicit/storage.json"))
|
|
142
|
+
print(default_storage_path())
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Responsibility
|
|
146
|
+
|
|
147
|
+
This tool only edits **local** files you point it at. You must follow Cursor’s terms of service and applicable law. Use for legitimate purposes (e.g. privacy, troubleshooting your own setup).
|
|
148
|
+
|
|
149
|
+
## Layout
|
|
150
|
+
|
|
151
|
+
| Path | Role |
|
|
152
|
+
|------|------|
|
|
153
|
+
| `src/cursor_storage_reset/paths.py` | Pure path resolution (testable per OS) |
|
|
154
|
+
| `src/cursor_storage_reset/storage.py` | JSON load/save + atomic write + ID generation |
|
|
155
|
+
| `src/cursor_storage_reset/cli.py` | `argparse` CLI |
|
|
156
|
+
| `src/cursor_storage_reset/exceptions.py` | Narrow error types |
|
|
157
|
+
| `tests/` | Pytest coverage for paths + storage |
|
|
158
|
+
|
|
159
|
+
Legacy root-level `mac.py` / `linux.py` / `windows.py` scripts were removed in favor of one cross-platform CLI and `python -m cursor_storage_reset`.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/cursor_storage_reset/__init__.py
|
|
5
|
+
src/cursor_storage_reset/__main__.py
|
|
6
|
+
src/cursor_storage_reset/cli.py
|
|
7
|
+
src/cursor_storage_reset/exceptions.py
|
|
8
|
+
src/cursor_storage_reset/paths.py
|
|
9
|
+
src/cursor_storage_reset/py.typed
|
|
10
|
+
src/cursor_storage_reset/storage.py
|
|
11
|
+
src/cursor_storage_reset.egg-info/PKG-INFO
|
|
12
|
+
src/cursor_storage_reset.egg-info/SOURCES.txt
|
|
13
|
+
src/cursor_storage_reset.egg-info/dependency_links.txt
|
|
14
|
+
src/cursor_storage_reset.egg-info/entry_points.txt
|
|
15
|
+
src/cursor_storage_reset.egg-info/requires.txt
|
|
16
|
+
src/cursor_storage_reset.egg-info/top_level.txt
|
|
17
|
+
tests/test_paths.py
|
|
18
|
+
tests/test_storage.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
cursor_storage_reset
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from cursor_storage_reset.paths import resolve_default_storage_path
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_darwin_path() -> None:
|
|
9
|
+
home = Path("/Users/tester")
|
|
10
|
+
p = resolve_default_storage_path(platform="darwin", home=home, environ={})
|
|
11
|
+
assert p == home / "Library/Application Support/Cursor/User/globalStorage/storage.json"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_linux_respects_xdg() -> None:
|
|
15
|
+
home = Path("/home/tester")
|
|
16
|
+
env = {"XDG_CONFIG_HOME": "/alt/cfg"}
|
|
17
|
+
p = resolve_default_storage_path(platform="linux", home=home, environ=env)
|
|
18
|
+
assert p == Path("/alt/cfg/Cursor/User/globalStorage/storage.json")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_linux_fallback_config() -> None:
|
|
22
|
+
home = Path("/home/tester")
|
|
23
|
+
p = resolve_default_storage_path(platform="linux", home=home, environ={})
|
|
24
|
+
assert p == home / ".config/Cursor/User/globalStorage/storage.json"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_windows_uses_appdata() -> None:
|
|
28
|
+
home = Path("C:/Users/tester")
|
|
29
|
+
env = {"APPDATA": r"C:\Users\tester\AppData\Roaming"}
|
|
30
|
+
p = resolve_default_storage_path(platform="win32", home=home, environ=env)
|
|
31
|
+
assert p == Path(r"C:\Users\tester\AppData\Roaming/Cursor/User/globalStorage/storage.json")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_windows_appdata_missing_falls_back() -> None:
|
|
35
|
+
home = Path("C:/Users/tester")
|
|
36
|
+
p = resolve_default_storage_path(platform="win32", home=home, environ={})
|
|
37
|
+
assert p == home / "AppData/Roaming/Cursor/User/globalStorage/storage.json"
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
8
|
+
from cursor_storage_reset.exceptions import InvalidStorageFile
|
|
9
|
+
from cursor_storage_reset.storage import TELEMETRY_KEYS, refresh_telemetry_ids
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_refresh_preserves_other_keys(tmp_path: Path) -> None:
|
|
13
|
+
path = tmp_path / "storage.json"
|
|
14
|
+
path.write_text(
|
|
15
|
+
json.dumps(
|
|
16
|
+
{
|
|
17
|
+
"keep": "value",
|
|
18
|
+
TELEMETRY_KEYS[0]: "oldmac",
|
|
19
|
+
TELEMETRY_KEYS[1]: "oldmid",
|
|
20
|
+
TELEMETRY_KEYS[2]: "00000000-0000-0000-0000-000000000000",
|
|
21
|
+
},
|
|
22
|
+
),
|
|
23
|
+
encoding="utf-8",
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
refresh_telemetry_ids(path)
|
|
27
|
+
data = json.loads(path.read_text(encoding="utf-8"))
|
|
28
|
+
|
|
29
|
+
assert data["keep"] == "value"
|
|
30
|
+
assert data[TELEMETRY_KEYS[0]] != "oldmac"
|
|
31
|
+
assert data[TELEMETRY_KEYS[1]] != "oldmid"
|
|
32
|
+
assert data[TELEMETRY_KEYS[2]] != "00000000-0000-0000-0000-000000000000"
|
|
33
|
+
assert len(data[TELEMETRY_KEYS[0]]) == 64
|
|
34
|
+
assert len(data[TELEMETRY_KEYS[1]]) == 64
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_missing_file(tmp_path: Path) -> None:
|
|
38
|
+
with pytest.raises(FileNotFoundError):
|
|
39
|
+
refresh_telemetry_ids(tmp_path / "nope.json")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_invalid_json(tmp_path: Path) -> None:
|
|
43
|
+
path = tmp_path / "storage.json"
|
|
44
|
+
path.write_text("{not json", encoding="utf-8")
|
|
45
|
+
with pytest.raises(InvalidStorageFile):
|
|
46
|
+
refresh_telemetry_ids(path)
|