nexmod 0.3.0__py3-none-any.whl
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.
- nexmod-0.3.0.dist-info/METADATA +443 -0
- nexmod-0.3.0.dist-info/RECORD +7 -0
- nexmod-0.3.0.dist-info/WHEEL +5 -0
- nexmod-0.3.0.dist-info/entry_points.txt +2 -0
- nexmod-0.3.0.dist-info/licenses/LICENSE +21 -0
- nexmod-0.3.0.dist-info/top_level.txt +1 -0
- nexmod.py +4111 -0
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nexmod
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Linux-native CLI mod manager for Nexus Mods (requires Premium)
|
|
5
|
+
License: MIT
|
|
6
|
+
Keywords: nexusmods,mods,gaming,linux,cli
|
|
7
|
+
Classifier: Development Status :: 4 - Beta
|
|
8
|
+
Classifier: Environment :: Console
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Topic :: Games/Entertainment
|
|
16
|
+
Classifier: Topic :: Utilities
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: click>=8.1
|
|
21
|
+
Requires-Dist: requests>=2.31
|
|
22
|
+
Requires-Dist: rich>=13.7
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: pytest>=7.4; extra == "dev"
|
|
25
|
+
Requires-Dist: pytest-mock>=3.12; extra == "dev"
|
|
26
|
+
Requires-Dist: responses>=0.24; extra == "dev"
|
|
27
|
+
Dynamic: license-file
|
|
28
|
+
|
|
29
|
+
# nexmod
|
|
30
|
+
|
|
31
|
+
[](https://github.com/morecitricacid-coder/nexmod/actions/workflows/ci.yml)
|
|
32
|
+
|
|
33
|
+
A Linux-native CLI mod manager for [Nexus Mods](https://www.nexusmods.com/).
|
|
34
|
+
|
|
35
|
+
> **Requires a Nexus Mods Premium account** — the download API is Premium-only.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Features
|
|
40
|
+
|
|
41
|
+
- Install / track / update mods by **mod ID *or* full Nexus URL**
|
|
42
|
+
- **`nxm://` link handler** — register once, then click "Mod Manager Download" on any Nexus mod page
|
|
43
|
+
- **Rollback** — every install/update is snapshotted; one command restores the previous version
|
|
44
|
+
- Named **profiles** for hotswapping load orders (e.g. `minimal` ↔ `full`)
|
|
45
|
+
- Dependency-aware load-order sorting (reads `mod.json` and `<folder>.mod`)
|
|
46
|
+
- **Robust `mod_load_order.txt` management** — orphan detection, foreign-entry preservation, framework auto-pinning, atomic writes with backup, external-edit drift detection, in-file `nexmod:freeze` directive
|
|
47
|
+
- **Pin folders** to top/bottom or relative to other folders — pins persist in the DB and survive every reconcile
|
|
48
|
+
- **Missing-dependency detection** — every `nexmod update` scans the inventory; `--fix-deps` installs the gaps interactively
|
|
49
|
+
- **Conflict detection** — peeks archives before extracting; warns when a mod would overwrite another tracked mod's folder
|
|
50
|
+
- **Network resilience** — Range-resume on download interruption, retries with exponential backoff, honors `Retry-After` on 429, falls back across CDN mirrors
|
|
51
|
+
- **Pre-flight `nexmod doctor`** — verifies API + Premium + Steam + Wine + 7z + disk + DB before you install anything
|
|
52
|
+
- **`nexmod fsck`** — audits and repairs DB drift (legacy rows missing `folder_name`, version mismatches)
|
|
53
|
+
- Vortex import — reads `vortex.deployment.json`, no re-downloads
|
|
54
|
+
- Steam (native + Flatpak) and Wine/Proton paths auto-detected
|
|
55
|
+
- Atomic downloads with MD5 verification before extraction
|
|
56
|
+
- `.zip`, `.tar.gz`, `.tar.bz2`, `.tar.xz`, `.7z` archives supported
|
|
57
|
+
- Disk-space pre-check before download/extract
|
|
58
|
+
- Path-traversal protection (rejects `../` and absolute paths in archives)
|
|
59
|
+
- Darktide: enable/disable mod loading via dtkit-patch through Wine
|
|
60
|
+
- `diag` surfaces mod errors from the game's own log
|
|
61
|
+
- Full operation history in a local SQLite DB; no telemetry, no daemons
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Supported Games
|
|
66
|
+
|
|
67
|
+
| Game | Slug | Load order | Mod-loader patch |
|
|
68
|
+
|------|------|:---:|:---:|
|
|
69
|
+
| Warhammer 40,000: Darktide | `darktide` | ✓ | ✓ (dtkit) |
|
|
70
|
+
| Skyrim Special Edition | `skyrimse` | — | — |
|
|
71
|
+
| Baldur's Gate 3 | `bg3` | — | — |
|
|
72
|
+
| Cyberpunk 2077 | `cyberpunk2077` | — | — |
|
|
73
|
+
| Fallout 4 | `fallout4` | — | — |
|
|
74
|
+
|
|
75
|
+
Unknown games still work for download/extract — pass any Nexus domain and set the mod dir manually with `nexmod path set`.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Installation
|
|
80
|
+
|
|
81
|
+
### Recommended: pipx
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
pipx install nexmod
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### pip (user install)
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
pip install --user nexmod
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### From source
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
git clone https://github.com/morecitricacid-coder/nexmod
|
|
97
|
+
cd nexmod
|
|
98
|
+
pip install -e . --user
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Shell script (no pip)
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
bash install.sh
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Quick Start
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
# 1. Get your API key: nexusmods.com → avatar → Settings → API Keys
|
|
113
|
+
nexmod config set-key <your-key>
|
|
114
|
+
|
|
115
|
+
# 2. Verify everything's wired up (API, Premium, Steam, Wine, 7z, disk, ...)
|
|
116
|
+
nexmod doctor
|
|
117
|
+
|
|
118
|
+
# 3. (optional) Register the nxm:// handler so the "Mod Manager Download"
|
|
119
|
+
# button on Nexus mod pages launches nexmod automatically.
|
|
120
|
+
nexmod nxm-register
|
|
121
|
+
|
|
122
|
+
# 4a. Install by URL (any Nexus mod page works)
|
|
123
|
+
nexmod install https://www.nexusmods.com/warhammer40kdarktide/mods/1234
|
|
124
|
+
|
|
125
|
+
# 4b. Or by slug + ID
|
|
126
|
+
nexmod install darktide 1234
|
|
127
|
+
|
|
128
|
+
# 5. List, check, update
|
|
129
|
+
nexmod list darktide
|
|
130
|
+
nexmod check darktide
|
|
131
|
+
nexmod update darktide -y
|
|
132
|
+
|
|
133
|
+
# 6. If a new version breaks the mod, roll back to the previous snapshot.
|
|
134
|
+
nexmod rollback darktide 1234
|
|
135
|
+
|
|
136
|
+
# 7. Snapshot the current load order, then hotswap later
|
|
137
|
+
nexmod profile save darktide minimal -d "QoL only"
|
|
138
|
+
nexmod profile save darktide full
|
|
139
|
+
nexmod profile load darktide minimal # apply minimal
|
|
140
|
+
nexmod profile load darktide full # back to full
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Command Reference
|
|
146
|
+
|
|
147
|
+
### Configuration
|
|
148
|
+
|
|
149
|
+
| Command | Description |
|
|
150
|
+
|---------|-------------|
|
|
151
|
+
| `nexmod config set-key <KEY>` | Save your Nexus API key (chmod 600) |
|
|
152
|
+
| `nexmod config show` | Show current configuration (key masked) |
|
|
153
|
+
| `nexmod config verify` | Hit the API to confirm key + Premium |
|
|
154
|
+
| `nexmod config auto-reorder on\|off` | Toggle automatic load-order sort after install/update |
|
|
155
|
+
|
|
156
|
+
### Installing & tracking
|
|
157
|
+
|
|
158
|
+
| Command | Flags | Description |
|
|
159
|
+
|---------|-------|-------------|
|
|
160
|
+
| `nexmod install <game> <mod_id>` | `--file-id N`, `--no-reorder`, `--dry-run` | Download, extract, track. Accepts a Nexus URL in place of `<game> <mod_id>`. `--dry-run` resolves metadata + prints what would happen without fetching. Conflict detection: if the archive's top-level folder is already claimed by another tracked mod, you'll be prompted before overwriting. |
|
|
161
|
+
| `nexmod install <nexus-url>` | (same) | URL form — game and mod ID parsed from the URL. |
|
|
162
|
+
| `nexmod track <game> <mod_id>` | — | Record an already-installed mod for update tracking. URL form supported. |
|
|
163
|
+
| `nexmod scan <game>` | `--dry-run` | Import all mods from `vortex.deployment.json`. |
|
|
164
|
+
| `nexmod remove <game> <mod_id>` | `--purge`, `--yes`, `--dry-run`, `--force-legacy-purge` | Untrack. `--purge` also deletes the mod folder (with confirmation; `--yes` skips). Rows missing `folder_name` (legacy installs) refuse to purge unless `--force-legacy-purge` is set — run `nexmod fsck --fix` first. |
|
|
165
|
+
|
|
166
|
+
### Updates
|
|
167
|
+
|
|
168
|
+
| Command | Flags | Description |
|
|
169
|
+
|---------|-------|-------------|
|
|
170
|
+
| `nexmod check <game>` | — | Show installed-vs-latest for every tracked mod (no download). |
|
|
171
|
+
| `nexmod update <game>` | `--mod-id N`, `-y/--yes`, `--no-reorder`, `--fix-deps` | Download and apply available updates, then scan for missing deps. Reports gaps by default; `--fix-deps` prompts to install each. The dep scan runs **even when nothing was updated** so silent rot is impossible. |
|
|
172
|
+
|
|
173
|
+
### Listing & inspection
|
|
174
|
+
|
|
175
|
+
| Command | Flags | Description |
|
|
176
|
+
|---------|-------|-------------|
|
|
177
|
+
| `nexmod list <game>` | `--json` | Tracked mods. `--json` emits machine-readable rows. |
|
|
178
|
+
| `nexmod info <game> <mod_id>` | — | Local DB row + one Nexus call: author, version, deps, install date. |
|
|
179
|
+
| `nexmod history [game]` | `--limit N`, `--failures` | Operation history. |
|
|
180
|
+
| `nexmod logs` | `--lines N`, `--errors`, `--follow` | Tail nexmod's own log. |
|
|
181
|
+
| `nexmod games` | — | List built-in game slugs. |
|
|
182
|
+
|
|
183
|
+
### Profiles (per-game named load-order snapshots)
|
|
184
|
+
|
|
185
|
+
| Command | Flags | Description |
|
|
186
|
+
|---------|-------|-------------|
|
|
187
|
+
| `nexmod profile save <game> <name>` | `-d/--description`, `-f/--force` | Snapshot the current load order. |
|
|
188
|
+
| `nexmod profile list <game>` | — | Show all saved profiles. |
|
|
189
|
+
| `nexmod profile show <game> <name>` | — | Print a profile's full mod list (flags missing-from-disk). |
|
|
190
|
+
| `nexmod profile load <game> <name>` | `--dry-run`, `--install`, `--strict` | Apply a profile. `--install` installs profile mods missing from disk via their tracked mod_id. `--strict` also drops foreign (untracked) entries — default preserves them. |
|
|
191
|
+
| `nexmod profile delete <game> <name>` | `-f/--force` | Delete a profile. |
|
|
192
|
+
| `nexmod profile rename <game> <old> <new>` | — | Rename a profile. |
|
|
193
|
+
|
|
194
|
+
### Load order
|
|
195
|
+
|
|
196
|
+
| Command | Flags | Description |
|
|
197
|
+
|---------|-------|-------------|
|
|
198
|
+
| `nexmod order <game>` | `--dry-run`, `--check`, `--fsck`, `--freeze`, `--unfreeze`, `--adopt`, `--auto-merge` | Reconcile `mod_load_order.txt` against DB + disk + pins. |
|
|
199
|
+
| `nexmod pin <game> <folder> top\|bottom\|before\|after [other]` | — | Pin a folder to a load-order position. |
|
|
200
|
+
| `nexmod unpin <game> <folder>` | — | Remove a pin. |
|
|
201
|
+
| `nexmod pins <game>` | — | List active pins. |
|
|
202
|
+
|
|
203
|
+
**`order` flags:**
|
|
204
|
+
|
|
205
|
+
| Flag | What it does |
|
|
206
|
+
|------|--------------|
|
|
207
|
+
| `--check` | Print classification table + diff. No write. |
|
|
208
|
+
| `--dry-run` | Same, but quieter. |
|
|
209
|
+
| `--fsck` | Restore from `.bak` (kept by every reconcile). |
|
|
210
|
+
| `--freeze` | Insert `-- nexmod:freeze` directive. nexmod stops touching the file. |
|
|
211
|
+
| `--unfreeze` | Remove the freeze directive. |
|
|
212
|
+
| `--adopt` | Promote foreign (untracked) entries: prompts for a Nexus URL per entry. |
|
|
213
|
+
| `--auto-merge` | Force overwrite even if external drift is detected. |
|
|
214
|
+
|
|
215
|
+
### How the reconciler classifies entries
|
|
216
|
+
|
|
217
|
+
| Class | Meaning | Action |
|
|
218
|
+
|-------|---------|--------|
|
|
219
|
+
| `framework` | Foreign + matches game's framework set (e.g. `mod_compat`, `dmf`) | Pin to top |
|
|
220
|
+
| `managed-present` | Tracked in DB, folder on disk | Topo-sort by deps |
|
|
221
|
+
| `foreign` | Folder on disk, not tracked | Preserve verbatim |
|
|
222
|
+
| `managed-missing` | Tracked in DB, folder gone | Drop |
|
|
223
|
+
| `orphan` | Listed in file, no folder, no DB row | Drop |
|
|
224
|
+
|
|
225
|
+
### In-file directives
|
|
226
|
+
|
|
227
|
+
The reconciler parses these `--`-prefixed comment directives and re-emits them on every write:
|
|
228
|
+
|
|
229
|
+
```
|
|
230
|
+
-- nexmod:freeze # disables nexmod auto-mutation
|
|
231
|
+
-- nexmod:framework custom_framework_dir # adds to the framework set (auto-pinned to top)
|
|
232
|
+
-- nexmod:pin my_mod top # pin to top of file
|
|
233
|
+
-- nexmod:pin my_mod bottom # pin to bottom of file
|
|
234
|
+
-- nexmod:pin my_mod before mod_compat # pin immediately before another folder
|
|
235
|
+
-- nexmod:pin my_mod after dmf # pin immediately after another folder
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Pins set via `nexmod pin` go into the DB; pins via directives go in the file. Both are honored.
|
|
239
|
+
|
|
240
|
+
### Rollback & snapshots
|
|
241
|
+
|
|
242
|
+
| Command | Flags | Description |
|
|
243
|
+
|---------|-------|-------------|
|
|
244
|
+
| `nexmod snapshots <game> [<mod_id>]` | `--prune` | List cached version snapshots. With `--prune`, force-trims each mod to the most-recent `SNAPSHOTS_PER_MOD` (default 3). |
|
|
245
|
+
| `nexmod rollback <game> <mod_id>` | `--version V`, `--list`, `--yes` | Restore a tracked mod from a cached snapshot. Defaults to the most-recent prior version; `--version` picks a specific one; `--list` shows what's available without restoring. |
|
|
246
|
+
|
|
247
|
+
Snapshots are written automatically after every successful install/update to
|
|
248
|
+
`~/.cache/nexmod/<game>/<mod_id>/<version>.<ext>` and capped to the most-recent
|
|
249
|
+
`SNAPSHOTS_PER_MOD` per mod (override via the `NEXMOD_SNAPSHOTS_PER_MOD` env var).
|
|
250
|
+
|
|
251
|
+
### NXM links
|
|
252
|
+
|
|
253
|
+
| Command | Description |
|
|
254
|
+
|---------|-------------|
|
|
255
|
+
| `nexmod nxm-register` | Write `~/.local/share/applications/nexmod-nxm.desktop` and register it via `xdg-mime` as the system handler for `nxm://` links. |
|
|
256
|
+
| `nexmod nxm-unregister` | Remove the registration. |
|
|
257
|
+
| `nexmod nxm <uri>` | Manually handle an `nxm://` URI. Usually invoked by the system handler after registration; safe to paste a URI yourself. |
|
|
258
|
+
|
|
259
|
+
After `nxm-register`, clicking "Mod Manager Download" on any Nexus mod page
|
|
260
|
+
launches nexmod and starts the install.
|
|
261
|
+
|
|
262
|
+
### Mod loader (Darktide)
|
|
263
|
+
|
|
264
|
+
| Command | Description |
|
|
265
|
+
|---------|-------------|
|
|
266
|
+
| `nexmod enable <game>` | Run dtkit-patch via Wine to enable mod loading. |
|
|
267
|
+
| `nexmod disable <game>` | Unpatch (disable mod loading). |
|
|
268
|
+
| `nexmod toggle <game>` | Flip patch state. |
|
|
269
|
+
|
|
270
|
+
### Paths & diagnostics
|
|
271
|
+
|
|
272
|
+
| Command | Flags | Description |
|
|
273
|
+
|---------|-------|-------------|
|
|
274
|
+
| `nexmod doctor` | `--game <slug>` | Pre-flight environment check: API key + Premium, Steam libraries, per-game install paths, Wine + 7z presence, disk space, DB integrity. Exits 1 if any required check fails; warnings are advisory. |
|
|
275
|
+
| `nexmod fsck [<game>]` | `--fix`, `--with-api` | Audit the local DB. Reports legacy rows missing `folder_name` and version. With `--fix`, auto-backfills folder names by 4-strategy inference (filename stem → mod.json name → .mod title → fuzzy match), refusing collisions. With `--with-api --fix`, also re-fetches missing version strings from Nexus. |
|
|
276
|
+
| `nexmod path set <game> <dir>` | — | Override auto-detected mod directory. |
|
|
277
|
+
| `nexmod path show <game>` | — | Print the resolved mod directory. |
|
|
278
|
+
| `nexmod diag <game>` | `--lines N`, `--all` | Surface mod errors from the game's own log file. |
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## Common Workflows
|
|
283
|
+
|
|
284
|
+
### Migrate from Vortex (Wine) without re-downloading
|
|
285
|
+
|
|
286
|
+
```bash
|
|
287
|
+
nexmod scan darktide # imports every mod from vortex.deployment.json
|
|
288
|
+
nexmod update darktide -y # ensures every entry is at the current Nexus version
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
`scan` stores `version=NULL` intentionally so the next `update` always re-fetches — Vortex-installed files may be older than what Nexus reports.
|
|
292
|
+
|
|
293
|
+
### Detect and repair missing dependencies
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
nexmod update darktide # scans + reports any missing deps
|
|
297
|
+
nexmod update darktide --fix-deps # same, but interactively installs each missing dep
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
Output when something's missing:
|
|
301
|
+
|
|
302
|
+
```
|
|
303
|
+
⚠ 5 mod(s) missing 6 dependency(ies):
|
|
304
|
+
red_weapons_at_home → modding_tools
|
|
305
|
+
hybrid_sprint → modding_tools, MultiBind, ToggleAltFire
|
|
306
|
+
Skitarius → Mark9
|
|
307
|
+
...
|
|
308
|
+
Re-run with --fix-deps to install them interactively.
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
The default is report-only because `--yes` automation contexts (cron, scripts) shouldn't block on a Nexus URL prompt.
|
|
312
|
+
|
|
313
|
+
### Recover from a bad update
|
|
314
|
+
|
|
315
|
+
```bash
|
|
316
|
+
nexmod update darktide -y # mod ID 1234 ships v1.2 — breaks something
|
|
317
|
+
nexmod snapshots darktide 1234 # see what versions are cached
|
|
318
|
+
nexmod rollback darktide 1234 # restore most recent prior version
|
|
319
|
+
# or pin a specific version:
|
|
320
|
+
nexmod rollback darktide 1234 --version 1.0
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Snapshots are saved automatically after every install/update; you don't need
|
|
324
|
+
to opt in. The cache caps to 3 versions per mod by default.
|
|
325
|
+
|
|
326
|
+
### Hotswap load orders
|
|
327
|
+
|
|
328
|
+
```bash
|
|
329
|
+
nexmod profile save darktide stable
|
|
330
|
+
# ...experiment, install some test mods...
|
|
331
|
+
nexmod profile save darktide testing
|
|
332
|
+
nexmod profile load darktide stable # snap back to known-good
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### Bring a profile to a new machine
|
|
336
|
+
|
|
337
|
+
```bash
|
|
338
|
+
# On new machine: profile JSON is at ~/.config/nexmod/profiles/<game>/<name>.json
|
|
339
|
+
# After copying it over and tracking the relevant mod IDs:
|
|
340
|
+
nexmod profile load darktide full --install
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
`--install` resolves each missing folder to a `mod_id` from the local DB and installs via the normal pipeline.
|
|
344
|
+
|
|
345
|
+
### Script tracked mods with jq
|
|
346
|
+
|
|
347
|
+
```bash
|
|
348
|
+
nexmod list darktide --json | jq '.[] | select(.version == null) | .mod_id'
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
The JSON shape is one object per `mods` row:
|
|
352
|
+
|
|
353
|
+
```json
|
|
354
|
+
[
|
|
355
|
+
{
|
|
356
|
+
"id": 1,
|
|
357
|
+
"game": "darktide",
|
|
358
|
+
"mod_id": 1234,
|
|
359
|
+
"file_id": 5678,
|
|
360
|
+
"name": "Some Mod",
|
|
361
|
+
"version": "1.2.3",
|
|
362
|
+
"filename": "SomeMod-1234-1-2-3.zip",
|
|
363
|
+
"mod_dir": "/path/to/Darktide/mods",
|
|
364
|
+
"tracked_at": "2026-04-25T...",
|
|
365
|
+
"updated_at": "2026-04-25T..."
|
|
366
|
+
}
|
|
367
|
+
]
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
---
|
|
371
|
+
|
|
372
|
+
## Configuration
|
|
373
|
+
|
|
374
|
+
| File | Location |
|
|
375
|
+
|------|----------|
|
|
376
|
+
| Config (API key, settings) | `~/.config/nexmod/config.json` (chmod 600) |
|
|
377
|
+
| Profiles | `~/.config/nexmod/profiles/<game>/<name>.json` |
|
|
378
|
+
| Database (SQLite, WAL) | `~/.local/share/nexmod/mods.db` |
|
|
379
|
+
| Log (rotating, 5 MB × 3) | `~/.local/share/nexmod/nexmod.log` |
|
|
380
|
+
| Wine prefix (dtkit) | `~/.local/share/nexmod/wine-prefix` |
|
|
381
|
+
| Snapshot cache (rollback) | `~/.cache/nexmod/<game>/<mod_id>/<version>.<ext>` |
|
|
382
|
+
| NXM handler (after `nxm-register`) | `~/.local/share/applications/nexmod-nxm.desktop` |
|
|
383
|
+
|
|
384
|
+
### Tunable env vars
|
|
385
|
+
|
|
386
|
+
| Variable | Default | Purpose |
|
|
387
|
+
|----------|---------|---------|
|
|
388
|
+
| `NEXMOD_API_RETRIES` | `3` | Total attempts for `nexus_get` requests |
|
|
389
|
+
| `NEXMOD_API_RETRY_DELAY` | `1.0` | Base delay (seconds) for exponential backoff |
|
|
390
|
+
| `NEXMOD_429_MAX_WAIT` | `60.0` | Cap on `Retry-After` honored on HTTP 429 |
|
|
391
|
+
| `NEXMOD_DOWNLOAD_RETRIES` | `3` | Total attempts for `download_file` (with Range-resume) |
|
|
392
|
+
| `NEXMOD_SNAPSHOTS_PER_MOD` | `3` | Max snapshots kept per mod in rollback cache |
|
|
393
|
+
| `XDG_CACHE_HOME` | `~/.cache` | Base for snapshot cache directory |
|
|
394
|
+
|
|
395
|
+
The SQLite DB is safe to read concurrently while nexmod is running — WAL is enabled. Schema:
|
|
396
|
+
|
|
397
|
+
- `mods(id, game, mod_id, file_id, name, version, filename, mod_dir, folder_name, tracked_at, updated_at)` — one row per tracked mod, unique on `(game, mod_id)`. `folder_name` is captured at install time and is required for reliable `remove --purge`; legacy rows backfill on the next install/update.
|
|
398
|
+
- `game_paths(game, path)` — manual mod-dir overrides
|
|
399
|
+
- `history(timestamp, action, game, mod_id, mod_name, version, status, detail)` — every install/update/scan/remove
|
|
400
|
+
- `load_order_state(game, file_path, last_hash, last_written_at, frozen)` — sha256 of the load order at last write; used to detect external edits.
|
|
401
|
+
- `load_order_pins(game, folder, position, relative_to, source, created_at)` — persistent pins set via `nexmod pin`.
|
|
402
|
+
|
|
403
|
+
---
|
|
404
|
+
|
|
405
|
+
## Exit Codes
|
|
406
|
+
|
|
407
|
+
| Code | Meaning |
|
|
408
|
+
|:---:|---------|
|
|
409
|
+
| `0` | Success (or non-fatal warning, e.g. account not Premium) |
|
|
410
|
+
| `1` | Hard failure: missing API key, API error, rate-limited, malformed URL, mod not tracked, profile not found, etc. |
|
|
411
|
+
|
|
412
|
+
`config verify` exits `1` only when the API call itself fails. A successful call against a non-Premium account still exits `0` (with a warning) — Premium is enforced at download time, not at validate time.
|
|
413
|
+
|
|
414
|
+
---
|
|
415
|
+
|
|
416
|
+
## For Automation / LLMs
|
|
417
|
+
|
|
418
|
+
- **Read-only / no-network commands:** `list`, `list --json`, `history`, `logs`, `path show`, `games`, `profile list`, `profile show`, `config show`, `snapshots`, `fsck` (without `--fix`), `rollback --list`. Safe to invoke without an API key.
|
|
419
|
+
- **Network / API key required:** `install`, `track`, `update`, `check`, `scan`, `info`, `config verify`, `doctor`, `nxm`, `fsck --with-api`.
|
|
420
|
+
- **Inspect state without parsing CLI output:** read `~/.local/share/nexmod/mods.db` directly. WAL mode means concurrent readers are fine.
|
|
421
|
+
- **Stable JSON contract:** `nexmod list <game> --json` (full DB row per mod). Other commands do not yet emit JSON — expect Rich-formatted tables.
|
|
422
|
+
- **Idempotency:** `track`, `scan`, `path set`, `fsck --fix`, `nxm-register` are safe to re-run. `install` re-extracts and bumps `updated_at`. `profile save` overwrites without prompt only with `-f`.
|
|
423
|
+
- **Non-interactive flags:** `update -y`, `remove --yes`, `rollback --yes` skip confirmations. `install --dry-run` and `remove --dry-run` make no changes. `nexmod doctor` exits 1 on any required-check failure (warnings still exit 0).
|
|
424
|
+
- **Determining freshness:** `version IS NULL` means scanned-but-not-yet-updated (Vortex import); a normal `update` will pull the current Nexus version.
|
|
425
|
+
- **Network resilience is automatic:** clients don't need to retry — `nexus_get` and `download_file` retry transient errors internally with exponential backoff and respect `Retry-After`. Tune via env vars (see Configuration).
|
|
426
|
+
|
|
427
|
+
---
|
|
428
|
+
|
|
429
|
+
## Requirements
|
|
430
|
+
|
|
431
|
+
- Linux (tested on Ubuntu 24.04+, Arch, Fedora 40+)
|
|
432
|
+
- Python 3.10+
|
|
433
|
+
- `7z` / `7zz` / `7za` for `.7z` archives:
|
|
434
|
+
- Debian/Ubuntu: `sudo apt install p7zip-full`
|
|
435
|
+
- Arch: `sudo pacman -S p7zip`
|
|
436
|
+
- Fedora: `sudo dnf install p7zip p7zip-plugins`
|
|
437
|
+
- Wine — only required for Darktide `enable` / `disable` / `toggle`.
|
|
438
|
+
|
|
439
|
+
---
|
|
440
|
+
|
|
441
|
+
## License
|
|
442
|
+
|
|
443
|
+
MIT
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
nexmod.py,sha256=XBzTh7HRHJn_upRWzk23fcGf1GmYacIkS0nCPAX6kTo,171083
|
|
2
|
+
nexmod-0.3.0.dist-info/licenses/LICENSE,sha256=JKAYFZsTesu1En2lToFeRZ1m7IhpPv1LQmcfVPDd5dY,1076
|
|
3
|
+
nexmod-0.3.0.dist-info/METADATA,sha256=53GsE-EImaJ0cJUm1zLkJbZzXFVpHVqXnLPUVQbIfZA,19886
|
|
4
|
+
nexmod-0.3.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
5
|
+
nexmod-0.3.0.dist-info/entry_points.txt,sha256=IjwTmdqbALxaFqfz4YjbExi9otpFjzpLboQ5S9dNTSw,38
|
|
6
|
+
nexmod-0.3.0.dist-info/top_level.txt,sha256=V9tCBEBIyivmMixcSQlRSrmlYL9pGEq394s9FAxqnHI,7
|
|
7
|
+
nexmod-0.3.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 nexmod contributors
|
|
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 @@
|
|
|
1
|
+
nexmod
|