redfetch 1.3.0__tar.gz → 1.4.1__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.
Files changed (69) hide show
  1. {redfetch-1.3.0 → redfetch-1.4.1}/.github/workflows/pipeline.yml +28 -12
  2. {redfetch-1.3.0 → redfetch-1.4.1}/.gitignore +4 -0
  3. redfetch-1.4.1/AGENTS.md +21 -0
  4. {redfetch-1.3.0 → redfetch-1.4.1}/CHANGELOG.md +19 -0
  5. {redfetch-1.3.0 → redfetch-1.4.1}/PKG-INFO +50 -44
  6. {redfetch-1.3.0 → redfetch-1.4.1}/README.md +45 -39
  7. {redfetch-1.3.0 → redfetch-1.4.1}/pyproject.toml +9 -3
  8. {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/__about__.py +2 -2
  9. redfetch-1.4.1/src/redfetch/api.py +50 -0
  10. {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/config.py +31 -19
  11. {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/detecteq.py +26 -0
  12. {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/download.py +199 -36
  13. {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/listener.py +14 -4
  14. {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/main.py +131 -163
  15. {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/navmesh.py +32 -98
  16. {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/net.py +1 -10
  17. redfetch-1.4.1/src/redfetch/post_update.py +143 -0
  18. redfetch-1.4.1/src/redfetch/processes.py +336 -0
  19. {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/runtime_errors.py +1 -3
  20. redfetch-1.4.1/src/redfetch/shortcuts.py +229 -0
  21. {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/store.py +11 -15
  22. {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/sync.py +74 -29
  23. {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/sync_discovery.py +17 -53
  24. {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/sync_planner.py +1 -3
  25. redfetch-1.4.1/src/redfetch/sync_remote.py +135 -0
  26. {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/sync_types.py +33 -4
  27. {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/terminal_ui.py +404 -590
  28. {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/terminal_ui.tcss +25 -77
  29. {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/utils.py +56 -2
  30. redfetch-1.4.1/tests/cli_reference.py +138 -0
  31. redfetch-1.4.1/tests/test_autologin_eqpath.py +74 -0
  32. {redfetch-1.3.0 → redfetch-1.4.1}/tests/test_check.py +2 -2
  33. redfetch-1.4.1/tests/test_close_gating.py +800 -0
  34. {redfetch-1.3.0 → redfetch-1.4.1}/tests/test_env_selection.py +19 -15
  35. redfetch-1.4.1/tests/test_fetch_sync_info.py +67 -0
  36. redfetch-1.4.1/tests/test_licensed_resources_filtering.py +83 -0
  37. redfetch-1.4.1/tests/test_process_lock.py +48 -0
  38. redfetch-1.4.1/tests/test_readme_cli_reference.py +36 -0
  39. redfetch-1.4.1/tests/test_remote_snapshot.py +97 -0
  40. redfetch-1.4.1/tests/test_self_heal_eqpath.py +198 -0
  41. redfetch-1.4.1/tests/test_shortcuts.py +250 -0
  42. redfetch-1.4.1/tests/test_staged_extraction.py +340 -0
  43. {redfetch-1.3.0 → redfetch-1.4.1}/tests/test_sync_queue_rules.py +29 -8
  44. redfetch-1.3.0/src/redfetch/api.py +0 -134
  45. redfetch-1.3.0/src/redfetch/processes.py +0 -118
  46. redfetch-1.3.0/src/redfetch/sync_remote.py +0 -182
  47. redfetch-1.3.0/src/redfetch/unloadmq.py +0 -148
  48. redfetch-1.3.0/tests/test_breadcrumb.py +0 -56
  49. redfetch-1.3.0/tests/test_cancellation_e2e.py +0 -123
  50. redfetch-1.3.0/tests/test_licensed_resources_filtering.py +0 -320
  51. redfetch-1.3.0/tests/test_publish_message_file_fallback.py +0 -158
  52. redfetch-1.3.0/tests/test_uninstall_e2e.py +0 -233
  53. {redfetch-1.3.0 → redfetch-1.4.1}/LICENSE +0 -0
  54. {redfetch-1.3.0 → redfetch-1.4.1}/redfetch.ico +0 -0
  55. {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/__init__.py +0 -0
  56. {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/auth.py +0 -0
  57. {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/config_firstrun.py +0 -0
  58. {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/desktop_shortcut.py +0 -0
  59. {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/meta.py +0 -0
  60. {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/push.py +0 -0
  61. {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/settings.toml +0 -0
  62. {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/special.py +0 -0
  63. {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/sync_executor.py +0 -0
  64. {redfetch-1.3.0 → redfetch-1.4.1}/src/redfetch/update_status.py +0 -0
  65. {redfetch-1.3.0 → redfetch-1.4.1}/tests/test_first_run_setup.py +0 -0
  66. {redfetch-1.3.0 → redfetch-1.4.1}/tests/test_is_special_or_dependency.py +0 -0
  67. {redfetch-1.3.0 → redfetch-1.4.1}/tests/test_path_reset.py +0 -0
  68. {redfetch-1.3.0 → redfetch-1.4.1}/tests/test_settings_local_deltas.py +0 -0
  69. {redfetch-1.3.0 → redfetch-1.4.1}/tests/test_sync_pipeline_regressions.py +0 -0
@@ -60,6 +60,13 @@ jobs:
60
60
  run: |
61
61
  hatch build
62
62
 
63
+ - name: Upload Python distribution
64
+ if: github.ref == 'refs/heads/develop'
65
+ uses: actions/upload-artifact@v7
66
+ with:
67
+ name: python-dist
68
+ path: dist/
69
+
63
70
  - name: Get Version
64
71
  id: get_version
65
72
  run: |
@@ -89,22 +96,26 @@ jobs:
89
96
  with:
90
97
  fetch-depth: 0 # Ensure full Git history is available for versioning
91
98
 
99
+ - name: Download Python distribution
100
+ if: github.ref == 'refs/heads/develop'
101
+ uses: actions/download-artifact@v8
102
+ with:
103
+ name: python-dist
104
+ path: dist
105
+
92
106
  - name: Set Environment Variables
93
107
  id: set_env_vars
94
108
  shell: pwsh
95
109
  run: |
96
- # Set static environment variables
97
- "PYAPP_PROJECT_NAME=redfetch" >> $env:GITHUB_ENV
98
110
  "PYAPP_EXEC_MODULE=redfetch.main" >> $env:GITHUB_ENV
99
111
  "PYAPP_PASS_LOCATION=1" >> $env:GITHUB_ENV
100
112
 
101
- # Conditionally set PYAPP_PIP_EXTRA_ARGS based on the branch or tag
102
113
  if ($env:GITHUB_REF -eq 'refs/heads/develop') {
103
- $pipArgs = "--index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/"
114
+ "PYAPP_EMBED_WHEEL=1" >> $env:GITHUB_ENV
104
115
  } else {
105
- $pipArgs = ""
116
+ "PYAPP_PROJECT_NAME=redfetch" >> $env:GITHUB_ENV
106
117
  }
107
- "PYAPP_PIP_EXTRA_ARGS=$pipArgs" >> $env:GITHUB_ENV
118
+ "PYAPP_PIP_EXTRA_ARGS=" >> $env:GITHUB_ENV
108
119
 
109
120
  # Output the values for verification
110
121
  Get-Content $env:GITHUB_ENV
@@ -145,18 +156,23 @@ jobs:
145
156
 
146
157
  - name: Build PyApp Executable
147
158
  run: |
148
- $env:PYAPP_PROJECT_NAME = "${{ env.PYAPP_PROJECT_NAME }}"
159
+ if ($env:PYAPP_EMBED_WHEEL -eq '1') {
160
+ # Develop embeds the built wheel so its deps resolve from PyPI rather than TestPyPI
161
+ $wheel = Get-ChildItem -Path "$env:GITHUB_WORKSPACE\dist\*.whl" | Select-Object -First 1
162
+ if (-not $wheel) { throw "No redfetch wheel found in dist/" }
163
+ $env:PYAPP_PROJECT_PATH = $wheel.FullName
164
+ Remove-Item Env:PYAPP_PROJECT_NAME -ErrorAction SilentlyContinue
165
+ Remove-Item Env:PYAPP_PROJECT_VERSION -ErrorAction SilentlyContinue
166
+ }
167
+
149
168
  $env:PYAPP_EXEC_MODULE = "${{ env.PYAPP_EXEC_MODULE }}"
150
169
  $env:PYAPP_PASS_LOCATION = "${{ env.PYAPP_PASS_LOCATION }}"
151
170
  $env:PYAPP_PIP_EXTRA_ARGS = "${{ env.PYAPP_PIP_EXTRA_ARGS }}"
152
- $env:PYAPP_PROJECT_VERSION = "${{ env.PYAPP_PROJECT_VERSION }}"
153
-
154
- Write-Host "PYAPP_PROJECT_NAME: $env:PYAPP_PROJECT_NAME"
171
+
155
172
  Write-Host "PYAPP_EXEC_MODULE: $env:PYAPP_EXEC_MODULE"
156
173
  Write-Host "PYAPP_PASS_LOCATION: $env:PYAPP_PASS_LOCATION"
157
174
  Write-Host "PYAPP_PIP_EXTRA_ARGS: $env:PYAPP_PIP_EXTRA_ARGS"
158
- Write-Host "PYAPP_PROJECT_VERSION: $env:PYAPP_PROJECT_VERSION"
159
-
175
+
160
176
  cargo build --release
161
177
  working-directory: .\pyapp-latest
162
178
  shell: pwsh
@@ -7,6 +7,10 @@ __pycache__/
7
7
  #virtual environments
8
8
  env/
9
9
  venv/
10
+ .venv/
11
+
12
+ # uv (project is hatch-managed)
13
+ uv.lock
10
14
 
11
15
  # Downloaded files
12
16
  Downloads/
@@ -0,0 +1,21 @@
1
+ # AGENTS.md
2
+
3
+ **redfetch** — a CLI/TUI for downloading and publishing MacroQuest and EverQuest scripts and software via the RedGuides API.
4
+
5
+ ## Running tests
6
+
7
+ Use `hatch test` — not `pytest`, `uv`, or an ad-hoc venv
8
+
9
+ ```sh
10
+ hatch test # full suite
11
+ hatch test tests/test_check.py # single file
12
+ ```
13
+ ## Libraries
14
+
15
+ - [Textual](https://textual.textualize.io/) - TUI
16
+ - [Hatch](https://hatch.pypa.io/latest/) - Build system
17
+ - [PYPA](https://www.pypa.io/en/latest/specifications/) - Package index
18
+
19
+ ## Conventions
20
+
21
+ - Use the `dev` environment (`hatch shell dev`) for development
@@ -5,6 +5,25 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.4.1] - 2026-07-05
9
+
10
+ ### Changed
11
+ - The update button once again doubles as a *launch MQ* button, even when no update occurs.
12
+
13
+ ### Added
14
+ - `redfetch run` and `redfetch open` CLI commands act as shortcuts, for example `redfetch run eqgame` will start EQgame.exe, `redfetch open eqhost` will open the eqhost.txt file, etc. These mirror the TUI shortcuts.
15
+
16
+ ## [1.4.0] - 2026-07-05
17
+
18
+ ### Changed
19
+ - You can now update while playing. Updates take effect the next time you start Very Vanilla MQ.
20
+
21
+ ### Added
22
+ - New logic for restarting VVMQ after an update. redfetch will never touch EQ nor unload MQ.
23
+
24
+ ### Removed
25
+ - The "Close MQ pre-update" setting and TUI toggle.
26
+
8
27
  ## [1.3.0] - 2026-06-26
9
28
 
10
29
  ### Added
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: redfetch
3
- Version: 1.3.0
3
+ Version: 1.4.1
4
4
  Summary: Download and publish EverQuest scripts and software using the RedGuides API
5
5
  Project-URL: Homepage, https://www.redguides.com
6
6
  Project-URL: Documentation, https://www.redguides.com/community/resources/redfetch.3177/
7
7
  Project-URL: Repository, https://github.com/RedGuides/redfetch
8
8
  Project-URL: Issues, https://github.com/RedGuides/redfetch/issues
9
9
  Project-URL: Changelog, https://github.com/RedGuides/redfetch/blob/main/CHANGELOG.md
10
- Project-URL: Source_archive, https://github.com/RedGuides/redfetch/archive/aaf2d42ab82d45abeadfafa943e2b61abba2cb60.zip
10
+ Project-URL: Source_archive, https://github.com/RedGuides/redfetch/archive/6c3be23e13fb9b1cb6eed3c065b930111c484175.zip
11
11
  Author-email: Redbot <ask@redguides.com>
12
12
  License-Expression: GPL-3.0-or-later
13
13
  License-File: LICENSE
@@ -21,9 +21,10 @@ Requires-Python: >=3.12
21
21
  Requires-Dist: aiofiles~=25.1
22
22
  Requires-Dist: aiohttp~=3.14
23
23
  Requires-Dist: aiosqlite~=0.22
24
- Requires-Dist: cachetools~=7.1
25
24
  Requires-Dist: diskcache~=5.6
26
- Requires-Dist: dynaconf<3.3,~=3.2
25
+ Requires-Dist: dynaconf>=3.3.2,~=3.3
26
+ Requires-Dist: filelock~=3.29
27
+ Requires-Dist: hishel[async,httpx]~=1.3
27
28
  Requires-Dist: httpx[http2]~=0.28
28
29
  Requires-Dist: keepachangelog~=2.0
29
30
  Requires-Dist: keyring~=25.7
@@ -32,7 +33,6 @@ Requires-Dist: packaging~=26.2
32
33
  Requires-Dist: platformdirs~=4.10
33
34
  Requires-Dist: psutil~=7.2
34
35
  Requires-Dist: pydantic~=2.13
35
- Requires-Dist: pyperclip~=1.11
36
36
  Requires-Dist: pywin32; sys_platform == 'win32'
37
37
  Requires-Dist: rich~=15.0
38
38
  Requires-Dist: tenacity~=9.1
@@ -105,42 +105,56 @@ To run redfetch from the command line:
105
105
 
106
106
  ## Command Line Reference
107
107
 
108
- > Run `redfetch --help` or `.\redfetch.exe --help` to see something like this in your terminal:
109
- >
108
+ <!-- BEGIN GENERATED CLI REFERENCE -->
109
+ > Run `redfetch --help` for the current list, or `redfetch <COMMAND> --help` for a command's options. It looks like:
110
+ >
110
111
  > ### 📦 Resource Management
111
- > - `update` - Update all watched and special resources
112
- > - `--force` / `-f` - Force re-download of all watched resources
113
- > - `--server` / `-s` - Switch to this server before updating (`LIVE`, `TEST`, `EMU`)
114
- > - `download <ID_OR_URL>` - Download a specific resource by ID or URL
115
- > - `--force` / `-f` - Force re-download by resetting this resource's download date
116
- > - `--server` / `-s` - Switch to this server before downloading (`LIVE`, `TEST`, `EMU`)
117
- > - `list` - List resources and dependencies in the cache database
118
- > - `reset` - Reset download dates for watched resources
119
- >
112
+ > - `update` - Update all *watched* and special resources.
113
+ > - `--force` / `-f` - Force re-download of all watched resources.
114
+ > - `--server` / `-s` - Update this server for this run only, without changing your current server (LIVE, TEST, EMU).
115
+ > - `download <ID_OR_URL>` - Download a specific resource by ID or URL.
116
+ > - `ID_OR_URL` - RedGuides resource ID or URL
117
+ > - `--force` / `-f` - Force re-download by resetting this resource's download date.
118
+ > - `--server` / `-s` - Download from this server for this run only, without changing your current server (LIVE, TEST, EMU).
119
+ > - `check` - Non-interactive update check (for automation.)
120
+ > - `--server` / `-s` - Check this server's env for this run only, without persisting it (LIVE, TEST, EMU).
121
+ > - `list` - List resources and dependencies in your local cache.
122
+ > - `reset` - Reset download dates for *watched resources* in the database.
123
+ >
124
+ > ### 🔧 System & Utilities
125
+ > - `ui` - Launch the *Terminal User Interface*.
126
+ > - `run [SHORTCUT]` - Run a shortcut (e.g. **vvmq**, **eqbcs**, **myseq**). **run** by itself will show a full list.
127
+ > - `SHORTCUT` - Shortcut to run: vvmq, eqbcs, eq, eqgame, etc.
128
+ > - `--server` / `-s` - Run for this server this run only, without changing your current server (LIVE, TEST, EMU).
129
+ > - `open [SHORTCUT]` - Open a folder or file (e.g. **downloads**, **eqhost**). **open** by itself will show a full list.
130
+ > - `SHORTCUT` - Folder/file to open: downloads, vvmq, eq, etc.
131
+ > - `--server` / `-s` - Resolve paths for this server this run only, without changing your current server (LIVE, TEST, EMU).
132
+ > - `web` - Launch the **RedGuides.com** web interface.
133
+ > - `version` - Show version and exit.
134
+ > - `uninstall` - Uninstall **redfetch** and clean up data.
135
+ > - `logout` - Log out and clear cached token and API cache.
136
+ >
120
137
  > ### 🍔 Configuration
121
- > - `server <SERVER>` - Switch the current server/environment to `LIVE`, `TEST`, or `EMU`
122
- > - `config <SETTING_PATH> <VALUE>` - Update a setting by path and value
123
- > - `SETTING_PATH` - Dot-separated setting path (e.g., `SPECIAL_RESOURCES.1974.opt_in`)
138
+ > - `config <SETTING_PATH> <VALUE>` - Update a setting by path and value.
139
+ > - `SETTING_PATH` - Dot-separated setting path (e.g., SPECIAL_RESOURCES.1974.opt_in)
124
140
  > - `VALUE` - New value for the setting
125
- > - `--server` / `-s` - Server to apply the change in (`LIVE`, `TEST`, `EMU`)
126
- > - `status` - Show the configuration for the current or specified server
141
+ > - `--server` / `-s` - Server to apply the change in (LIVE, TEST, EMU)
142
+ > - `server <SERVER>` - Switch the current server/environment to LIVE, TEST, or EMU.
143
+ > - `SERVER` - Server to use: LIVE, TEST, EMU
144
+ > - `status` - Show the configuration for the current or specified server.
127
145
  > - `--server` / `-s` - Server to show (defaults to current)
128
- >
129
- > ### 🔧 System & Utilities
130
- > - `ui` - Launch the Terminal User Interface
131
- > - `web` - Launch the RedGuides.com web interface
132
- > - `version` - Show version and exit
133
- > - `logout` - Disconnect your account from redfetch
134
- > - `uninstall` - Uninstall redfetch and clean up data
135
- >
146
+ >
136
147
  > ### 📤 Publishing
137
- > - `publish <resource_id>` - Publish an update to you or your team's resource. [There's also a github action for this.](https://github.com/marketplace/actions/redguides-publish)
138
- > - `resource_id` - Existing RedGuides resource ID
139
- > - `--description <README.md>` / `-d` - Path to a file (e.g. `README.md`) that will become the resource's overview description
140
- > - `--version <version_number>` / `-v` - New version string (e.g., `v1.0.1`)
141
- > - `--message <CHANGELOG.md | MESSAGE>` / `-m` - Version update message, a message file (e.g. `message.md` / `message.txt`), or a `CHANGELOG.md` (keep a changelog) file.
148
+ > - `publish <RESOURCE_ID>` - Publish updates to a **RedGuides** resource.
149
+ > - `RESOURCE_ID` - Existing RedGuides resource ID
150
+ > - `--description <README.md>` / `-d` - Path to a description file (e.g. README.md) to become the overview description.
151
+ > - `--version` / `-v` - New version string (e.g., v1.0.1)
152
+ > - `--message <CHANGELOG.md | MESSAGE>` / `-m` - Path to *CHANGELOG.md* (keep a changelog), other message file, or a direct message string.
142
153
  > - `--file <FILE.zip>` / `-f` - Path to your zipped release file
143
- > - `--domain <URL>` - Domain to prepend to relative URLs in README.md or CHANGELOG.md files. (mostly for images. e.g., `https://raw.githubusercontent.com/yourusername/yourrepo/main/`)
154
+ > - `--domain` - If description or message is a .md file with relative URLs, resolve them to this domain (e.g., https://raw.githubusercontent.com/your/repo/main/)
155
+ <!-- END GENERATED CLI REFERENCE -->
156
+
157
+ The `publish` command also has a [GitHub Action](https://github.com/marketplace/actions/redguides-publish).
144
158
 
145
159
  ## Settings
146
160
 
@@ -163,7 +177,7 @@ opt_in = true
163
177
  ```
164
178
  * Note the use of single quotes around the path, which are required for windows paths.
165
179
 
166
- The above will install Brewall's maps to the EQ maps directory the next time `--download-watched` is run for `LIVE` servers.
180
+ The above will install Brewall's maps to the EQ maps directory the next time `redfetch update` is run for `LIVE` servers.
167
181
 
168
182
  ### Overwrite protection
169
183
 
@@ -220,14 +234,6 @@ command = ["C:\\Users\\Public\\Python\\python.exe", "C:\\Users\\Public\\redfetch
220
234
 
221
235
  You can set these per-server, e.g. `[TEST.POST_UPDATE_LAUNCH]`, or global `[DEFAULT.POST_UPDATE_LAUNCH]`.
222
236
 
223
-
224
- ## Trailmap
225
- - Add custom buttons for "fetch" tab.
226
- - Option: Close after update
227
- - Indicate when updated VV is available
228
- - Run from MQ
229
- - Deeper integration with the forums
230
-
231
237
  ![Watchers on RedGuides](https://www.redguides.com/community/resources/redfetch.3177/watchers-sparkline?months=12&w=500&h=180)
232
238
 
233
239
  ## Contributing
@@ -61,42 +61,56 @@ To run redfetch from the command line:
61
61
 
62
62
  ## Command Line Reference
63
63
 
64
- > Run `redfetch --help` or `.\redfetch.exe --help` to see something like this in your terminal:
65
- >
64
+ <!-- BEGIN GENERATED CLI REFERENCE -->
65
+ > Run `redfetch --help` for the current list, or `redfetch <COMMAND> --help` for a command's options. It looks like:
66
+ >
66
67
  > ### 📦 Resource Management
67
- > - `update` - Update all watched and special resources
68
- > - `--force` / `-f` - Force re-download of all watched resources
69
- > - `--server` / `-s` - Switch to this server before updating (`LIVE`, `TEST`, `EMU`)
70
- > - `download <ID_OR_URL>` - Download a specific resource by ID or URL
71
- > - `--force` / `-f` - Force re-download by resetting this resource's download date
72
- > - `--server` / `-s` - Switch to this server before downloading (`LIVE`, `TEST`, `EMU`)
73
- > - `list` - List resources and dependencies in the cache database
74
- > - `reset` - Reset download dates for watched resources
75
- >
68
+ > - `update` - Update all *watched* and special resources.
69
+ > - `--force` / `-f` - Force re-download of all watched resources.
70
+ > - `--server` / `-s` - Update this server for this run only, without changing your current server (LIVE, TEST, EMU).
71
+ > - `download <ID_OR_URL>` - Download a specific resource by ID or URL.
72
+ > - `ID_OR_URL` - RedGuides resource ID or URL
73
+ > - `--force` / `-f` - Force re-download by resetting this resource's download date.
74
+ > - `--server` / `-s` - Download from this server for this run only, without changing your current server (LIVE, TEST, EMU).
75
+ > - `check` - Non-interactive update check (for automation.)
76
+ > - `--server` / `-s` - Check this server's env for this run only, without persisting it (LIVE, TEST, EMU).
77
+ > - `list` - List resources and dependencies in your local cache.
78
+ > - `reset` - Reset download dates for *watched resources* in the database.
79
+ >
80
+ > ### 🔧 System & Utilities
81
+ > - `ui` - Launch the *Terminal User Interface*.
82
+ > - `run [SHORTCUT]` - Run a shortcut (e.g. **vvmq**, **eqbcs**, **myseq**). **run** by itself will show a full list.
83
+ > - `SHORTCUT` - Shortcut to run: vvmq, eqbcs, eq, eqgame, etc.
84
+ > - `--server` / `-s` - Run for this server this run only, without changing your current server (LIVE, TEST, EMU).
85
+ > - `open [SHORTCUT]` - Open a folder or file (e.g. **downloads**, **eqhost**). **open** by itself will show a full list.
86
+ > - `SHORTCUT` - Folder/file to open: downloads, vvmq, eq, etc.
87
+ > - `--server` / `-s` - Resolve paths for this server this run only, without changing your current server (LIVE, TEST, EMU).
88
+ > - `web` - Launch the **RedGuides.com** web interface.
89
+ > - `version` - Show version and exit.
90
+ > - `uninstall` - Uninstall **redfetch** and clean up data.
91
+ > - `logout` - Log out and clear cached token and API cache.
92
+ >
76
93
  > ### 🍔 Configuration
77
- > - `server <SERVER>` - Switch the current server/environment to `LIVE`, `TEST`, or `EMU`
78
- > - `config <SETTING_PATH> <VALUE>` - Update a setting by path and value
79
- > - `SETTING_PATH` - Dot-separated setting path (e.g., `SPECIAL_RESOURCES.1974.opt_in`)
94
+ > - `config <SETTING_PATH> <VALUE>` - Update a setting by path and value.
95
+ > - `SETTING_PATH` - Dot-separated setting path (e.g., SPECIAL_RESOURCES.1974.opt_in)
80
96
  > - `VALUE` - New value for the setting
81
- > - `--server` / `-s` - Server to apply the change in (`LIVE`, `TEST`, `EMU`)
82
- > - `status` - Show the configuration for the current or specified server
97
+ > - `--server` / `-s` - Server to apply the change in (LIVE, TEST, EMU)
98
+ > - `server <SERVER>` - Switch the current server/environment to LIVE, TEST, or EMU.
99
+ > - `SERVER` - Server to use: LIVE, TEST, EMU
100
+ > - `status` - Show the configuration for the current or specified server.
83
101
  > - `--server` / `-s` - Server to show (defaults to current)
84
- >
85
- > ### 🔧 System & Utilities
86
- > - `ui` - Launch the Terminal User Interface
87
- > - `web` - Launch the RedGuides.com web interface
88
- > - `version` - Show version and exit
89
- > - `logout` - Disconnect your account from redfetch
90
- > - `uninstall` - Uninstall redfetch and clean up data
91
- >
102
+ >
92
103
  > ### 📤 Publishing
93
- > - `publish <resource_id>` - Publish an update to you or your team's resource. [There's also a github action for this.](https://github.com/marketplace/actions/redguides-publish)
94
- > - `resource_id` - Existing RedGuides resource ID
95
- > - `--description <README.md>` / `-d` - Path to a file (e.g. `README.md`) that will become the resource's overview description
96
- > - `--version <version_number>` / `-v` - New version string (e.g., `v1.0.1`)
97
- > - `--message <CHANGELOG.md | MESSAGE>` / `-m` - Version update message, a message file (e.g. `message.md` / `message.txt`), or a `CHANGELOG.md` (keep a changelog) file.
104
+ > - `publish <RESOURCE_ID>` - Publish updates to a **RedGuides** resource.
105
+ > - `RESOURCE_ID` - Existing RedGuides resource ID
106
+ > - `--description <README.md>` / `-d` - Path to a description file (e.g. README.md) to become the overview description.
107
+ > - `--version` / `-v` - New version string (e.g., v1.0.1)
108
+ > - `--message <CHANGELOG.md | MESSAGE>` / `-m` - Path to *CHANGELOG.md* (keep a changelog), other message file, or a direct message string.
98
109
  > - `--file <FILE.zip>` / `-f` - Path to your zipped release file
99
- > - `--domain <URL>` - Domain to prepend to relative URLs in README.md or CHANGELOG.md files. (mostly for images. e.g., `https://raw.githubusercontent.com/yourusername/yourrepo/main/`)
110
+ > - `--domain` - If description or message is a .md file with relative URLs, resolve them to this domain (e.g., https://raw.githubusercontent.com/your/repo/main/)
111
+ <!-- END GENERATED CLI REFERENCE -->
112
+
113
+ The `publish` command also has a [GitHub Action](https://github.com/marketplace/actions/redguides-publish).
100
114
 
101
115
  ## Settings
102
116
 
@@ -119,7 +133,7 @@ opt_in = true
119
133
  ```
120
134
  * Note the use of single quotes around the path, which are required for windows paths.
121
135
 
122
- The above will install Brewall's maps to the EQ maps directory the next time `--download-watched` is run for `LIVE` servers.
136
+ The above will install Brewall's maps to the EQ maps directory the next time `redfetch update` is run for `LIVE` servers.
123
137
 
124
138
  ### Overwrite protection
125
139
 
@@ -176,14 +190,6 @@ command = ["C:\\Users\\Public\\Python\\python.exe", "C:\\Users\\Public\\redfetch
176
190
 
177
191
  You can set these per-server, e.g. `[TEST.POST_UPDATE_LAUNCH]`, or global `[DEFAULT.POST_UPDATE_LAUNCH]`.
178
192
 
179
-
180
- ## Trailmap
181
- - Add custom buttons for "fetch" tab.
182
- - Option: Close after update
183
- - Indicate when updated VV is available
184
- - Run from MQ
185
- - Deeper integration with the forums
186
-
187
193
  ![Watchers on RedGuides](https://www.redguides.com/community/resources/redfetch.3177/watchers-sparkline?months=12&w=500&h=180)
188
194
 
189
195
  ## Contributing
@@ -21,9 +21,10 @@ dependencies = [
21
21
  "aiofiles~=25.1",
22
22
  "aiosqlite~=0.22",
23
23
  "aiohttp~=3.14",
24
- "cachetools~=7.1",
25
24
  "diskcache~=5.6",
26
- "dynaconf~=3.2,<3.3", # 3.3 has a bug
25
+ "dynaconf~=3.3,>=3.3.2", # (pr 1409 fixed RecursionError)
26
+ "filelock~=3.29",
27
+ "hishel[httpx,async]~=1.3",
27
28
  "httpx[http2]~=0.28",
28
29
  "keepachangelog~=2.0",
29
30
  "keyring~=25.7",
@@ -32,7 +33,6 @@ dependencies = [
32
33
  "pydantic~=2.13",
33
34
  "platformdirs~=4.10",
34
35
  "psutil~=7.2",
35
- "pyperclip~=1.11",
36
36
  "rich~=15.0",
37
37
  "tenacity~=9.1",
38
38
  "textual~=8.2",
@@ -78,3 +78,9 @@ dependencies = [
78
78
  "pip-tools",
79
79
  "pip-check"
80
80
  ]
81
+
82
+ [tool.hatch.envs.dev.scripts]
83
+ # Regenerate the README 'Command Line Reference' from the typer app.
84
+ gen-docs = "python tests/cli_reference.py --write"
85
+ # Verify it's current
86
+ check-docs = "python tests/cli_reference.py --check"
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '1.3.0'
22
- __version_tuple__ = version_tuple = (1, 3, 0)
21
+ __version__ = version = '1.4.1'
22
+ __version_tuple__ = version_tuple = (1, 4, 1)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -0,0 +1,50 @@
1
+ """Resource API client: fetch_*() takes a client, get_*() is self-contained."""
2
+
3
+ import httpx
4
+ from redfetch import net
5
+ from redfetch.sync_types import SyncInfo
6
+
7
+ BASE_URL = net.BASE_URL
8
+
9
+
10
+ async def fetch_sync_info(client: httpx.AsyncClient) -> SyncInfo:
11
+ """Per-user sync info"""
12
+ url = f'{BASE_URL}/api/rgsync'
13
+ is_level_2 = is_moderator = False
14
+ watched: set[str] = set()
15
+ licenses: list[dict] = []
16
+ page = 1
17
+ while True:
18
+ data = await net.get_json(client, url, params={'page': page})
19
+ is_level_2 = bool(data.get('is_level_2', False))
20
+ is_moderator = bool(data.get('is_moderator', False))
21
+ watched.update(str(rid) for rid in data.get('watched', []))
22
+ licenses.extend(data.get('licenses', []))
23
+ if not data.get('pagination', {}).get('has_more', False):
24
+ break
25
+ page += 1
26
+
27
+ licensed_ids = {
28
+ str(lic['resource_id']) for lic in licenses if lic.get('resource_id') is not None
29
+ }
30
+ return SyncInfo(
31
+ is_level_2=is_level_2,
32
+ is_moderator=is_moderator,
33
+ watched=watched,
34
+ licensed_ids=licensed_ids,
35
+ licenses=licenses,
36
+ )
37
+
38
+
39
+ async def get_sync_info(headers: dict) -> SyncInfo:
40
+ """fetch_sync_info for callers that hold headers but not a client."""
41
+ async with httpx.AsyncClient(headers=headers, http2=True, timeout=30.0) as client:
42
+ return await fetch_sync_info(client)
43
+
44
+
45
+ async def get_resource_details(resource_id: int, headers: dict) -> dict:
46
+ """Retrieve details of a specific resource from the API."""
47
+ url = f'{BASE_URL}/api/resources/{resource_id}'
48
+ async with httpx.AsyncClient(headers=headers, http2=True, timeout=30.0) as client:
49
+ data = await net.get_json(client, url)
50
+ return data['resource']
@@ -8,6 +8,7 @@ import shutil
8
8
  # third-party
9
9
  import tomlkit
10
10
  from dynaconf import Dynaconf, Validator, ValidationError
11
+ from dynaconf.loaders import env_loader
11
12
  from platformdirs import user_config_dir, user_data_dir
12
13
 
13
14
  # Parent Category to folder
@@ -162,12 +163,40 @@ def initialize_config():
162
163
  ]
163
164
  )
164
165
 
166
+ self_heal_eqpath()
165
167
  write_breadcrumb()
166
168
 
167
169
  # Return the settings object for potential use
168
170
  return settings
169
171
 
170
172
 
173
+ def self_heal_eqpath() -> None:
174
+ """Fill a blank or broken EQ PATH from autologin's login.db, per env."""
175
+ from redfetch import utils, detecteq
176
+
177
+ for env in ("LIVE", "TEST", "EMU"):
178
+ try:
179
+ env_settings = settings.from_env(env)
180
+ stored = env_settings.get("EQPATH")
181
+ if stored and detecteq._is_valid_eq_dir(stored):
182
+ continue # heal only when blank or broken
183
+
184
+ vvmq_id = utils.get_current_vvmq_id(env)
185
+ if not vvmq_id:
186
+ continue
187
+ vvmq = utils.resolve_special_destination(
188
+ env_settings.SPECIAL_RESOURCES.get(vvmq_id), env_settings.DOWNLOAD_FOLDER
189
+ )
190
+ if not vvmq:
191
+ continue
192
+
193
+ detected = detecteq.read_autologin_eq_path(os.path.join(vvmq, "config"), env)
194
+ if detected and detected != stored:
195
+ update_setting(["EQPATH"], detected, env=env)
196
+ except Exception:
197
+ continue # we're not stopping for this
198
+
199
+
171
200
  def _resolve_redfetch_executable():
172
201
  """PYAPP will give a path when built with PYAPP_PASS_LOCATION=1"""
173
202
  pyapp = os.environ.get("PYAPP")
@@ -459,25 +488,8 @@ def update_setting(setting_path, setting_value, env=None):
459
488
 
460
489
 
461
490
  def write_env_to_file(new_env):
462
- """Update the environment setting in the .env file."""
491
+ """Persist the selected environment to the .env file; dynaconf reads it back via env_switcher."""
463
492
  if env_file_path is None:
464
493
  raise RuntimeError("Configuration has not been initialized. Call initialize_config() first.")
465
494
 
466
- # Read the existing content of the .env file
467
- with open(env_file_path, 'r') as file:
468
- lines = file.readlines()
469
-
470
- # Update the environment line
471
- updated = False
472
- for i, line in enumerate(lines):
473
- if line.startswith('REDFETCH_ENV='):
474
- lines[i] = f'REDFETCH_ENV={new_env}\n'
475
- updated = True
476
- break
477
-
478
- # If the environment line was not found, add it
479
- if not updated:
480
- lines.append(f'REDFETCH_ENV={new_env}\n')
481
-
482
- # Write the updated content back to the .env file
483
- atomic_write_text(env_file_path, ''.join(lines))
495
+ env_loader.write(env_file_path, {"REDFETCH_ENV": new_env})
@@ -1,5 +1,7 @@
1
1
  import os
2
+ import sqlite3
2
3
  import sys
4
+ from pathlib import Path
3
5
 
4
6
  # Only import winreg on Windows
5
7
  if sys.platform == 'win32':
@@ -112,5 +114,29 @@ def find_everquest_uninstall_location():
112
114
  return None
113
115
 
114
116
 
117
+ def read_autologin_eq_path(mq_config_dir: str | None, server_type: str) -> str | None:
118
+ """from autologin's login.db."""
119
+ if not mq_config_dir:
120
+ return None
121
+ db_path = os.path.join(mq_config_dir, "login.db")
122
+ if not os.path.isfile(db_path):
123
+ return None # autologin maybe hasn't run
124
+
125
+ con = None
126
+ try:
127
+ con = sqlite3.connect(Path(db_path).as_uri() + "?mode=ro", uri=True)
128
+ row = con.execute(
129
+ "SELECT eq_path FROM server_types WHERE type = LOWER(?)", (server_type,)
130
+ ).fetchone()
131
+ except (sqlite3.Error, ValueError):
132
+ return None # locked or something
133
+ finally:
134
+ if con is not None:
135
+ con.close()
136
+
137
+ candidate = row[0] if row else None
138
+ return os.path.normpath(candidate) if _is_valid_eq_dir(candidate) else None
139
+
140
+
115
141
  if __name__ == "__main__":
116
142
  print(find_everquest_uninstall_location() or "")