wyvrnpm 2.1.0 → 2.4.1
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.
- package/README.md +325 -6
- package/bin/wyvrn.js +167 -14
- package/claude/skills/wyvrnpm.skill +0 -0
- package/cmake/macros.cmake +51 -0
- package/package.json +2 -1
- package/src/build/cache.js +47 -0
- package/src/build/index.js +42 -11
- package/src/build/recipe.js +30 -4
- package/src/commands/add.js +140 -0
- package/src/commands/build.js +96 -12
- package/src/commands/clean.js +39 -15
- package/src/commands/install-skill.js +107 -0
- package/src/commands/install.js +202 -5
- package/src/commands/publish.js +150 -35
- package/src/commands/show.js +237 -0
- package/src/compat.js +53 -12
- package/src/download.js +65 -3
- package/src/ignore.js +118 -0
- package/src/logger.js +26 -10
- package/src/manifest.js +12 -7
- package/src/options.js +303 -0
- package/src/profile.js +28 -4
- package/src/providers/base.js +16 -1
- package/src/providers/file.js +8 -3
- package/src/providers/http.js +12 -8
- package/src/providers/s3.js +11 -7
- package/src/resolve.js +171 -13
- package/src/toolchain/index.js +71 -0
- package/src/toolchain/presets.js +125 -16
- package/src/toolchain/template.cmake +11 -0
- package/src/upload-built.js +256 -0
- package/src/version-range.js +301 -0
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ A simple, private C++ package manager that works with any static file hosting pr
|
|
|
4
4
|
|
|
5
5
|
There is no central registry. You control where packages are hosted.
|
|
6
6
|
|
|
7
|
-
> **README version: 2.
|
|
7
|
+
> **README version: 2.4.0** — Highlights since 2.1.0: `wyvrnpm add <name>` for dependency authoring, `HeaderOnlyLib` packages that survive cross-arch `find_package()`, `WYVRN_ARCH_SUFFIX` + the `WYVRN_APPLY_ARCH_SUFFIX(<target>)` helper so x64/x86 artefacts coexist on disk, `wyvrnpm build --generator / -G` to swap CMake generators from the CLI, package `options`, version ranges (`^`, `~`, explicit comparators), `--format=json` output, and `--upload-built` to warm the registry with source-built artefacts.
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
@@ -37,16 +37,19 @@ wyvrnpm configure add-source --kind publish --name default --url s3://my-bucket
|
|
|
37
37
|
# 3. Detect and save your build environment as the default profile
|
|
38
38
|
wyvrnpm configure profile detect
|
|
39
39
|
|
|
40
|
-
# 4.
|
|
40
|
+
# 4. Add dependencies (resolves latest from your configured sources)
|
|
41
|
+
wyvrnpm add zlib fmt@^10.0.0
|
|
42
|
+
|
|
43
|
+
# 5. Install dependencies (resolved against your build profile)
|
|
41
44
|
wyvrnpm install
|
|
42
45
|
|
|
43
|
-
#
|
|
46
|
+
# 6. Configure and build via the generated CMake preset
|
|
44
47
|
wyvrnpm build
|
|
45
48
|
|
|
46
|
-
#
|
|
49
|
+
# 7. Publish your package (uses the active profile automatically)
|
|
47
50
|
wyvrnpm publish
|
|
48
51
|
|
|
49
|
-
#
|
|
52
|
+
# 8. (Optional) Link local packages for development
|
|
50
53
|
cd ../my-local-lib && wyvrnpm link
|
|
51
54
|
cd ../my-app && wyvrnpm link my-local-lib
|
|
52
55
|
```
|
|
@@ -109,6 +112,42 @@ wyvrnpm init --root ./my-project
|
|
|
109
112
|
|
|
110
113
|
---
|
|
111
114
|
|
|
115
|
+
### `wyvrnpm add`
|
|
116
|
+
|
|
117
|
+
Adds one or more dependencies to `wyvrn.json` without downloading them. Useful when you know the package name but not the latest version.
|
|
118
|
+
|
|
119
|
+
- **Bare name** — resolves `latest.json` from your configured install sources and writes `"^<latest>"` (caret range, major-pinned).
|
|
120
|
+
- **`name@<version>`** — writes the given string verbatim. Accepts exact pins (`1.2.3.4`), caret/tilde ranges (`^1.2.3.4`, `~1.2.3.4`), and explicit comparators (`>=1.80.0 <2.0.0`).
|
|
121
|
+
|
|
122
|
+
If the dependency is already listed, it is replaced. When the prior entry was an object (`{ version, settings, options }`), the `settings` and `options` are preserved — only the version field is updated.
|
|
123
|
+
|
|
124
|
+
`add` does **not** run install. Chain `wyvrnpm install` afterwards to download.
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
# Resolve latest, write "^<latest>"
|
|
128
|
+
wyvrnpm add zlib
|
|
129
|
+
|
|
130
|
+
# Pin exactly
|
|
131
|
+
wyvrnpm add zlib@1.3.0.0
|
|
132
|
+
|
|
133
|
+
# Use a caret / tilde / comparator range
|
|
134
|
+
wyvrnpm add fmt@^10.0.0
|
|
135
|
+
wyvrnpm add boost@">=1.80.0 <2.0.0"
|
|
136
|
+
|
|
137
|
+
# Multiple in one call
|
|
138
|
+
wyvrnpm add zlib fmt@10.2.0.0 spdlog
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**Options**
|
|
142
|
+
|
|
143
|
+
| Option | Default | Description |
|
|
144
|
+
|---|---|---|
|
|
145
|
+
| `--source` / `-s` | *(config)* | Base URL used for the `latest` lookup. Repeat for multiple. |
|
|
146
|
+
| `--platform` | `win_x64` | v1 legacy platform sub-path (used as a fallback during `latest` lookup). |
|
|
147
|
+
| `--manifest` | `./wyvrn.json` | Path to the manifest file. |
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
112
151
|
### `wyvrnpm install`
|
|
113
152
|
|
|
114
153
|
Resolves the full dependency graph and downloads all packages.
|
|
@@ -164,6 +203,25 @@ After download, `install` also generates:
|
|
|
164
203
|
- `wyvrn_internal/wyvrn_profile.json` — snapshot of the active build profile.
|
|
165
204
|
- `CMakePresets.json` (or `CMakeUserPresets.json` if the project root file is user-owned) with a `wyvrn-<profile>` configure preset. Presets for other profiles are preserved so multiple profiles coexist.
|
|
166
205
|
|
|
206
|
+
**Toolchain variables exposed to your `CMakeLists.txt` (valid BEFORE `project()`)**:
|
|
207
|
+
|
|
208
|
+
| Variable | Value |
|
|
209
|
+
|---|---|
|
|
210
|
+
| `WYVRN_PROFILE_NAME` / `WYVRN_PROFILE_HASH` | Active profile name + 16-char hash |
|
|
211
|
+
| `WYVRN_PROFILE_OS` / `WYVRN_PROFILE_ARCH` | OS + architecture from the profile |
|
|
212
|
+
| `WYVRN_PROFILE_COMPILER` / `WYVRN_PROFILE_COMPILER_VERSION` | Compiler identity |
|
|
213
|
+
| `WYVRN_PROFILE_CPPSTD` / `WYVRN_PROFILE_RUNTIME` | C++ standard + MSVC runtime linkage |
|
|
214
|
+
| `WYVRN_ARCH_SUFFIX` | `"64"` for 64-bit arches, `"32"` for 32-bit, empty for unmapped. Derived at toolchain-generation time so it's usable before `project()` — unlike `CMAKE_SIZEOF_VOID_P`. |
|
|
215
|
+
|
|
216
|
+
The optional helper `WYVRN_APPLY_ARCH_SUFFIX(<target> [BASE_NAME <name>])` from the bundled `macros.cmake` applies the suffix to a target's `OUTPUT_NAME` so x64 and x86 builds of the same library coexist on disk without overwriting each other. Target linkage (`target_link_libraries`) is unaffected — only the produced filename changes.
|
|
217
|
+
|
|
218
|
+
```cmake
|
|
219
|
+
project(String CXX)
|
|
220
|
+
add_library(String STATIC ...)
|
|
221
|
+
WYVRN_APPLY_ARCH_SUFFIX(String) # → String64.lib / String32.lib
|
|
222
|
+
WYVRN_APPLY_ARCH_SUFFIX(String BASE_NAME my-str) # → my-str64.lib / my-str32.lib
|
|
223
|
+
```
|
|
224
|
+
|
|
167
225
|
---
|
|
168
226
|
|
|
169
227
|
### `wyvrnpm build`
|
|
@@ -189,6 +247,10 @@ wyvrnpm build --target my-lib
|
|
|
189
247
|
# Wipe the build directory first
|
|
190
248
|
wyvrnpm build --clean
|
|
191
249
|
|
|
250
|
+
# Override the preset's generator (pair with --clean when switching generators)
|
|
251
|
+
wyvrnpm build --clean --generator "Visual Studio 17 2022"
|
|
252
|
+
wyvrnpm build --clean -G "Ninja Multi-Config"
|
|
253
|
+
|
|
192
254
|
# Build, then install to the CMake install prefix
|
|
193
255
|
wyvrnpm build --install
|
|
194
256
|
wyvrnpm build --install --install-prefix C:\out\stage
|
|
@@ -210,6 +272,7 @@ wyvrnpm build -- -j 8
|
|
|
210
272
|
| `--target` / `-t` | *(all)* | Specific CMake target to build. |
|
|
211
273
|
| `--verbose` / `-v` | `false` | Pass `--verbose` to `cmake --build`. |
|
|
212
274
|
| `--clean` | `false` | Remove the build directory before configuring. |
|
|
275
|
+
| `--generator` / `-G` | *(from preset)* | Override the CMake generator used at configure time (e.g. `"Visual Studio 17 2022"`, `"Ninja"`, `"Ninja Multi-Config"`). CMake bakes the generator into the cache, so pair with `--clean` when switching. When the effective generator is Visual Studio, `-A <platform>` is auto-derived from the profile's arch (`x86→Win32`, `x86_64→x64`, `armv8→ARM64`, `armv7→ARM`) so VS doesn't silently default to x64 on 32-bit profiles. |
|
|
213
276
|
| `--install` | `false` | Run `cmake --install` after a successful build. |
|
|
214
277
|
| `--install-prefix` | *(baked in)* | Override `CMAKE_INSTALL_PREFIX` for `--install` without reconfiguring. |
|
|
215
278
|
| `--auto-install` | `true` | Auto-run `wyvrnpm install` when the toolchain is stale. Disable with `--no-auto-install`. |
|
|
@@ -518,6 +581,101 @@ wyvrnpm configure profile delete msvc_release
|
|
|
518
581
|
|
|
519
582
|
---
|
|
520
583
|
|
|
584
|
+
## JSON output (`--format json`)
|
|
585
|
+
|
|
586
|
+
`install`, `publish`, and `show` accept `--format json` to emit a single machine-readable JSON object on stdout. Log lines (`[wyvrn]` prefix) move to **stderr** in this mode, so stdout is reserved exclusively for the JSON payload — no scraping required.
|
|
587
|
+
|
|
588
|
+
```bash
|
|
589
|
+
wyvrnpm install --format json > install-result.json
|
|
590
|
+
wyvrnpm publish --format json 2>/dev/null # swallow logs, keep JSON
|
|
591
|
+
wyvrnpm show zlib@1.3.0.0 --format json | jq '.versions["1.3.0.0"].profiles[].origin'
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
### Shape — `install`
|
|
595
|
+
|
|
596
|
+
```json
|
|
597
|
+
{
|
|
598
|
+
"command": "install",
|
|
599
|
+
"wyvrnpmVersion": "2.2.1",
|
|
600
|
+
"profile": { "os": "Windows", "arch": "x86_64", "compiler": "msvc", "compiler.version": "193", "compiler.cppstd": "23", "compiler.runtime": "dynamic" },
|
|
601
|
+
"profileName": "default",
|
|
602
|
+
"profileHash": "bf341c08af0aee2d",
|
|
603
|
+
"packages": [
|
|
604
|
+
{
|
|
605
|
+
"name": "zlib",
|
|
606
|
+
"version": "1.3.0.0",
|
|
607
|
+
"versionRange": "^1.3.0.0",
|
|
608
|
+
"profileHash": "a1b2c3d4e5f60718",
|
|
609
|
+
"options": { "shared": false, "minizip": true },
|
|
610
|
+
"contentSha256": "…",
|
|
611
|
+
"gitSha": "…",
|
|
612
|
+
"resolvedFrom": "v2"
|
|
613
|
+
}
|
|
614
|
+
],
|
|
615
|
+
"lockFile": "/abs/path/to/wyvrn.lock",
|
|
616
|
+
"uploadSummary": { "uploaded": 0, "skipped": 0, "failed": 0 }
|
|
617
|
+
}
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
- `versionRange` is `null` when the user pinned an exact version.
|
|
621
|
+
- `options` is `null` for packages that declare no options.
|
|
622
|
+
- `uploadSummary` is `null` unless `--upload-built` was passed.
|
|
623
|
+
- `packages` is sorted by name.
|
|
624
|
+
|
|
625
|
+
### Shape — `publish`
|
|
626
|
+
|
|
627
|
+
```json
|
|
628
|
+
{
|
|
629
|
+
"command": "publish",
|
|
630
|
+
"wyvrnpmVersion": "2.2.1",
|
|
631
|
+
"name": "zlib",
|
|
632
|
+
"version": "1.3.0.0",
|
|
633
|
+
"profileHash": "a1b2c3d4e5f60718",
|
|
634
|
+
"source": "s3://team-pkgs",
|
|
635
|
+
"contentSha256": "…",
|
|
636
|
+
"options": { "shared": false, "minizip": true },
|
|
637
|
+
"gitSha": "…",
|
|
638
|
+
"gitRepo": "…",
|
|
639
|
+
"publishedAt": "2026-04-21T12:34:56.789Z"
|
|
640
|
+
}
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
### Shape — `show`
|
|
644
|
+
|
|
645
|
+
```json
|
|
646
|
+
{
|
|
647
|
+
"command": "show",
|
|
648
|
+
"wyvrnpmVersion": "2.2.1",
|
|
649
|
+
"source": "s3://team-pkgs",
|
|
650
|
+
"package": "zlib",
|
|
651
|
+
"latest": "1.3.0.0",
|
|
652
|
+
"versions": {
|
|
653
|
+
"1.3.0.0": {
|
|
654
|
+
"source": { "gitRepo": "…", "gitSha": "…" },
|
|
655
|
+
"profiles": [
|
|
656
|
+
{
|
|
657
|
+
"profileHash": "a1b2c3d4e5f60718",
|
|
658
|
+
"contentSha256": "…",
|
|
659
|
+
"publishedAt": "…",
|
|
660
|
+
"buildSettings": { "os": "Windows", "...": "...", "options": { "minizip": true } },
|
|
661
|
+
"origin": "author-publish",
|
|
662
|
+
"uploadedBy": null,
|
|
663
|
+
"compatibility": { "compiler.cppstd": "lte" },
|
|
664
|
+
"declaredOptions": { "minizip": { "default": true, "allowed": [true, false] } }
|
|
665
|
+
}
|
|
666
|
+
]
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
- `origin` is `"author-publish"` | `"consumer-source-build"` | `null` (the last when the summary view can't tell without a deep fetch).
|
|
673
|
+
- Bare-name queries (`wyvrnpm show zlib --format json`) skip the per-profile deep fetch; `origin`, `uploadedBy`, `compatibility`, `declaredOptions` are `null` for each profile.
|
|
674
|
+
|
|
675
|
+
Exit codes and error behaviour are identical to text mode. Fatal errors leave stdout empty rather than emitting partial JSON.
|
|
676
|
+
|
|
677
|
+
---
|
|
678
|
+
|
|
521
679
|
## Configuration File
|
|
522
680
|
|
|
523
681
|
wyvrnpm stores persistent configuration in a `config.json` file:
|
|
@@ -733,7 +891,97 @@ Fields use PascalCase. `Dependencies` is an array — transitive dependencies ar
|
|
|
733
891
|
|
|
734
892
|
**`kind` values:** `ConsoleApp`, `StaticLib`, `DynamicLib`, `HeaderOnlyLib`, `WebApp`, `Utility`, `Service`, `TestProject`
|
|
735
893
|
|
|
736
|
-
**`dependencies`** can be either a plain version string (`"major.minor.patch.build"`) or an object with `version` and an optional `
|
|
894
|
+
**`dependencies`** can be either a plain version string (`"major.minor.patch.build"`) or an object with `version`, an optional `settings` map, and an optional `options` map. The `settings` map overrides individual build profile fields for that specific dependency — useful when a dependency must be consumed with a different runtime linkage or C++ standard than your default profile. The `options` map is covered in the next section.
|
|
895
|
+
|
|
896
|
+
### Version ranges
|
|
897
|
+
|
|
898
|
+
The `version` field accepts ranges in addition to exact versions and the `"latest"` tag:
|
|
899
|
+
|
|
900
|
+
| Spec | Meaning |
|
|
901
|
+
|---|---|
|
|
902
|
+
| `"1.2.3.4"` | exact — must match byte-identically |
|
|
903
|
+
| `"latest"` | resolves to whatever `latest.json` points at |
|
|
904
|
+
| `"^1.2.3.4"` | major-pinned — `>= 1.2.3.4` and `< 2.0.0.0` |
|
|
905
|
+
| `"~1.2.3.4"` | minor-pinned — `>= 1.2.3.4` and `< 1.3.0.0` |
|
|
906
|
+
| `">=1.2.3.4 <2.0.0.0"` | explicit comparators — space-separated, supports `>=`, `>`, `<=`, `<`, `=` |
|
|
907
|
+
|
|
908
|
+
Ranges resolve at install time against each source's `versions.json`. wyvrnpm picks the **highest version** that satisfies the range. The resolved exact version is pinned into `wyvrn.lock` alongside the original range string, so re-installs are byte-reproducible — ranges are only re-evaluated when the lock file's entry for that dependency is removed.
|
|
909
|
+
|
|
910
|
+
**Error handling:**
|
|
911
|
+
|
|
912
|
+
- **No version satisfies the range** → strict failure with every published version listed so you can see what's available.
|
|
913
|
+
- **Conflicting ranges across the graph** (direct consumer wants `^1.x` but a transitive wants `^2.x`) → precise error naming both contributors and their ranges.
|
|
914
|
+
- **Malformed range string** → fail fast with a cheat-sheet of supported syntaxes.
|
|
915
|
+
|
|
916
|
+
Example:
|
|
917
|
+
|
|
918
|
+
```json
|
|
919
|
+
{
|
|
920
|
+
"dependencies": {
|
|
921
|
+
"OpenSSL": "^3.0.0.0",
|
|
922
|
+
"zlib": "~1.3.0.0",
|
|
923
|
+
"boost": ">=1.80.0.0 <2.0.0.0"
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
```
|
|
927
|
+
|
|
928
|
+
### Package options (`options`)
|
|
929
|
+
|
|
930
|
+
A library author can declare **options** — compile-time feature toggles that change the produced binary's ABI. Examples: `shared` (shared vs static library), `minizip` (zlib's minizip support), `fips` (OpenSSL's FIPS mode). Each option produces a distinct `profileHash`, so every option combination gets its own artefact on the registry.
|
|
931
|
+
|
|
932
|
+
**Author side — declare in the recipe's `wyvrn.json`:**
|
|
933
|
+
|
|
934
|
+
```json
|
|
935
|
+
{
|
|
936
|
+
"name": "zlib",
|
|
937
|
+
"version": "1.3.0.0",
|
|
938
|
+
"options": {
|
|
939
|
+
"shared": { "default": false, "allowed": [true, false] },
|
|
940
|
+
"minizip": { "default": true, "allowed": [true, false] },
|
|
941
|
+
"api": { "default": "default", "allowed": ["default", "legacy"] }
|
|
942
|
+
},
|
|
943
|
+
"build": {
|
|
944
|
+
"configure": [
|
|
945
|
+
"-DBUILD_SHARED_LIBS=${options.shared}",
|
|
946
|
+
"-DZLIB_BUILD_MINIZIP=${options.minizip}",
|
|
947
|
+
"-DZLIB_API_MODE=${options.api}"
|
|
948
|
+
]
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
```
|
|
952
|
+
|
|
953
|
+
Rules for the declaration:
|
|
954
|
+
|
|
955
|
+
- Option names must match `/^[a-z][a-z0-9_-]*$/`.
|
|
956
|
+
- Each option needs `default` + `allowed`. `default` must be a member of `allowed`.
|
|
957
|
+
- `allowed` values are `boolean` or `string`. No integers, no lists.
|
|
958
|
+
- `${options.<name>}` tokens in `build.configure` / `build.buildArgs` are expanded at source-build time. Booleans expand to `ON`/`OFF`; strings expand verbatim. A typo (`${options.fips}` with no `fips` declared) is a hard error — it will not silently reach CMake.
|
|
959
|
+
|
|
960
|
+
**Consumer side — override per dependency:**
|
|
961
|
+
|
|
962
|
+
```json
|
|
963
|
+
{
|
|
964
|
+
"dependencies": {
|
|
965
|
+
"zlib": {
|
|
966
|
+
"version": "1.3.0.0",
|
|
967
|
+
"options": { "minizip": false }
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
```
|
|
972
|
+
|
|
973
|
+
**Consumer side — override via CLI (precedence: CLI > wyvrn.json > recipe defaults):**
|
|
974
|
+
|
|
975
|
+
```bash
|
|
976
|
+
wyvrnpm install -o zlib:minizip=false -o zlib:shared=true
|
|
977
|
+
wyvrnpm publish -o zlib:minizip=false # publishing a specific variant
|
|
978
|
+
```
|
|
979
|
+
|
|
980
|
+
Option overrides are validated against the recipe's `allowed` list at resolve time; unknown option names surface a "did you mean?" suggestion.
|
|
981
|
+
|
|
982
|
+
**What happens on the registry.** Options fold into `profileHash`, so `zlib@1.3.0.0` with `minizip=true` and `zlib@1.3.0.0` with `minizip=false` are two distinct artefacts at two different URLs — same version, different hashes. `wyvrnpm show zlib@1.3.0.0` lists all published variants and prints each one's resolved options.
|
|
983
|
+
|
|
984
|
+
**Backward compatibility.** Packages that do not declare an `options` block hash byte-identically to how they did before options existed. No registry migration required.
|
|
737
985
|
|
|
738
986
|
### Source-build recipe (`build`)
|
|
739
987
|
|
|
@@ -932,6 +1180,77 @@ wyvrnpm clean --build-cache
|
|
|
932
1180
|
|
|
933
1181
|
wyvrnpm uses your ambient git configuration (SSH keys, credential helpers, Windows Credential Manager). It does not manage git credentials itself. If `git clone <gitRepo>` works from your shell, source-build works.
|
|
934
1182
|
|
|
1183
|
+
### Upload back to the registry (`--upload-built`)
|
|
1184
|
+
|
|
1185
|
+
`--build=missing` warms *your* cache. `--build=missing --upload-built` warms the *team's* cache: after a successful source-build, the freshly-zipped artefact is pushed back to the registry under the consumer's `profileHash`, so the next teammate on the same profile gets a direct v2 download.
|
|
1186
|
+
|
|
1187
|
+
```bash
|
|
1188
|
+
# Build missing artefacts locally AND publish them back under your profile.
|
|
1189
|
+
wyvrnpm install --build=missing --upload-built
|
|
1190
|
+
|
|
1191
|
+
# Override the upload destination (URL or configured publish-source name).
|
|
1192
|
+
wyvrnpm install --build=missing --upload-built --upload-source team-s3
|
|
1193
|
+
|
|
1194
|
+
# Upload auth is resolved in order: CLI flag → named-source config → first configured publish source.
|
|
1195
|
+
wyvrnpm install --build=missing --upload-built --aws-profile my-sso
|
|
1196
|
+
wyvrnpm install --build=missing --upload-built --token mytoken
|
|
1197
|
+
```
|
|
1198
|
+
|
|
1199
|
+
Semantics:
|
|
1200
|
+
|
|
1201
|
+
- **Opt-in.** Nothing uploads unless `--upload-built` is passed. Requires `--build=missing`.
|
|
1202
|
+
- **Skip if exists.** If `(name, version, profileHash)` already lives on the upload source, the upload is a silent no-op. A consumer cannot overwrite an existing artefact; use `wyvrnpm publish --force` from the source repo for that.
|
|
1203
|
+
- **`latest.json` is untouched.** A consumer upload is a new `profileHash` of an existing version, not a new version release.
|
|
1204
|
+
- **Upload failures never break the install.** The consumer already has a working `wyvrn_internal/` on disk — a broken upload is logged as a warning and the install continues.
|
|
1205
|
+
- **Provenance.** Uploaded manifests carry a small `uploadedBy` block so maintainers can tell consumer builds apart from author publishes:
|
|
1206
|
+
|
|
1207
|
+
```json
|
|
1208
|
+
{
|
|
1209
|
+
"uploadedBy": {
|
|
1210
|
+
"kind": "source-build",
|
|
1211
|
+
"profileHash": "a1b2c3d4e5f60718",
|
|
1212
|
+
"builtFromGit": "https://github.com/madler/zlib",
|
|
1213
|
+
"builtFromSha": "abcdef01...",
|
|
1214
|
+
"uploadedAt": "2026-04-21T12:34:56.789Z",
|
|
1215
|
+
"wyvrnpmVersion": "2.1.0"
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
```
|
|
1219
|
+
|
|
1220
|
+
Absence of `uploadedBy` means the artefact was published by the author via `wyvrnpm publish`.
|
|
1221
|
+
|
|
1222
|
+
At the end of an install with `--upload-built`, a one-line summary is printed:
|
|
1223
|
+
|
|
1224
|
+
```
|
|
1225
|
+
[wyvrn] upload-built summary: 2 artefacts uploaded, 1 skipped (already existed), 0 failed
|
|
1226
|
+
```
|
|
1227
|
+
|
|
1228
|
+
**Trust model.** Registry write auth is the gate — only consumers with credentials for the upload destination can upload at all. The same `v2Publish` path is used as `wyvrnpm publish`, so the resulting artefact is byte-identical in layout. SHA256 verification on the download side protects against in-transit tampering. Uploading deliberately opts into "your machine's build of this package is trusted by everyone else on this profile" — use CI with clean toolchains rather than developer workstations if you care about reproducibility.
|
|
1229
|
+
|
|
1230
|
+
#### Auditing consumer uploads (`wyvrnpm show`)
|
|
1231
|
+
|
|
1232
|
+
Maintainers can list every build of a package and tell author-published artefacts apart from consumer uploads:
|
|
1233
|
+
|
|
1234
|
+
```bash
|
|
1235
|
+
wyvrnpm show zlib # all versions + their profile hashes
|
|
1236
|
+
wyvrnpm show zlib@1.3.0.0 # every profile for that version, with origin + builtFromSha
|
|
1237
|
+
wyvrnpm show zlib@1.3.0.0@a1b2c3d4... # one specific build, full metadata
|
|
1238
|
+
```
|
|
1239
|
+
|
|
1240
|
+
With a version specified, `show` fetches each build's `wyvrn.json` and reports `author publish` or `consumer upload (source-build)` alongside the relevant `uploadedBy` fields. Sources default to the configured install sources; override with `--source <url>`.
|
|
1241
|
+
|
|
1242
|
+
#### Forgetting uploads (`clean --uploaded-built`)
|
|
1243
|
+
|
|
1244
|
+
Each successful upload writes a `.uploaded-to.json` sidecar alongside the cached artefact under `%LOCALAPPDATA%\wyvrnpm\bc\{name}-{version}-{profileHash}\`. It records destination, timestamp, and SHA — purely informational, nothing reads it during install.
|
|
1245
|
+
|
|
1246
|
+
To wipe just those local records (no effect on the registry, the artefact zips, or `wyvrn_internal/`):
|
|
1247
|
+
|
|
1248
|
+
```bash
|
|
1249
|
+
wyvrnpm clean --uploaded-built
|
|
1250
|
+
```
|
|
1251
|
+
|
|
1252
|
+
This is a privacy / local-audit cleanup knob — `clean --build-cache` already removes sidecars as a side effect of nuking the cache.
|
|
1253
|
+
|
|
935
1254
|
---
|
|
936
1255
|
|
|
937
1256
|
## Lock File (wyvrn.lock)
|
package/bin/wyvrn.js
CHANGED
|
@@ -3,13 +3,16 @@
|
|
|
3
3
|
'use strict';
|
|
4
4
|
|
|
5
5
|
const yargs = require('yargs');
|
|
6
|
-
const init
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
6
|
+
const init = require('../src/commands/init');
|
|
7
|
+
const add = require('../src/commands/add');
|
|
8
|
+
const install = require('../src/commands/install');
|
|
9
|
+
const clean = require('../src/commands/clean');
|
|
10
|
+
const publish = require('../src/commands/publish');
|
|
11
|
+
const configure = require('../src/commands/configure');
|
|
12
|
+
const profile = require('../src/commands/profile');
|
|
13
|
+
const build = require('../src/commands/build');
|
|
14
|
+
const show = require('../src/commands/show');
|
|
15
|
+
const installSkill = require('../src/commands/install-skill');
|
|
13
16
|
const { link, unlink } = require('../src/commands/link');
|
|
14
17
|
|
|
15
18
|
yargs
|
|
@@ -33,6 +36,34 @@ yargs
|
|
|
33
36
|
(argv) => init(argv),
|
|
34
37
|
)
|
|
35
38
|
|
|
39
|
+
// ── add ───────────────────────────────────────────────────────────────────
|
|
40
|
+
.command(
|
|
41
|
+
'add <packages...>',
|
|
42
|
+
'Add dependencies to wyvrn.json. Without @version, resolves latest and writes "^<latest>"; ' +
|
|
43
|
+
'with @version, writes the given string verbatim. Does not run install.',
|
|
44
|
+
(y) => {
|
|
45
|
+
y
|
|
46
|
+
.positional('packages', {
|
|
47
|
+
type: 'string',
|
|
48
|
+
array: true,
|
|
49
|
+
describe: 'One or more <name>[@<version>] specs',
|
|
50
|
+
})
|
|
51
|
+
.option('source', {
|
|
52
|
+
alias: 's',
|
|
53
|
+
type: 'string',
|
|
54
|
+
description: 'Base URL of a package source for `latest` lookup (can be repeated)',
|
|
55
|
+
array: true,
|
|
56
|
+
})
|
|
57
|
+
.option('platform', {
|
|
58
|
+
type: 'string',
|
|
59
|
+
description: 'v1 legacy platform path (used as fallback when looking up latest)',
|
|
60
|
+
choices: ['win_x64', 'win_x86', 'linux_x64', 'linux_x86', 'osx_x64', 'osx_arm64'],
|
|
61
|
+
default: 'win_x64',
|
|
62
|
+
});
|
|
63
|
+
},
|
|
64
|
+
(argv) => add(argv),
|
|
65
|
+
)
|
|
66
|
+
|
|
36
67
|
// ── install ───────────────────────────────────────────────────────────────
|
|
37
68
|
.command(
|
|
38
69
|
'install',
|
|
@@ -58,6 +89,36 @@ yargs
|
|
|
58
89
|
'Resolution mode when no exact profile match exists. ' +
|
|
59
90
|
'never=fail; missing=build from source (pending phase 3); always=rebuild (pending phase 3)',
|
|
60
91
|
})
|
|
92
|
+
.option('option', {
|
|
93
|
+
alias: 'o',
|
|
94
|
+
type: 'string',
|
|
95
|
+
array: true,
|
|
96
|
+
description:
|
|
97
|
+
'Override a package option. Repeatable. Format: <pkg>:<name>=<value>. ' +
|
|
98
|
+
'Example: -o zlib:minizip=false -o OpenSSL:fips=true',
|
|
99
|
+
})
|
|
100
|
+
.option('upload-built', {
|
|
101
|
+
type: 'boolean',
|
|
102
|
+
default: false,
|
|
103
|
+
description:
|
|
104
|
+
'After successful --build=missing, upload each source-built artefact ' +
|
|
105
|
+
'back to the registry under the active profileHash so the next consumer ' +
|
|
106
|
+
'gets a direct download. Requires write credentials for the upload source.',
|
|
107
|
+
})
|
|
108
|
+
.option('upload-source', {
|
|
109
|
+
type: 'string',
|
|
110
|
+
description:
|
|
111
|
+
'Destination for --upload-built (URL or configured publish-source name). ' +
|
|
112
|
+
'Defaults to the first configured publish source.',
|
|
113
|
+
})
|
|
114
|
+
.option('aws-profile', {
|
|
115
|
+
type: 'string',
|
|
116
|
+
description: 'AWS SSO profile for --upload-built when uploading to S3',
|
|
117
|
+
})
|
|
118
|
+
.option('token', {
|
|
119
|
+
type: 'string',
|
|
120
|
+
description: 'Bearer token for --upload-built when uploading to HTTP',
|
|
121
|
+
})
|
|
61
122
|
.option('platform', {
|
|
62
123
|
type: 'string',
|
|
63
124
|
description: 'v1 legacy platform path (used as fallback if v2 not found)',
|
|
@@ -68,9 +129,20 @@ yargs
|
|
|
68
129
|
type: 'number',
|
|
69
130
|
description: 'HTTP timeout in seconds',
|
|
70
131
|
default: 300,
|
|
132
|
+
})
|
|
133
|
+
.option('format', {
|
|
134
|
+
type: 'string',
|
|
135
|
+
choices: ['text', 'json'],
|
|
136
|
+
default: 'text',
|
|
137
|
+
description: 'Output format. "json" emits a single JSON object on stdout; log lines go to stderr.',
|
|
71
138
|
});
|
|
72
139
|
},
|
|
73
|
-
(argv) => install(
|
|
140
|
+
(argv) => install({
|
|
141
|
+
...argv,
|
|
142
|
+
uploadBuilt: argv['upload-built'],
|
|
143
|
+
uploadSource: argv['upload-source'],
|
|
144
|
+
awsProfile: argv['aws-profile'],
|
|
145
|
+
}),
|
|
74
146
|
)
|
|
75
147
|
|
|
76
148
|
// ── build ─────────────────────────────────────────────────────────────────
|
|
@@ -113,6 +185,13 @@ yargs
|
|
|
113
185
|
default: false,
|
|
114
186
|
description: 'Remove the build directory before configuring',
|
|
115
187
|
})
|
|
188
|
+
.option('generator', {
|
|
189
|
+
alias: 'G',
|
|
190
|
+
type: 'string',
|
|
191
|
+
description:
|
|
192
|
+
'Override the preset\'s CMake generator (e.g. "Visual Studio 17 2022", "Ninja", "Ninja Multi-Config"). ' +
|
|
193
|
+
'CMake locks the generator into the build-dir cache, so pair this with --clean when switching generators.',
|
|
194
|
+
})
|
|
116
195
|
.option('install', {
|
|
117
196
|
type: 'boolean',
|
|
118
197
|
default: false,
|
|
@@ -155,13 +234,60 @@ yargs
|
|
|
155
234
|
'clean',
|
|
156
235
|
'Remove downloaded packages and lock file. --build-cache also wipes the machine-wide source-build cache.',
|
|
157
236
|
(y) => {
|
|
158
|
-
y
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
237
|
+
y
|
|
238
|
+
.option('build-cache', {
|
|
239
|
+
type: 'boolean',
|
|
240
|
+
default: false,
|
|
241
|
+
description: 'Also remove the source-build cache under %LOCALAPPDATA%\\wyvrnpm\\bc\\',
|
|
242
|
+
})
|
|
243
|
+
.option('uploaded-built', {
|
|
244
|
+
type: 'boolean',
|
|
245
|
+
default: false,
|
|
246
|
+
description:
|
|
247
|
+
'Wipe only the local record of artefacts uploaded via --upload-built ' +
|
|
248
|
+
'(the .uploaded-to.json sidecars in the build cache). Does not touch ' +
|
|
249
|
+
'the registry, the artefact zips, or wyvrn_internal/.',
|
|
250
|
+
});
|
|
163
251
|
},
|
|
164
|
-
(argv) => clean({
|
|
252
|
+
(argv) => clean({
|
|
253
|
+
...argv,
|
|
254
|
+
buildCache: argv['build-cache'],
|
|
255
|
+
uploadedBuilt: argv['uploaded-built'],
|
|
256
|
+
}),
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
// ── show ──────────────────────────────────────────────────────────────────
|
|
260
|
+
.command(
|
|
261
|
+
'show <pkg>',
|
|
262
|
+
'Display registry metadata for a package. Surfaces `uploadedBy` so consumer uploads can be told apart from author publishes.',
|
|
263
|
+
(y) => {
|
|
264
|
+
y
|
|
265
|
+
.positional('pkg', {
|
|
266
|
+
type: 'string',
|
|
267
|
+
description: 'Spec: <name>[@<version>[@<profileHash>]]',
|
|
268
|
+
})
|
|
269
|
+
.option('source', {
|
|
270
|
+
alias: 's',
|
|
271
|
+
type: 'string',
|
|
272
|
+
array: true,
|
|
273
|
+
description: 'Source URL(s) to query; defaults to configured install sources',
|
|
274
|
+
})
|
|
275
|
+
.option('aws-profile', {
|
|
276
|
+
type: 'string',
|
|
277
|
+
description: 'AWS SSO profile for S3 sources',
|
|
278
|
+
})
|
|
279
|
+
.option('token', {
|
|
280
|
+
type: 'string',
|
|
281
|
+
description: 'Bearer token for HTTP sources',
|
|
282
|
+
})
|
|
283
|
+
.option('format', {
|
|
284
|
+
type: 'string',
|
|
285
|
+
choices: ['text', 'json'],
|
|
286
|
+
default: 'text',
|
|
287
|
+
description: 'Output format. "json" emits a single JSON object on stdout; log lines go to stderr.',
|
|
288
|
+
});
|
|
289
|
+
},
|
|
290
|
+
(argv) => show({ ...argv, awsProfile: argv['aws-profile'] }),
|
|
165
291
|
)
|
|
166
292
|
|
|
167
293
|
// ── publish ───────────────────────────────────────────────────────────────
|
|
@@ -200,6 +326,20 @@ yargs
|
|
|
200
326
|
description: 'Overwrite an existing published version',
|
|
201
327
|
default: false,
|
|
202
328
|
})
|
|
329
|
+
.option('option', {
|
|
330
|
+
alias: 'o',
|
|
331
|
+
type: 'string',
|
|
332
|
+
array: true,
|
|
333
|
+
description:
|
|
334
|
+
'Override an option value at publish time. Repeatable. ' +
|
|
335
|
+
'Format: <pkg>:<name>=<value> (pkg must match the package being published).',
|
|
336
|
+
})
|
|
337
|
+
.option('format', {
|
|
338
|
+
type: 'string',
|
|
339
|
+
choices: ['text', 'json'],
|
|
340
|
+
default: 'text',
|
|
341
|
+
description: 'Output format. "json" emits a single JSON object on stdout; log lines go to stderr.',
|
|
342
|
+
})
|
|
203
343
|
;
|
|
204
344
|
},
|
|
205
345
|
(argv) => publish({ ...argv, awsProfile: argv['aws-profile'] }),
|
|
@@ -365,6 +505,19 @@ yargs
|
|
|
365
505
|
(argv) => unlink(argv),
|
|
366
506
|
)
|
|
367
507
|
|
|
508
|
+
// ── install-skill ─────────────────────────────────────────────────────────
|
|
509
|
+
.command(
|
|
510
|
+
'install-skill',
|
|
511
|
+
'Install the bundled wyvrnpm Agent Skill into Claude Code (~/.claude/skills/wyvrnpm/)',
|
|
512
|
+
(y) => {
|
|
513
|
+
y
|
|
514
|
+
.option('claude', { type: 'boolean', description: 'Install for Claude Code', default: false })
|
|
515
|
+
.option('force', { type: 'boolean', description: 'Overwrite an existing installation', default: false })
|
|
516
|
+
.option('dry-run', { type: 'boolean', description: 'Report actions without writing', default: false });
|
|
517
|
+
},
|
|
518
|
+
(argv) => installSkill(argv),
|
|
519
|
+
)
|
|
520
|
+
|
|
368
521
|
.demandCommand(1)
|
|
369
522
|
.strict()
|
|
370
523
|
.help()
|
|
Binary file
|