Commit graph

158 commits

Author SHA1 Message Date
Shekhar Vaidya
3ceb179ae6 fix(frontend): remove redundant brew formula-name disclaimer from CLI page
The msdl-cli vs msdl naming footnote is useful context in README/release
notes, but unnecessary clutter for a casual visitor on the web CLI page --
the "recommended" badge and the command itself are enough.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-14 00:58:35 +05:30
Shekhar
11e7f7274f
Merge pull request #34 from starkSV/fix/homebrew-formula-collision
fix: update Homebrew install command to msdl-cli formula
2026-07-14 00:54:20 +05:30
Shekhar Vaidya
4dce41483d fix: update Homebrew install command to msdl-cli formula
homebrew/core already has an unrelated package literally named "msdl"
(a streaming-protocol downloader). brew install msdl silently
resolved to that instead of our tap, since Homebrew always prefers
core for a bare-name collision -- confirmed in production when a
user's `brew install msdl` pulled homebrew-core's msdl 1.2.7-r2
instead of ours. The tap's formula is renamed to msdl-cli
(starkSV/homebrew-msdl, separate repo) to fix this permanently; the
installed command itself is unaffected, still just `msdl`.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-14 00:52:57 +05:30
Shekhar
2fae096b15
Merge pull request #33 from starkSV/feat/install-script
feat: add curl | bash installer for Linux/macOS without Homebrew
2026-07-13 22:29:55 +05:30
Shekhar Vaidya
5eec0351e7 feat: add curl | bash installer for Linux/macOS without Homebrew
Adds a GET /install.sh endpoint on the backend (embedded via Go's
embed package, so the script stays a real, shellcheck-able .sh file
rather than a Go string literal). Auto-detects OS/arch (including
Termux on Android via $PREFIX) and always resolves the latest GitHub
release via the /releases/latest/download/ redirect, so unlike the
winget/brew/AUR manifests it needs no per-release maintenance.

Verified end-to-end in an ephemeral Ubuntu container (both the normal
/usr/local/bin path and the Termux $PREFIX path), and locally against
a running backend instance.

Documented alongside the existing winget/Homebrew instructions in
README.md, the release notes template, the web CLI page, and the
per-product CliHandoff card.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-13 22:28:23 +05:30
Shekhar
151f70d67f
Merge pull request #32 from starkSV/feat/aur-package
feat: add AUR msdl-bin package; document Homebrew tap everywhere
2026-07-13 13:40:17 +05:30
Shekhar Vaidya
eddf7802bb docs: note AUR publishing is blocked; add Homebrew install everywhere
Arch Linux disabled new AUR account registrations on 2026-06-15 after
a malware campaign compromised 1,500+ AUR packages. There's no
announced reopening date, so aur/msdl-bin/README.md now says so
clearly instead of implying publishing is just a pending step.

Adds the Homebrew tap install command (brew tap starkSV/msdl && brew
install msdl) to every place winget is already documented: README.md,
the release workflow's notes, the web CLI page, and the per-product
CliHandoff card. Also adds the missing linux-arm64 row to the manual
download tables.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-13 13:39:30 +05:30
Shekhar Vaidya
c14c18e955 feat: generate .SRCINFO via Docker; pin AUR files to LF line endings
.SRCINFO generated with the real makepkg tool inside an ephemeral
archlinux container -- no VM or native Arch install needed, and no
risk of hand-written formatting drift from the makepkg-canonical
output.

Also adds .gitattributes forcing LF for PKGBUILD/.SRCINFO regardless
of platform checkout settings -- both are parsed by shell/makepkg
tooling that breaks on CRLF.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-13 13:26:26 +05:30
Shekhar Vaidya
e660e95d49 feat: add AUR msdl-bin package; document Homebrew tap
Tracks the AUR PKGBUILD in-repo for version history, mirroring how
winget/manifests/ already works. Also corrects CONTRIBUTING.md's claim
that winget auto-updates via CI -- WINGET_TOKEN isn't set, so that
step always no-ops; package manager updates are all submitted
manually by the maintainer after each release.

Homebrew tap published separately at starkSV/homebrew-msdl.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-13 13:16:12 +05:30
Shekhar
f514d972a3
Merge pull request #31 from starkSV/chore/cli-v0.3.5-bump
chore(release): bump to v0.3.5 -- backend version string + winget manifests
2026-07-13 12:58:55 +05:30
Shekhar Vaidya
388f115db6 chore(release): bump to v0.3.5 -- backend version string + winget manifests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-13 12:58:06 +05:30
Shekhar
a314cdcb5e
Merge pull request #30 from starkSV/fix/help-lang-example
feat(cli): homepage landing screen, search fix, and --lang example fix
2026-07-13 12:53:42 +05:30
Shekhar
1432cb3158
Merge pull request #29 from starkSV/feat/sentinel-distinct-sources
feat(backend): approximate distinct-source count for Sentinel rejections
2026-07-13 12:46:06 +05:30
Shekhar Vaidya
327c5e195d simplify(backend): drop date-scoping for Sentinel stats, use flat all-time fields
Matches the shape actually asked for and how every other telemetry
counter in this file already works (all-time cumulative via HIncrBy,
no expiry, no per-day key rotation): /metrics now exposes flat
sentinel_errors and sentinel_distinct_sources_est fields instead of a
nested, date-scoped sentinel_today object.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-13 12:43:31 +05:30
Shekhar Vaidya
b252ba1c30 feat(backend): approximate distinct-source count for Sentinel rejections
CLI telemetry showed a growing count of Sentinel rejections but no way
to tell whether they come from one user retrying repeatedly or many
distinct users independently getting blocked.

Adds a per-day Redis HyperLogLog (msdl:telemetry:sentinel_hll:<date>)
seeded from the reporting IP whenever a CLI error contains "Sentinel",
plus a parallel daily counter. /metrics now exposes sentinel_today:
{date, errors_today, distinct_sources_est}. No raw IPs are stored --
HLL is a one-way cardinality estimator, and both keys expire after 48h.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-13 12:37:01 +05:30
Shekhar Vaidya
3994a82d6d feat(cli): homepage landing screen for bare msdl; fix search substring bug
The bare `msdl` invocation showed all 22 products (17 consumer + 5
eval) as a flat numbered list. Replaces it with a curated landing
screen: 5 popular products (picked from real usage telemetry) plus a
2-item eval shortlist, with "list" as an escape hatch to today's full
picker and free text routed through the existing search flow.

Also fixes a search bug: querying "windows 10" incorrectly matched
Windows 11 24H2 because "10" is a substring of its build number
(26100.1742). Query words now must start at a word boundary rather
than matching anywhere, so digit fragments inside unrelated numbers
can no longer collide -- while "arm" still fuzzy-matches "ARM64".

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-13 12:24:43 +05:30
Shekhar Vaidya
b1ee80f9b6 fix(cli): use plain "English" in --lang examples, not "English (United States)"
Consumer Windows products list their English option as "English" (or
"English International"), not "English (United States)" -- the exact
example this used, which is why it was such a common source of --lang
mismatch errors. Applies to --help, the --lang flag description, and
the release workflow's usage example.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-13 11:27:12 +05:30
Shekhar
6e374e6a45
Merge pull request #28 from starkSV/fix/changelog-generation
fix(ci): correct release changelog generation
2026-07-13 11:18:24 +05:30
Shekhar Vaidya
1d5fc9c0f5 fix(ci): correct release changelog generation
Two bugs combined to make v0.3.4's changelog show the previous
release's version-bump commit instead of the actual change:

1. The path filter (cli/ backend/ frontend/) excluded workflow-only
   changes, so a CI-only addition (like linux-arm64) never appeared.
2. The "bump version + winget manifest" commit is created *after* its
   own tag (it needs the release's SHA256, which doesn't exist until
   after tagging) -- so it always lands in the *next* release's diff
   range instead of its own, showing up as stale noise.

Adds .github/workflows/cli-release.yml to the path filter, filters
out "chore(release): bump" commits as noise, and falls back to a
generic line if a release genuinely has nothing else to show.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-13 11:16:24 +05:30
Shekhar
e1151ff0cf
Merge pull request #27 from starkSV/docs/contributing
docs: add CONTRIBUTING.md
2026-07-13 11:03:44 +05:30
Shekhar Vaidya
4bb0ba9f2a docs: add CONTRIBUTING.md
Moves the README's inline contributing notes into a dedicated file
and expands them with dev setup, PR conventions, and how CLI releases
work, so contributors have one place to look.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-13 11:00:46 +05:30
Shekhar
98f61b1831
Merge pull request #26 from starkSV/feat/linux-arm64-build
feat(ci): add linux-arm64 build target for CLI releases
2026-07-13 10:50:49 +05:30
Shekhar Vaidya
e2e41e8ecf feat(ci): add linux-arm64 build target for CLI releases
Telemetry showed a growing number of Termux (Android) users running
the CLI, but the release workflow only builds linux-amd64 -- those
users had to build from source since Termux runs on ARM64. Adds a
linux-arm64 binary and install line so they can just curl it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-13 10:45:33 +05:30
Shekhar
c4fb7a251b
Merge pull request #25 from starkSV/chore/cli-v0.3.3-bump
chore(release): bump to v0.3.3 -- backend version string + winget manifests
2026-07-12 18:18:05 +05:30
Shekhar Vaidya
a3f75c1ce5 chore(release): bump to v0.3.3 -- backend version string + winget manifests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-12 18:17:30 +05:30
Shekhar
75295943b6
Merge pull request #24 from starkSV/feat/cli-error-messages
fix(cli): list available languages on --lang mismatch; fix telemetry truncation
2026-07-12 18:14:26 +05:30
Shekhar Vaidya
61a296b92b fix(cli): list available languages on --lang mismatch; fix telemetry truncation
Real-world telemetry showed --lang "English (United States)" failing
for several consumer products that only list "English International"
-- the exact example used in --help. The mismatch error now lists the
languages actually available, and the help text notes names vary by
product.

Also fixes truncateError to keep both head and tail of long error
strings (previously kept only the head, which for wrapped errors with
a long URL cut off the actual root cause at the end).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-12 18:13:48 +05:30
Shekhar
48a6cb5eb4
Merge pull request #23 from starkSV/fix/lockdown-cache-status
fix(backend): don't mislabel fresh lockdown-gate cache hits as stale
2026-07-08 19:48:19 +05:30
Shekhar Vaidya
8b430fab5d fix(backend): don't mislabel fresh lockdown-gate cache hits as stale
The Sentinel lockdown gate served any linkCache hit as "stale"
regardless of whether the entry was still within its own TTL. A link
just contributed via CLI (still valid for ~24h) would get labeled
stale, causing the frontend to hide it and show the CLI-only banner
again instead of the working link it just received.

Now checks the entry's actual expiry: still-fresh entries are labeled
"cached" (frontend shows the link normally); only genuinely expired
entries are labeled "stale".

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-08 19:47:09 +05:30
Shekhar
e5d9dc3d61
Merge pull request #22 from starkSV/chore/cli-v0.3.2-bump
chore(release): bump to v0.3.2 -- backend version string + winget manifests
2026-07-08 12:45:06 +05:30
Shekhar Vaidya
ef64e06b99 chore(release): bump to v0.3.2 -- backend version string + winget manifests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-08 12:44:07 +05:30
Shekhar
6d55ee573f
Merge pull request #21 from starkSV/feat/telemetry-error-field
feat(telemetry): capture error message on failed CLI runs
2026-07-08 12:36:26 +05:30
Shekhar Vaidya
8ab322038b feat(telemetry): capture error message on failed CLI runs
Failure telemetry only recorded success=false with no context. Adds
a truncated error string (150 chars, CLI-side) forwarded to a new
msdl:telemetry:errors Redis hash so failure causes are diagnosable
from /metrics instead of guessed at.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-08 12:29:59 +05:30
Shekhar
8258bd139d
Merge pull request #20 from starkSV/feat/cli-v0.3.1
chore(release): bump to v0.3.1 -- backend version string + winget manifests
2026-06-30 08:14:14 +05:30
Shekhar Vaidya
d275340c93 chore(release): bump to v0.3.1 — backend version string + winget manifests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 08:10:25 +05:30
Shekhar Vaidya
bc857c4986 fix(cli): send telemetry synchronously; tag eval-via-picker correctly
Telemetry was fired as a goroutine then the process exited immediately,
killing it before the HTTP request could complete. Now called
synchronously — sendTelemetry already has a 2s internal timeout so
worst-case latency is unchanged for the user.

Also moves the combined picker into run() so eval products selected
interactively are tagged action=eval with the correct eval_slug instead
of action=interactive with no slug.

Adds What's New changelog section to release notes, auto-generated from
commits since the previous tag.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 07:50:13 +05:30
Shekhar Vaidya
5fa1b11835 chore(winget): bump manifests to v0.3.0
SHA256: FA6DA7B905CFCAD52A0DAE884641055B00A831914F38E78F86F927DED51B0639

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 07:28:58 +05:30
Shekhar Vaidya
2c068ff055 feat(cli): v0.3.0 — telemetry, arch picker, O/C menu, eval in picker
Backend:
- GET /cli/version update-check endpoint
- POST /telemetry anonymous usage counters (Redis-persisted)
- Telemetry counters in /metrics response

CLI:
- Version var injected via ldflags at build time
- Update notice on every run (non-blocking, 500ms timeout)
- Architecture picker when multiple links returned
- Post-fetch [O] Open in browser / [C] Copy / [Enter] Exit menu
- Combined interactive picker includes Evaluation/Enterprise products
  (Windows Server 2025/2022/2019/2016, Windows 11 Enterprise)
- openInBrowser: writes URL to temp .url file — & no longer truncated
- copyToClipboard: calls clip.exe via full SystemRoot path

Frontend:
- Winget elevated to primary install on CLI page + CliHandoff card
- CliHandoff command shows msdl (no .exe suffix)

Docs: README telemetry disclosure

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 07:26:31 +05:30
Shekhar Vaidya
a9c713cd6a feat(cli): combined product picker with eval products + clipboard fix
- Interactive `msdl` (no flags) now lists consumer + eval products in one
  picker with an Evaluation / Enterprise separator; selecting a server
  product routes to the eval flow automatically
- copyToClipboard calls clip.exe by full SystemRoot path, bypassing
  cmd.exe PATH lookup that failed in some terminal environments
- CliHandoff card: winget primary install + msdl (no .exe) in run command

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 07:25:03 +05:30
Shekhar Vaidya
7a267a2581 fix(cli): open browser via temp .url file to preserve & in signed URLs
cmd.exe treats & as a command separator, truncating Microsoft's signed
download URLs at the first query parameter boundary. Writing the URL to
a Windows Internet Shortcut (.url) file means cmd.exe only ever sees a
temp filename with no special characters; the browser reads the full URL
from the file contents.

Uses %COMSPEC% to locate cmd.exe reliably across environments.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 07:06:02 +05:30
Shekhar Vaidya
9e836503c0 Merge branch 'main' into feat/cli-telemetry 2026-06-30 06:40:46 +05:30
Shekhar Vaidya
d4973ab42a fix(backend): truncate HTTP error body in logs to 120 chars
Microsoft's 403 capacity page was logging hundreds of lines of HTML
per error. Both non-200 error paths now cap the body at 120 chars.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-27 10:33:03 +05:30
Shekhar Vaidya
d2c5c3c082 feat(frontend): use tracked shortlink for CLI releases download button
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 12:17:59 +05:30
Shekhar Vaidya
40327c33b4 fix(frontend): show lockdown message on error path when no stale data
When the backend returns 429+lockdown with no cached stale data to serve,
the error section now shows the human-readable lockdown explanation instead
of the raw "Sentinel marked this request as rejected." string.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 12:12:51 +05:30
Shekhar Vaidya
e4e0e52971 Merge feat/sentinel-lockdown into main 2026-06-25 12:07:11 +05:30
Shekhar Vaidya
3141635300 feat(backend+frontend): Sentinel lockdown mode + stale UX overhaul
Backend (main.go):
- negCacheEntry gains IsSentinel bool; lockdownTTL = 90min constant
- Lockdown gate at top of handleProxy: serves stale with X-MSDL-Lockdown:1
  header or 429 {"error":"...","lockdown":true} — zero MS API calls made
- Sentinel stale path: sets 90min lockdown neg cache, adds lockdown header,
  suppresses background refresh (retrying extends the Sentinel block timer)
- Non-Sentinel stale path: unchanged (60s neg cache, bg refresh fires)
- Expose X-MSDL-Lockdown in Access-Control-Expose-Headers

Frontend (ProductDetailPage.tsx):
- isLockdown state: set from X-MSDL-Lockdown header or data.lockdown field
- Stale state hides expired download links entirely; shows CLI recommendation
  inside the card instead of sending users to look for options elsewhere
- Lockdown: red banner explaining 90min cooldown + highlighted CLI card,
  no Refresh button (it cannot help while Sentinel block is active)
- Normal stale: amber banner + highlighted CLI card + secondary Refresh button
- Outer CliHandoff suppressed when already shown inside card (stale states)

Frontend (CliHandoff.tsx):
- CLI command uses msdl.exe (primary audience is Windows users)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 12:07:00 +05:30
Shekhar Vaidya
3e45bfd0c7 feat(cli): architecture picker + post-fetch O/C/Enter menu
When multiple download links are returned (e.g. x32+x64 for consumer
ISOs), an architecture picker is shown before printing the URL.

After the URL is printed, an interactive menu lets the user open the
link in the default browser (O), copy it to the clipboard (C), or exit
(Enter). Menu is suppressed when stdout is not a terminal (piping).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 11:49:31 +05:30
Shekhar Vaidya
0525015ca9 fix(backend): detect Sentinel in JSON Errors field, evict session on block
Microsoft's WAF returns HTTP 200 with {"Errors":[{"Value":"Sentinel marked
this request as rejected."}]}. The existing Sentinel check only ran for
non-200 responses, so the session was never evicted — every subsequent
request reused the poisoned session and hit Sentinel again.

Add the same eviction + rateLimitError logic inside the errMap["Value"]
branch so the session is evicted regardless of the WAF's HTTP status code.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-22 11:13:34 +05:30
Shekhar Vaidya
29d6b6b3f1 feat(frontend): elevate winget install to primary method on CLI page
Replaces the dim "coming soon" footnote with a featured green card at the
top of the Install section. Manual binary downloads moved to a secondary
"Other platforms" section below. Ships with the post-winget-approval release.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 21:38:40 +05:30
Shekhar Vaidya
1e00dcee2e fix(backend): validate telemetry fields; add method guard to /cli/version
- Add platform allowlist validation (windows, darwin, linux) and length caps for version (≤20) and product_id (≤10) in handleTelemetry to prevent unbounded Redis hash growth from attacker-controlled field values.
- Add HTTP method guard to handleCLIVersion to reject non-GET requests with 405 Method Not Allowed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 21:14:21 +05:30