Merge pull request #41656 from BerriAI/litellm_remove_dead_networking_and_marketplace_helpers

refactor(ui): remove dead networking exports and orphaned Claude Code marketplace helpers
This commit is contained in:
Mateo Wang 2026-09-18 08:47:46 -07:00 committed by GitHub
commit 8ff4991583
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 5 additions and 459 deletions

View file

@ -1066,7 +1066,7 @@
"count": 1
},
"prefer-const": {
"count": 2
"count": 1
}
},
"src/app/(dashboard)/search-tools/_components/SearchTools.tsx": {
@ -1804,16 +1804,16 @@
"count": 1
},
"max-params": {
"count": 23
"count": 21
},
"no-nested-ternary": {
"count": 5
},
"no-restricted-syntax": {
"count": 150
"count": 147
},
"prefer-const": {
"count": 32
"count": 31
}
},
"src/components/object_permissions_view.tsx": {

View file

@ -19,7 +19,6 @@ vi.mock("@/components/networking", () => ({
getAgentsList: vi.fn(),
fetchMCPServers: vi.fn(),
getUiSettings: vi.fn(),
getClaudeCodeMarketplace: vi.fn(),
getClaudeCodePluginsList: vi.fn(() => Promise.resolve({ plugins: [] })),
}));

View file

@ -64,7 +64,6 @@ vi.mock("./networking", () => ({
teamCreateCall: vi.fn(),
teamDeleteCall: vi.fn(),
fetchMCPAccessGroups: vi.fn(),
v2TeamListCall: vi.fn(),
getGuardrailsList: vi.fn().mockResolvedValue({ guardrails: [] }),
getPoliciesList: vi.fn().mockResolvedValue({ policies: [] }),
getDefaultTeamSettings: vi.fn().mockResolvedValue({ values: {} }),

View file

@ -1,26 +1,19 @@
import { describe, expect, it } from "vitest";
import {
formatInstallCommand,
extractCategories,
validatePluginName,
getSourceDisplayText,
getSourceLink,
getCategoryBadgeColor,
formatDateString,
truncateText,
filterPluginsBySearch,
filterPluginsByCategory,
isValidSemanticVersion,
isValidEmail,
isValidUrl,
parseKeywords,
formatKeywords,
parseSkillSource,
isValidSubPath,
isValidSha256,
buildMarketplaceSettingsSnippet,
} from "./helpers";
import { MarketplacePluginEntry } from "./types";
describe("buildMarketplaceSettingsSnippet", () => {
it("nests the url under a source object so Claude Code accepts the marketplace", () => {
@ -47,27 +40,6 @@ describe("formatInstallCommand", () => {
});
});
describe("extractCategories", () => {
it("returns All and Other for empty list", () => {
expect(extractCategories([])).toEqual(["All", "Other"]);
});
it("extracts and sorts unique categories", () => {
const plugins = [{ category: "Development" }, { category: "Analytics" }, { category: "Development" }];
expect(extractCategories(plugins)).toEqual(["All", "Analytics", "Development", "Other"]);
});
it("ignores empty/whitespace categories", () => {
const plugins = [{ category: "" }, { category: " " }, { category: "Tools" }];
expect(extractCategories(plugins)).toEqual(["All", "Tools", "Other"]);
});
it("handles undefined category", () => {
const plugins = [{ category: undefined }, { category: "Security" }];
expect(extractCategories(plugins)).toEqual(["All", "Security", "Other"]);
});
});
describe("validatePluginName", () => {
it("accepts valid kebab-case names", () => {
expect(validatePluginName("my-plugin")).toBe(true);
@ -209,106 +181,6 @@ describe("getCategoryBadgeColor", () => {
});
});
describe("formatDateString", () => {
it("formats valid date strings", () => {
const result = formatDateString("2024-01-15T12:00:00Z");
expect(result).toContain("2024");
expect(result).toContain("Jan");
expect(result).toContain("15");
});
it("returns N/A for undefined", () => {
expect(formatDateString(undefined)).toBe("N/A");
});
it("returns N/A for empty string", () => {
expect(formatDateString("")).toBe("N/A");
});
});
describe("truncateText", () => {
it("returns text unchanged if shorter than max", () => {
expect(truncateText("hello", 10)).toBe("hello");
});
it("truncates and adds ellipsis", () => {
expect(truncateText("hello world", 5)).toBe("hello...");
});
it("handles exact length", () => {
expect(truncateText("hello", 5)).toBe("hello");
});
it("handles empty text", () => {
expect(truncateText("", 5)).toBe("");
});
});
describe("filterPluginsBySearch", () => {
const plugins: MarketplacePluginEntry[] = [
{
name: "code-formatter",
source: { source: "github", repo: "org/formatter" },
description: "Formats code nicely",
keywords: ["format", "lint"],
},
{
name: "data-viewer",
source: { source: "github", repo: "org/viewer" },
description: "View data",
keywords: ["analytics"],
},
];
it("returns all plugins for empty search", () => {
expect(filterPluginsBySearch(plugins, "")).toEqual(plugins);
expect(filterPluginsBySearch(plugins, " ")).toEqual(plugins);
});
it("matches by name", () => {
expect(filterPluginsBySearch(plugins, "formatter")).toHaveLength(1);
expect(filterPluginsBySearch(plugins, "formatter")[0].name).toBe("code-formatter");
});
it("matches by description", () => {
expect(filterPluginsBySearch(plugins, "nicely")).toHaveLength(1);
});
it("matches by keyword", () => {
expect(filterPluginsBySearch(plugins, "analytics")).toHaveLength(1);
expect(filterPluginsBySearch(plugins, "analytics")[0].name).toBe("data-viewer");
});
it("is case insensitive", () => {
expect(filterPluginsBySearch(plugins, "FORMATTER")).toHaveLength(1);
});
});
describe("filterPluginsByCategory", () => {
const plugins: MarketplacePluginEntry[] = [
{ name: "a", source: { source: "github" }, category: "Dev" },
{ name: "b", source: { source: "github" }, category: "Security" },
{ name: "c", source: { source: "github" }, category: "" },
{ name: "d", source: { source: "github" } },
];
it("returns all plugins for 'All'", () => {
expect(filterPluginsByCategory(plugins, "All")).toEqual(plugins);
});
it("returns uncategorized plugins for 'Other'", () => {
const result = filterPluginsByCategory(plugins, "Other");
expect(result).toHaveLength(2);
expect(result.map((p) => p.name)).toEqual(["c", "d"]);
});
it("filters by specific category", () => {
const result = filterPluginsByCategory(plugins, "Dev");
expect(result).toHaveLength(1);
expect(result[0].name).toBe("a");
});
});
describe("isValidSemanticVersion", () => {
it("accepts valid semver", () => {
expect(isValidSemanticVersion("1.0.0")).toBe(true);
@ -389,17 +261,6 @@ describe("parseKeywords", () => {
});
});
describe("formatKeywords", () => {
it("joins keywords with comma and space", () => {
expect(formatKeywords(["a", "b", "c"])).toBe("a, b, c");
});
it("returns empty string for empty/undefined array", () => {
expect(formatKeywords([])).toBe("");
expect(formatKeywords(undefined)).toBe("");
});
});
describe("parseSkillSource", () => {
it("parses a plain github repo", () => {
expect(parseSkillSource("github.com/org/repo")?.parsed).toEqual({ source: "github", repo: "org/repo" });

View file

@ -2,7 +2,7 @@
* Helper utilities for Claude Code Marketplace
*/
import { PluginSource, MarketplacePluginEntry } from "./types";
import { PluginSource } from "./types";
export interface SkillSourcePreview {
parsed: PluginSource;
@ -262,25 +262,6 @@ export const buildMarketplaceSettingsSnippet = (proxyOrigin: string): string =>
*/
export const formatInstallCommand = (plugin: { name: string }): string => `/plugin install ${plugin.name}@litellm`;
/**
* Extract unique categories from plugins list
* Returns array with "All" first, then sorted categories, then "Other"
*/
export const extractCategories = (plugins: Array<{ category?: string }>): string[] => {
const categories = new Set<string>();
plugins.forEach((p) => {
if (p.category && p.category.trim() !== "") {
categories.add(p.category);
}
});
const sortedCategories = Array.from(categories).sort();
// Return: All, sorted categories, Other
return ["All", ...sortedCategories, "Other"];
};
/**
* Validate plugin name format (kebab-case)
* Must be lowercase letters, numbers, and hyphens only
@ -349,77 +330,6 @@ export const getCategoryBadgeColor = (
return "gray";
};
/**
* Format date to readable string
*/
export const formatDateString = (dateString?: string): string => {
if (!dateString) {
return "N/A";
}
try {
const date = new Date(dateString);
return date.toLocaleDateString("en-US", {
year: "numeric",
month: "short",
day: "numeric",
});
} catch (error) {
return "Invalid date";
}
};
/**
* Truncate text with ellipsis
*/
export const truncateText = (text: string, maxLength: number): string => {
if (!text || text.length <= maxLength) {
return text;
}
return text.substring(0, maxLength) + "...";
};
/**
* Filter plugins by search term
* Searches in: name, description, keywords
*/
export const filterPluginsBySearch = (
plugins: MarketplacePluginEntry[],
searchTerm: string,
): MarketplacePluginEntry[] => {
if (!searchTerm || searchTerm.trim() === "") {
return plugins;
}
const term = searchTerm.toLowerCase().trim();
return plugins.filter((plugin) => {
const nameMatch = plugin.name.toLowerCase().includes(term);
const descriptionMatch = plugin.description?.toLowerCase().includes(term) || false;
const keywordsMatch = plugin.keywords?.some((keyword) => keyword.toLowerCase().includes(term)) || false;
return nameMatch || descriptionMatch || keywordsMatch;
});
};
/**
* Filter plugins by category
*/
export const filterPluginsByCategory = (
plugins: MarketplacePluginEntry[],
category: string,
): MarketplacePluginEntry[] => {
if (category === "All") {
return plugins;
}
if (category === "Other") {
return plugins.filter((p) => !p.category || p.category.trim() === "");
}
return plugins.filter((p) => p.category === category);
};
/**
* Validate semantic version format (basic check)
*/
@ -474,14 +384,3 @@ export const parseKeywords = (keywordsString: string): string[] => {
.map((kw) => kw.trim())
.filter((kw) => kw !== "");
};
/**
* Format keywords array to comma-separated string
*/
export const formatKeywords = (keywords?: string[]): string => {
if (!keywords || keywords.length === 0) {
return "";
}
return keywords.join(", ");
};

View file

@ -126,7 +126,6 @@ import { serverRootPath, setServerRootPath } from "@/lib/serverRootPath";
export { serverRootPath };
export { deriveErrorMessage };
export { ApiError } from "@/lib/http/client";
const isLocal = process.env.NODE_ENV === "development";
// In dev, if NEXT_PUBLIC_USE_REWRITES=true the Next.js dev server proxies API calls
@ -1115,37 +1114,6 @@ export const userGetInfoV2 = async (accessToken: string, userId?: string): Promi
}
};
export const userInfoCall = async (
accessToken: string,
userID: string | null,
userRole: string,
viewAll: boolean = false,
page: number | null,
page_size: number | null,
lookup_user_id: boolean = false,
) => {
try {
if (viewAll) {
return await apiClient.get(`/user/list`, {
accessToken,
query: {
page: page != null ? page.toString() : undefined,
page_size: page_size != null ? page_size.toString() : undefined,
},
});
}
const includeUserID = !((userRole === "Admin" || userRole === "Admin Viewer") && !lookup_user_id) && userID;
return await apiClient.get(`/user/info`, {
accessToken,
query: { user_id: includeUserID ? userID : undefined },
});
} catch (error) {
console.error("Failed to fetch user data:", error);
throw error;
}
};
export const teamInfoCall = async (accessToken: string, teamID: string | null) => {
try {
return await apiClient.get(`/team/info`, { accessToken, query: { team_id: teamID || undefined } });
@ -1155,44 +1123,6 @@ export const teamInfoCall = async (accessToken: string, teamID: string | null) =
}
};
type TeamListResponse = {
teams: Team[];
total: number;
page: number;
page_size: number;
total_pages: number;
};
export const v2TeamListCall = async (
accessToken: string,
organizationID: string | null,
userID: string | null = null,
teamID: string | null = null,
team_alias: string | null = null,
page: number = 1,
page_size: number = 10,
sort_by: string | null = null,
sort_order: "asc" | "desc" | null = null,
): Promise<TeamListResponse> => {
/**
* Get list of teams with filtering and sorting options
*/
try {
return await apiClient.get(`/v2/team/list`, {
accessToken,
query: {
user_id: userID || undefined,
organization_id: organizationID || undefined,
team_id: teamID || undefined,
team_alias: team_alias || undefined,
},
});
} catch (error) {
console.error("Failed to create key:", error);
throw error;
}
};
export const teamListCall = async (
accessToken: string,
organizationID: string | null,
@ -1283,25 +1213,6 @@ export const organizationInfoCall = async (accessToken: string, organizationID:
}
};
export const organizationUpdateCall = async (
accessToken: string,
formValues: Record<string, any>, // Assuming formValues is an object
) => {
try {
const data = await apiClient.patch(`/organization/update`, {
accessToken,
body: {
...formValues, // Include formValues in the request body
},
});
return data;
// Handle success - you might want to update some state or UI based on the created key
} catch (error) {
console.error("Failed to create key:", error);
throw error;
}
};
export const organizationDeleteCall = async (accessToken: string, organizationID: string) => {
try {
const url = proxyBaseUrl ? `${proxyBaseUrl}/organization/delete` : `/organization/delete`;
@ -2043,16 +1954,6 @@ export const allTagNamesCall = async (accessToken: string) => {
}
};
export const allEndUsersCall = async (accessToken: string) => {
try {
const data = await apiClient.get(`/customer/list`, { accessToken });
return data;
} catch (error) {
console.error("Failed to fetch end users:", error);
throw error;
}
};
export const userFilterUICall = async (accessToken: string, params: URLSearchParams) => {
try {
return await apiClient.get(`/user/filter/ui`, {
@ -2318,38 +2219,6 @@ export const adminTopModelsCall = async (accessToken: string) => {
}
};
export const keyInfoCall = async (accessToken: string, keys: string[]) => {
try {
let url = proxyBaseUrl ? `${proxyBaseUrl}/v2/key/info` : `/v2/key/info`;
const response = await fetch(url, {
method: "POST",
headers: {
[globalLitellmHeaderName]: `Bearer ${accessToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
keys: keys,
}),
});
if (!response.ok) {
const errorData = await response.text();
if (errorData.includes("Invalid proxy server token passed")) {
throw new Error("Invalid proxy server token passed");
}
handleError(errorData);
throw new Error("Network response was not ok");
}
const data = await response.json();
return data;
} catch (error) {
console.error("Failed to create key:", error);
throw error;
}
};
export const testConnectionRequest = async (
accessToken: string,
litellm_params: Record<string, any>,
@ -3320,19 +3189,6 @@ export const serviceHealthCheck = async (accessToken: string, service: string) =
}
};
export const getBudgetList = async (accessToken: string) => {
/**
* Get all configurable params for setting a budget
*/
try {
const data = await apiClient.get(`/budget/list`, { accessToken });
return data;
// Handle success - you might want to update some state or UI based on the created key
} catch (error) {
console.error("Failed to get callbacks:", error);
throw error;
}
};
export const getCallbacksCall = async (accessToken: string, userID: string, userRole: string) => {
/**
* Get all the models user has access to
@ -7346,37 +7202,6 @@ export const updateUserBanner = async (accessToken: string, banner: UserBannerUp
// Claude Code Marketplace Networking Functions
/**
* Get public marketplace catalog (no authentication required)
* Returns marketplace.json for Claude Code CLI discovery
*/
export const getClaudeCodeMarketplace = async () => {
try {
const proxyBaseUrl = getProxyBaseUrl();
const url = proxyBaseUrl ? `${proxyBaseUrl}/claude-code/marketplace.json` : `/claude-code/marketplace.json`;
const response = await fetch(url, {
method: "GET",
headers: {
"Content-Type": "application/json",
},
});
if (!response.ok) {
const errorData = await response.text();
const errorMessage = deriveErrorMessage(JSON.parse(errorData));
handleError(errorMessage);
throw new Error(errorMessage);
}
const data = await response.json();
return data;
} catch (error) {
console.error("Failed to fetch Claude Code marketplace:", error);
throw error;
}
};
/**
* List all Claude Code plugins (admin only)
* @param accessToken - Admin access token
@ -7412,41 +7237,6 @@ export const getClaudeCodePluginsList = async (accessToken: string, enabledOnly:
}
};
/**
* Get details for a specific Claude Code plugin (admin only)
* @param accessToken - Admin access token
* @param pluginName - Name of the plugin
*/
export const getClaudeCodePluginDetails = async (accessToken: string, pluginName: string) => {
try {
const proxyBaseUrl = getProxyBaseUrl();
const url = proxyBaseUrl
? `${proxyBaseUrl}/claude-code/plugins/${pluginName}`
: `/claude-code/plugins/${pluginName}`;
const response = await fetch(url, {
method: "GET",
headers: {
[globalLitellmHeaderName]: `Bearer ${accessToken}`,
"Content-Type": "application/json",
},
});
if (!response.ok) {
const errorData = await response.text();
const errorMessage = deriveErrorMessage(JSON.parse(errorData));
handleError(errorMessage);
throw new Error(errorMessage);
}
const data = await response.json();
return data;
} catch (error) {
console.error(`Failed to fetch plugin "${pluginName}":`, error);
throw error;
}
};
/**
* Register a new Claude Code plugin (admin only). Create-only: the proxy returns
* 409 if a plugin with the same name already exists.

View file

@ -21,7 +21,6 @@ vi.mock("../networking", () => {
organizationMemberAddCall: vi.fn(),
organizationMemberUpdateCall: vi.fn(),
organizationMemberDeleteCall: vi.fn(),
organizationUpdateCall: vi.fn(),
serverRootPath: "",
};
});

View file

@ -15,7 +15,6 @@ vi.mock("../networking", async (importOriginal) => {
...actual,
uiSpendLogsCall: vi.fn(),
keyInfoV1Call: vi.fn().mockResolvedValue({ info: {} }),
allEndUsersCall: vi.fn().mockResolvedValue([]),
};
});