@acarmisc/backstage-plugin-litellm-backend 0.29.0 → 0.30.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/config.d.ts +8 -0
- package/dist/index.cjs.js +37 -5
- package/dist/index.cjs.js.map +3 -3
- package/dist/keySnippets.d.ts +8 -0
- package/dist/keySnippets.js +26 -0
- package/dist/openapi.js +1 -0
- package/dist/router.js +4 -0
- package/dist/routes/config.js +1 -0
- package/dist/routes/context.d.ts +3 -0
- package/dist/services/keyService.js +9 -1
- package/dist/services/mcpAccess.d.ts +10 -2
- package/dist/services/mcpAccess.js +10 -3
- package/package.json +1 -1
package/config.d.ts
CHANGED
|
@@ -461,6 +461,14 @@ export interface Config {
|
|
|
461
461
|
* @default []
|
|
462
462
|
*/
|
|
463
463
|
serviceKeyPrefixes?: string[];
|
|
464
|
+
/**
|
|
465
|
+
* Starter-snippet tabs shown after a key is generated, in this order.
|
|
466
|
+
* Allowed: office365, opencode, claude-code, pi, openai, curl. Omit to
|
|
467
|
+
* show all of them in that order (Office365 first). Must not be empty,
|
|
468
|
+
* contain unknown names, or repeat a tab. Display-only.
|
|
469
|
+
* @visibility frontend
|
|
470
|
+
*/
|
|
471
|
+
keySnippets?: Array<'office365' | 'opencode' | 'claude-code' | 'pi' | 'openai' | 'curl'>;
|
|
464
472
|
};
|
|
465
473
|
|
|
466
474
|
/**
|
package/dist/index.cjs.js
CHANGED
|
@@ -9167,6 +9167,26 @@ async function resolveUserRole(userEntityRef, roleConfigs, catalogClient, auth,
|
|
|
9167
9167
|
}
|
|
9168
9168
|
}
|
|
9169
9169
|
|
|
9170
|
+
// src/keySnippets.ts
|
|
9171
|
+
var KEY_SNIPPET_TABS = ["office365", "opencode", "claude-code", "pi", "openai", "curl"];
|
|
9172
|
+
function parseKeySnippetTabs(raw) {
|
|
9173
|
+
if (raw === void 0) return [...KEY_SNIPPET_TABS];
|
|
9174
|
+
if (raw.length === 0) {
|
|
9175
|
+
throw new Error("litellm.display.keySnippets must list at least one tab");
|
|
9176
|
+
}
|
|
9177
|
+
const allowed = KEY_SNIPPET_TABS;
|
|
9178
|
+
const unknown = raw.filter((tab) => !allowed.includes(tab));
|
|
9179
|
+
if (unknown.length > 0) {
|
|
9180
|
+
throw new Error(
|
|
9181
|
+
`litellm.display.keySnippets: unknown tab(s) ${unknown.join(", ")}. Allowed: ${KEY_SNIPPET_TABS.join(", ")}`
|
|
9182
|
+
);
|
|
9183
|
+
}
|
|
9184
|
+
if (new Set(raw).size !== raw.length) {
|
|
9185
|
+
throw new Error("litellm.display.keySnippets lists a tab more than once");
|
|
9186
|
+
}
|
|
9187
|
+
return raw;
|
|
9188
|
+
}
|
|
9189
|
+
|
|
9170
9190
|
// src/router.ts
|
|
9171
9191
|
var import_backstage_plugin_litellm_common11 = require("@acarmisc/backstage-plugin-litellm-common");
|
|
9172
9192
|
|
|
@@ -9787,7 +9807,8 @@ var openApiSpec = {
|
|
|
9787
9807
|
display: { type: "object", properties: {
|
|
9788
9808
|
hideTeamBudgetForMembers: { type: "boolean" },
|
|
9789
9809
|
hideTeamBudgetForManagers: { type: "boolean" },
|
|
9790
|
-
serviceKeyPrefixes: { type: "array", items: { type: "string" } }
|
|
9810
|
+
serviceKeyPrefixes: { type: "array", items: { type: "string" } },
|
|
9811
|
+
keySnippets: { type: "array", items: { type: "string", enum: ["office365", "opencode", "claude-code", "pi", "openai", "curl"] } }
|
|
9791
9812
|
} }
|
|
9792
9813
|
} } } }
|
|
9793
9814
|
}
|
|
@@ -10056,7 +10077,8 @@ function registerConfigRoutes(router, ctx) {
|
|
|
10056
10077
|
display: {
|
|
10057
10078
|
hideTeamBudgetForMembers: ctx.teamBudgetVisibility.hideTeamBudgetForMembers,
|
|
10058
10079
|
hideTeamBudgetForManagers: ctx.teamBudgetVisibility.hideTeamBudgetForManagers,
|
|
10059
|
-
serviceKeyPrefixes: ctx.serviceKeyPrefixes
|
|
10080
|
+
serviceKeyPrefixes: ctx.serviceKeyPrefixes,
|
|
10081
|
+
keySnippets: ctx.keySnippets
|
|
10060
10082
|
}
|
|
10061
10083
|
});
|
|
10062
10084
|
});
|
|
@@ -10142,9 +10164,12 @@ function teamMcpServers(team) {
|
|
|
10142
10164
|
const listed = team?.object_permission?.mcp_servers ?? [];
|
|
10143
10165
|
return [...new Set(listed)].filter((n) => import_backstage_plugin_litellm_common3.MCP_SERVER_NAME_RE.test(n));
|
|
10144
10166
|
}
|
|
10145
|
-
function findDisallowedMcpServers(requested, allowed) {
|
|
10167
|
+
function findDisallowedMcpServers(requested, allowed, servers = []) {
|
|
10146
10168
|
const ok = new Set(allowed);
|
|
10147
|
-
|
|
10169
|
+
const granted = (n) => ok.has(n) || servers.some(
|
|
10170
|
+
(s) => (s.name === n || s.id === n) && (ok.has(s.id) || s.name !== void 0 && ok.has(s.name))
|
|
10171
|
+
);
|
|
10172
|
+
return [...new Set(requested)].filter((n) => !granted(n));
|
|
10148
10173
|
}
|
|
10149
10174
|
|
|
10150
10175
|
// src/services/keyService.ts
|
|
@@ -10231,7 +10256,12 @@ async function createKeyForUser(user, input, ctx) {
|
|
|
10231
10256
|
if (!input.team_id) {
|
|
10232
10257
|
throw new KeyServiceError(400, { error: "team_id is required when mcp_servers is set" });
|
|
10233
10258
|
}
|
|
10234
|
-
const
|
|
10259
|
+
const allowed = teamMcpServers(await getTeam(input.team_id));
|
|
10260
|
+
let disallowed = findDisallowedMcpServers(mcpServers, allowed);
|
|
10261
|
+
if (disallowed.length > 0) {
|
|
10262
|
+
const servers = await client.listMcpServers().catch(() => []);
|
|
10263
|
+
disallowed = findDisallowedMcpServers(mcpServers, allowed, servers);
|
|
10264
|
+
}
|
|
10235
10265
|
if (disallowed.length > 0) {
|
|
10236
10266
|
throw new KeyServiceError(403, {
|
|
10237
10267
|
error: "One or more requested MCP servers are not allowed",
|
|
@@ -13673,6 +13703,7 @@ async function createRouter(options) {
|
|
|
13673
13703
|
const publicBaseUrl = config.getOptionalString("litellm.publicBaseUrl") ?? null;
|
|
13674
13704
|
const supportContact = config.getOptionalString("litellm.supportContact");
|
|
13675
13705
|
const serviceKeyPrefixes = (config.getOptionalStringArray("litellm.display.serviceKeyPrefixes") ?? []).map((p) => p.trim()).filter((p) => p.length > 0);
|
|
13706
|
+
const keySnippets = parseKeySnippetTabs(config.getOptionalStringArray("litellm.display.keySnippets"));
|
|
13676
13707
|
const baseClient = options.client ?? new LiteLLMClient({ baseUrl, masterKey });
|
|
13677
13708
|
const userInfoCacheTtlSeconds = config.getOptionalNumber("litellm.cache.userInfoTtlSeconds") ?? 10;
|
|
13678
13709
|
const client = userInfoCacheTtlSeconds > 0 ? withUserInfoCache(baseClient, { ttlMs: userInfoCacheTtlSeconds * 1e3 }) : baseClient;
|
|
@@ -13732,6 +13763,7 @@ async function createRouter(options) {
|
|
|
13732
13763
|
publicBaseUrl,
|
|
13733
13764
|
supportContact,
|
|
13734
13765
|
serviceKeyPrefixes,
|
|
13766
|
+
keySnippets,
|
|
13735
13767
|
userIdDomain,
|
|
13736
13768
|
provisioningEnabled,
|
|
13737
13769
|
provisioningDefaults,
|