src/modules/cmdpal/doc/extension-gallery.md
This document describes how Command Palette (CmdPal) discovers extensions for the in-app Extension gallery page.
extensions.json from a remote
HTTPS URL, parses it, and renders the entries.microsoft/CmdPal-Extensions at
https://aka.ms/CmdPal-ExtensionsJson.manifest.json fetch, and no
other network call for rendering the list.| Concern | File |
|---|---|
| Fetching, parsing, caching, pruning | Microsoft.CmdPal.Common/ExtensionGallery/Services/ExtensionGalleryService.cs |
| Resolving which URL to fetch | Microsoft.CmdPal.Common/ExtensionGallery/Services/GalleryFeedUrlProvider.cs + Microsoft.CmdPal.UI/Helpers/GalleryServiceRegistration.cs |
| HTTP + on-disk cache | Microsoft.CmdPal.Common/ExtensionGallery/Services/ExtensionGalleryHttpClient.cs (wraps Microsoft.CmdPal.Common/Services/HttpCaching/HttpCachingClient) |
| Feed + entry models | Microsoft.CmdPal.Common/ExtensionGallery/Models/ |
ExtensionGalleryService.GetFeedUrl() returns, in order:
SettingsModel.GalleryFeedUrl,
exposed via the hidden InternalPage settings page). Any non-empty value
wins. Mostly used for local testing against a custom feed.https://aka.ms/CmdPal-ExtensionsJson.Local file:// URIs are allowed too — FetchFeedDocumentAsync reads the file
directly and bypasses the HTTP cache.
The feed is a single wrapped JSON document with inline entries:
{
"$schema": "https://raw.githubusercontent.com/microsoft/CmdPal-Extensions/main/.github/schemas/gallery.schema.json",
"extensions": [
{
"id": "sample-extension",
"title": "Sample Extension",
"description": "A sample extension demonstrating the gallery feed format.",
"author": { "name": "Microsoft", "url": "https://github.com/microsoft" },
"homepage": "https://github.com/microsoft/CmdPal-Extensions",
"iconUrl": "https://.../icon.png",
"screenshotUrls": ["https://.../screenshot-1.png"],
"tags": ["sample"],
"installSources": [
{ "type": "winget", "id": "Contoso.SampleExtension" },
{ "type": "msstore", "id": "9P…" },
{ "type": "url", "uri": "https://github.com/contoso/sample/releases/latest" }
],
"detection": { "packageFamilyName": "Contoso.SampleExtension_1234567890abc" }
}
]
}
Only the extensions array is read at runtime. The authoritative JSON
schema for an entry lives in the upstream feed repo
(microsoft/CmdPal-Extensions);
don't duplicate it here — it drifts.
| Field | Required | Notes |
|---|---|---|
id | yes | Lowercase stable identifier; entries with empty id are dropped. |
title | yes | Display name. |
description | yes | Shown in list and detail views. |
author.name | yes | author.url optional. |
installSources | yes | At least one entry; see Install sources. |
homepage, iconUrl, screenshotUrls, tags, detection.packageFamilyName | no | All optional. |
Relative iconUrl / screenshotUrls are resolved against the feed URL's
directory (useful only for local / file:// feeds during development).
Each entry's installSources is consumed by
ExtensionGalleryItemViewModel to decide which install affordances to show.
type | Required field | Behaviour |
|---|---|---|
winget | id | Enables the "Install via WinGet" button (uses the shared WinGet service), and joins in-flight install progress + installed/update status. |
msstore | id | Opens ms-windows-store://pdp/?ProductId={id}. |
url | uri | Shown as a "GitHub" or "Website" link depending on host. |
An entry can declare any combination. Sources the runtime does not recognise are surfaced as an "unknown source" indicator.
ExtensionGalleryService uses ExtensionGalleryHttpClient, which wraps
HttpCachingClient over a file-system cache. Both the feed JSON and any
cacheable icon URLs are cached.
| Setting | Value | Defined in |
|---|---|---|
| Cache root | {AppCache}\GalleryCache\ | ExtensionGalleryHttpClient.CacheDirectoryName |
| Feed TTL | 4 hours | ExtensionGalleryHttpClient.DefaultTimeToLive |
| Icon TTL | 24 hours | ExtensionGalleryService.IconCacheTtl |
| HTTP timeout | 15 s | ExtensionGalleryHttpClient |
User-Agent | PowerToys-CmdPal/1.0 | ExtensionGalleryHttpClient |
{AppCache} resolves to ApplicationData.Current.LocalCacheFolder when
CmdPal runs packaged, and to
%LOCALAPPDATA%\Microsoft\PowerToys\Microsoft.CmdPal\Cache\ when unpackaged
(see ApplicationInfoService.DetermineCacheDirectory).
GetExtensionsAsync (normal load) and RefreshAsync (user-initiated
refresh, forceRefresh: true) both go through FetchWrappedFeedAsync:
HttpCachingClient.GetResourceAsync which:
If-None-Match). On 304 Not Modified it refreshes the cache metadata and returns the cached
body.UsedFallbackCache = true, so the UI can show a "stale data" banner.GallerySerializationContext
(strongly-typed GalleryRemoteIndex — no reflection, AOT-friendly).id, normalize relative iconUrl and
screenshotUrls, and resolve remote icon URIs through the same HTTP
cache so the UI binds to local file:// URIs.PruneCachedResources deletes cache
entries that are no longer referenced by the current feed (old feed URL
and icon URLs that dropped out of the feed).GetExtensionsAsync returns a GalleryFetchResult that the view model uses
for UI hints:
| Flag | Meaning |
|---|---|
FromCache | The feed came from cache without hitting the network (TTL still valid). |
UsedFallbackCache | A network request was attempted and failed, and the cached copy was served as fallback. The UI shows a stale-data info bar. |
RateLimited | The origin returned 429 Too Many Requests and no fallback was available. The UI shows a rate-limit error. |
microsoft/CmdPal-Extensions.$schema field.id stable once an extension is published — users may have it
installed and the gallery keys install status by id.winget source when the extension ships through App
Installer; the gallery uses it both for status ("Installed" / "Update
available") and for the in-app install button.detection.packageFamilyName lets the gallery recognise an
already-installed packaged extension before WinGet metadata resolves.