Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions www/src/lib/cli-downloads.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
export const FOUNDRY_LOCAL_RELEASES_URL = 'https://github.com/microsoft/Foundry-Local/releases';

export type CliDownloadLink = {
id: 'windows-cli' | 'macos-cli' | 'linux-cli';
label: string;
href: string;
releaseLabel: string;
};

export const fallbackCliDownloadLinks: CliDownloadLink[] = [
{
id: 'windows-cli',
label: 'Windows',
href: FOUNDRY_LOCAL_RELEASES_URL,
releaseLabel: 'GitHub releases'
},
{
id: 'macos-cli',
label: 'macOS',
href: FOUNDRY_LOCAL_RELEASES_URL,
releaseLabel: 'GitHub releases'
},
{
id: 'linux-cli',
label: 'Linux',
href: FOUNDRY_LOCAL_RELEASES_URL,
releaseLabel: 'GitHub releases'
}
];
44 changes: 19 additions & 25 deletions www/src/lib/components/download-dropdown.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@
import { Download, Copy, Check, ExternalLink } from 'lucide-svelte';
import { toast } from 'svelte-sonner';
import { IsMobile } from '$lib/hooks/is-mobile.svelte';

const CLI_RELEASE_URL =
'https://github.com/microsoft/Foundry-Local/releases/tag/cli-preview-0.10.0';
import { page } from '$app/stores';
import { fallbackCliDownloadLinks, type CliDownloadLink } from '$lib/cli-downloads';

interface Props {
variant?: 'default' | 'ghost' | 'outline';
Expand Down Expand Up @@ -43,6 +42,7 @@
id: string;
label: string;
href: string;
releaseLabel: string;
icon: string;
};

Expand All @@ -54,26 +54,20 @@
crossPlatformCommand: string;
};

const cliInstallOptions: CliInstallOption[] = [
{
id: 'windows',
label: 'Windows CLI',
href: CLI_RELEASE_URL,
icon: WindowsIcon
},
{
id: 'macos',
label: 'macOS CLI',
href: CLI_RELEASE_URL,
icon: AppleIcon
},
{
id: 'linux',
label: 'Linux CLI',
href: CLI_RELEASE_URL,
icon: LinuxIcon
}
];
const cliIcons = {
'windows-cli': WindowsIcon,
'macos-cli': AppleIcon,
'linux-cli': LinuxIcon
};

function getCliInstallOptions(): CliInstallOption[] {
const links = ($page.data.cliDownloadLinks as CliDownloadLink[] | undefined) ?? fallbackCliDownloadLinks;
return links.map((link) => ({
...link,
label: `${link.label} CLI`,
icon: cliIcons[link.id]
}));
}

const sdkInstallOptions: SdkInstallOption[] = [
{
Expand Down Expand Up @@ -181,7 +175,7 @@
</DropdownMenu.Label>

<DropdownMenu.Group>
{#each cliInstallOptions as item}
{#each getCliInstallOptions() as item}
<DropdownMenu.Item class="p-0">
{#snippet child({ props })}
<a
Expand All @@ -195,7 +189,7 @@
<span class="mt-0.5 inline-flex shrink-0" aria-hidden="true">{@html item.icon}</span>
<div class="flex flex-1 flex-col gap-1 px-2">
<span class="font-medium">{item.label}</span>
<code class="text-muted-foreground text-xs break-all">cli-preview-0.10.0 on GitHub</code>
<code class="text-muted-foreground text-xs break-all">{item.releaseLabel}</code>
</div>
<ExternalLink class="size-4 shrink-0 opacity-50" aria-hidden="true" />
</a>
Expand Down
86 changes: 86 additions & 0 deletions www/src/lib/server/cli-release.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { FOUNDRY_LOCAL_RELEASES_URL, fallbackCliDownloadLinks, type CliDownloadLink } from '$lib/cli-downloads';

type GitHubReleaseAsset = {
name: string;
browser_download_url: string;
};

type GitHubRelease = {
tag_name: string;
html_url: string;
assets?: GitHubReleaseAsset[];
};

type FetchLike = typeof fetch;

const RELEASES_API_URL = 'https://api.github.com/repos/microsoft/Foundry-Local/releases';
const CLI_PREVIEW_TAG = /^cli-preview-(\d+)\.(\d+)\.(\d+)$/i;

export async function getCliDownloadLinks(fetcher: FetchLike): Promise<CliDownloadLink[]> {
try {
const response = await fetcher(RELEASES_API_URL, {
headers: { Accept: 'application/vnd.github+json' }
});

if (!response.ok) {
return fallbackCliDownloadLinks;
}

const releases = (await response.json()) as GitHubRelease[];
const latestCliRelease = releases
.filter((release) => CLI_PREVIEW_TAG.test(release.tag_name))
.sort((left, right) => compareCliPreviewTags(right.tag_name, left.tag_name))[0];

if (!latestCliRelease?.assets?.length) {
return fallbackCliDownloadLinks;
}

return buildCliDownloadLinks(latestCliRelease);
} catch {
return fallbackCliDownloadLinks;
}
}

function compareCliPreviewTags(left: string, right: string): number {
const leftVersion = parseCliPreviewTag(left);
const rightVersion = parseCliPreviewTag(right);

for (let i = 0; i < leftVersion.length; i++) {
const delta = leftVersion[i] - rightVersion[i];
if (delta !== 0) return delta;
}

return 0;
}

function parseCliPreviewTag(tag: string): [number, number, number] {
const match = CLI_PREVIEW_TAG.exec(tag);
if (!match) return [0, 0, 0];
return [Number(match[1]), Number(match[2]), Number(match[3])];
}

function buildCliDownloadLinks(release: GitHubRelease): CliDownloadLink[] {
const fallbackById = new Map(fallbackCliDownloadLinks.map((link) => [link.id, link]));
const releaseLabel = release.tag_name;

return [
buildLink(fallbackById.get('windows-cli')!, release, releaseLabel, /win-x64-winml\.msix$/i),
buildLink(fallbackById.get('macos-cli')!, release, releaseLabel, /osx-arm64\.pkg$/i),
buildLink(fallbackById.get('linux-cli')!, release, releaseLabel, /linux-x64\.tar\.gz$/i)
];
}

function buildLink(
fallback: CliDownloadLink,
release: GitHubRelease,
releaseLabel: string,
assetPattern: RegExp
): CliDownloadLink {
const asset = release.assets?.find((candidate) => assetPattern.test(candidate.name));

return {
...fallback,
href: asset?.browser_download_url ?? release.html_url ?? FOUNDRY_LOCAL_RELEASES_URL,
releaseLabel: asset ? releaseLabel : 'GitHub releases'
};
}
7 changes: 7 additions & 0 deletions www/src/routes/+layout.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { getCliDownloadLinks } from '$lib/server/cli-release';

export async function load({ fetch }) {
return {
cliDownloadLinks: await getCliDownloadLinks(fetch)
};
}
25 changes: 5 additions & 20 deletions www/src/routes/models/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,15 @@
import { ModelFilters, ModelGrid, ModelDetailsModal } from './components';
import { Terminal, Copy, Check, ExternalLink } from 'lucide-svelte';
import { detectModelFamily } from '$lib/utils/model-helpers';
import { fallbackCliDownloadLinks, type CliDownloadLink } from '$lib/cli-downloads';

// Known device names used as shorthand URL params (e.g. /models?cpu)
const KNOWN_DEVICES = ['cpu', 'gpu', 'npu'];
const MODEL_QUERY_PARAM = 'model';
const CLI_RUN_COMMAND = 'foundry run qwen2.5-0.5b';
const CLI_RELEASE_URL =
'https://github.com/microsoft/Foundry-Local/releases/tag/cli-preview-0.10.0';
const CLI_INSTALL_LINKS = [
{
id: 'windows-cli',
label: 'Windows',
href: CLI_RELEASE_URL
},
{
id: 'macos-cli',
label: 'macOS',
href: CLI_RELEASE_URL
},
{
id: 'linux-cli',
label: 'Linux',
href: CLI_RELEASE_URL
}
];
function getCliInstallLinks(): CliDownloadLink[] {
return ($page.data.cliDownloadLinks as CliDownloadLink[] | undefined) ?? fallbackCliDownloadLinks;
}

// Debounce timer for search
let searchDebounceTimer: ReturnType<typeof setTimeout> | null = null;
Expand Down Expand Up @@ -535,7 +520,7 @@
<div
class="grid min-w-0 flex-1 gap-2 md:grid-cols-[repeat(3,minmax(6rem,auto))_minmax(18rem,1fr)]"
>
{#each CLI_INSTALL_LINKS as item}
{#each getCliInstallLinks() as item}
<a
href={item.href}
target="_blank"
Expand Down
Loading