@acarmisc/backstage-plugin-litellm-common 0.4.0 → 0.5.0
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/dist/index.cjs.js +9 -0
- package/dist/index.cjs.js.map +2 -2
- package/dist/keySchemas.d.ts +5 -0
- package/dist/keySchemas.js +14 -1
- package/dist/types.d.ts +4 -0
- package/package.json +1 -1
package/dist/index.cjs.js
CHANGED
|
@@ -25,6 +25,7 @@ __export(index_exports, {
|
|
|
25
25
|
BUDGET_WARNING_PCT: () => BUDGET_WARNING_PCT,
|
|
26
26
|
DEFAULT_KEY_BUDGET_DURATIONS: () => DEFAULT_KEY_BUDGET_DURATIONS,
|
|
27
27
|
DEFAULT_KEY_DURATIONS: () => DEFAULT_KEY_DURATIONS,
|
|
28
|
+
MCP_SERVER_NAME_RE: () => MCP_SERVER_NAME_RE,
|
|
28
29
|
RESERVED_METADATA_KEYS: () => RESERVED_METADATA_KEYS,
|
|
29
30
|
budgetStatusForPct: () => budgetStatusForPct,
|
|
30
31
|
createGenerateKeyInputSchema: () => createGenerateKeyInputSchema,
|
|
@@ -128,6 +129,7 @@ function reservedMetadataKeys(metadata) {
|
|
|
128
129
|
(k) => RESERVED_METADATA_KEYS.includes(k) || RESERVED_METADATA_PREFIXES.some((p) => k.startsWith(p))
|
|
129
130
|
);
|
|
130
131
|
}
|
|
132
|
+
var MCP_SERVER_NAME_RE = /^[A-Za-z0-9._-]+$/;
|
|
131
133
|
function createGenerateKeyInputSchema(config) {
|
|
132
134
|
const durationSchema = config.allowedDurations.length > 0 ? import_zod.z.string().refine(
|
|
133
135
|
(val) => config.allowedDurations.includes(val),
|
|
@@ -155,6 +157,12 @@ function createGenerateKeyInputSchema(config) {
|
|
|
155
157
|
tpm_limit: import_zod.z.number().int("tpm_limit must be an integer").positive("tpm_limit must be a positive number").max(config.maxTpm, `tpm_limit must not exceed ${config.maxTpm}`).optional(),
|
|
156
158
|
rpm_limit: import_zod.z.number().int("rpm_limit must be an integer").positive("rpm_limit must be a positive number").max(config.maxRpm, `rpm_limit must not exceed ${config.maxRpm}`).optional(),
|
|
157
159
|
team_id: import_zod.z.string().optional(),
|
|
160
|
+
// MCP servers the key may use; must be a subset of the team's (checked
|
|
161
|
+
// server-side). [] denies every MCP server; omitted leaves LiteLLM's
|
|
162
|
+
// default. Names only, so the value is safe to forward as-is.
|
|
163
|
+
mcp_servers: import_zod.z.array(
|
|
164
|
+
import_zod.z.string().min(1).max(128).regex(MCP_SERVER_NAME_RE, 'mcp_servers entries may only use letters, digits, ".", "_" and "-"')
|
|
165
|
+
).max(50, "mcp_servers array is too large").optional(),
|
|
158
166
|
// The UI always sends 'llm_api'; no other key type may be requested.
|
|
159
167
|
key_type: import_zod.z.literal("llm_api").optional(),
|
|
160
168
|
metadata: import_zod.z.record(import_zod.z.string(), import_zod.z.string()).refine((m) => reservedMetadataKeys(m).length === 0, {
|
|
@@ -214,6 +222,7 @@ function isModelAllowed(model, allowed) {
|
|
|
214
222
|
BUDGET_WARNING_PCT,
|
|
215
223
|
DEFAULT_KEY_BUDGET_DURATIONS,
|
|
216
224
|
DEFAULT_KEY_DURATIONS,
|
|
225
|
+
MCP_SERVER_NAME_RE,
|
|
217
226
|
RESERVED_METADATA_KEYS,
|
|
218
227
|
budgetStatusForPct,
|
|
219
228
|
createGenerateKeyInputSchema,
|
package/dist/index.cjs.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.ts", "../src/permissions.ts", "../src/keySchemas.ts", "../src/budgets.ts", "../src/modelAccess.ts"],
|
|
4
|
-
"sourcesContent": ["export * from './permissions';\nexport * from './keySchemas';\nexport * from './types';\nexport * from './budgets';\nexport * from './modelAccess';\n", "import { createPermission } from '@backstage/plugin-permission-common';\n\nexport const litellmKeyCreatePermission = createPermission({\n name: 'litellm.key.create',\n attributes: { action: 'create' },\n});\n\nexport const litellmKeyRevokePermission = createPermission({\n name: 'litellm.key.revoke',\n attributes: { action: 'delete' },\n});\n\nexport const litellmKeyManagePermission = createPermission({\n name: 'litellm.key.manage',\n attributes: { action: 'update' },\n});\n\nexport const litellmKeyResetSpendPermission = createPermission({\n name: 'litellm.key.resetSpend',\n attributes: { action: 'update' },\n});\n\nexport const litellmKeyUnblockPermission = createPermission({\n name: 'litellm.key.unblock',\n attributes: { action: 'update' },\n});\n\nexport const litellmAuditReadPermission = createPermission({\n name: 'litellm.audit.read',\n attributes: { action: 'read' },\n});\n\n// Team-management permissions \u2014 delegated to litellm-team-admins group\nexport const litellmTeamCreatePermission = createPermission({\n name: 'litellm.team.create',\n attributes: { action: 'create' },\n});\n\nexport const litellmTeamManagePermission = createPermission({\n name: 'litellm.team.manage',\n attributes: { action: 'update' },\n});\n\nexport const litellmTeamMembersManagePermission = createPermission({\n name: 'litellm.team.members.manage',\n attributes: { action: 'update' },\n});\n\nexport const litellmTeamKnowledgebaseManagePermission = createPermission({\n name: 'litellm.team.knowledgebase.manage',\n attributes: { action: 'update' },\n});\n\nexport const litellmTeamMcpManagePermission = createPermission({\n name: 'litellm.team.mcp.manage',\n attributes: { action: 'update' },\n});\n\nexport const litellmTeamDeletePermission = createPermission({\n name: 'litellm.team.delete',\n attributes: { action: 'delete' },\n});\n\n/**\n * Read the per-member usage breakdown of a team the caller belongs to.\n * Only consulted when `litellm.teamUsage.memberBreakdown.enabled` is true and\n * the permission framework is enabled.\n */\nexport const litellmTeamUsageReadPermission = createPermission({\n name: 'litellm.team.usage.read',\n attributes: { action: 'read' },\n});\n\nexport const litellmPermissions = [\n litellmKeyCreatePermission,\n litellmKeyRevokePermission,\n litellmKeyManagePermission,\n litellmKeyResetSpendPermission,\n litellmKeyUnblockPermission,\n litellmAuditReadPermission,\n litellmTeamCreatePermission,\n litellmTeamManagePermission,\n litellmTeamMembersManagePermission,\n litellmTeamKnowledgebaseManagePermission,\n litellmTeamMcpManagePermission,\n litellmTeamDeletePermission,\n litellmTeamUsageReadPermission,\n];\n", "import { z } from 'zod';\n\n/**\n * Configuration for key validation ceilings and allowed values.\n */\n/**\n * Metadata keys the server owns. Clients must not set them: `blocked_by` /\n * `blocked_at` decide who may unblock a key, and `created_*` / `updated_*`\n * record provenance.\n */\nexport const RESERVED_METADATA_KEYS = ['blocked_by', 'blocked_at'] as const;\nconst RESERVED_METADATA_PREFIXES = ['created_', 'updated_'] as const;\n\nexport function reservedMetadataKeys(metadata: Record<string, unknown>): string[] {\n return Object.keys(metadata).filter(\n k =>\n (RESERVED_METADATA_KEYS as readonly string[]).includes(k) ||\n RESERVED_METADATA_PREFIXES.some(p => k.startsWith(p)),\n );\n}\n\nexport interface KeyValidationConfig {\n maxBudget: number;\n maxTpm: number;\n maxRpm: number;\n allowedDurations: string[];\n /**\n * Allowed `budget_duration` values (how often a key's spend resets). An\n * empty list allows any value matching `<n><s|m|h|d|mo>`. Omitted means\n * {@link DEFAULT_KEY_BUDGET_DURATIONS}.\n */\n allowedBudgetDurations?: string[];\n}\n\n/**\n * Creates a strict Zod schema for GenerateKeyInput validation.\n * Rejects unknown fields and enforces server-side ceilings.\n *\n * @param config Server configuration with ceiling values and allowed durations\n * @returns A Zod object schema that validates the input\n */\nexport function createGenerateKeyInputSchema(config: KeyValidationConfig) {\n // When allowedDurations is set, restrict to that list.\n // Otherwise, allow any string matching the duration format regex.\n const durationSchema = config.allowedDurations.length > 0\n ? z.string().refine(\n (val) => config.allowedDurations.includes(val),\n {\n message: `duration must be one of: ${config.allowedDurations.join(', ')}`,\n },\n )\n : z.string().regex(/^\\d+[smhdwy]$/, {\n message: 'duration must match format <number><unit> (e.g. \"30d\", \"1h\")',\n });\n\n const budgetDurations = config.allowedBudgetDurations ?? DEFAULT_KEY_BUDGET_DURATIONS;\n const budgetDurationSchema = budgetDurations.length > 0\n ? z.string().refine(\n (val) => budgetDurations.includes(val),\n {\n message: `budget_duration must be one of: ${budgetDurations.join(', ')}`,\n },\n )\n : z.string().regex(/^\\d+(s|m|h|d|mo)$/, {\n message: 'budget_duration must match format <number><unit> (e.g. \"30d\", \"1mo\")',\n });\n\n return z\n .object({\n alias: z\n .string()\n .trim()\n .min(1, 'alias is required')\n .max(128, 'alias must be at most 128 characters'),\n models: z\n .array(z.string())\n .max(100, 'models array is too large')\n .optional(),\n duration: durationSchema.optional(),\n max_budget: z\n .number()\n .positive('max_budget must be a positive number')\n .max(config.maxBudget, `max_budget must not exceed ${config.maxBudget}`)\n .nullable()\n .optional(),\n budget_duration: budgetDurationSchema.optional(),\n tpm_limit: z\n .number()\n .int('tpm_limit must be an integer')\n .positive('tpm_limit must be a positive number')\n .max(config.maxTpm, `tpm_limit must not exceed ${config.maxTpm}`)\n .optional(),\n rpm_limit: z\n .number()\n .int('rpm_limit must be an integer')\n .positive('rpm_limit must be a positive number')\n .max(config.maxRpm, `rpm_limit must not exceed ${config.maxRpm}`)\n .optional(),\n team_id: z.string().optional(),\n // The UI always sends 'llm_api'; no other key type may be requested.\n key_type: z.literal('llm_api').optional(),\n metadata: z\n .record(z.string(), z.string())\n .refine(m => reservedMetadataKeys(m).length === 0, {\n message:\n 'metadata may not set server-owned keys (blocked_by, blocked_at, created_*, updated_*)',\n })\n .optional(),\n })\n .strict();\n}\n\nexport type GenerateKeyInput = z.infer<\n ReturnType<typeof createGenerateKeyInputSchema>\n>;\n\n/**\n * Creates a strict Zod schema for UpdateKeyInput validation.\n * Only allows key_alias, models, max_budget, tpm_limit, and rpm_limit fields.\n * Rejects all other fields including team_id, user_id, spend, blocked, key, and budget_duration.\n *\n * @param config Server configuration with ceiling values\n * @returns A Zod object schema that validates the input\n */\nexport function createUpdateKeyInputSchema(config: KeyValidationConfig) {\n return z\n .object({\n key_alias: z\n .string()\n .trim()\n .min(1, 'key_alias is required')\n .max(128, 'key_alias must be at most 128 characters')\n .optional(),\n models: z\n .array(z.string())\n .max(100, 'models array is too large')\n .optional(),\n max_budget: z\n .number()\n .positive('max_budget must be a positive number')\n .max(config.maxBudget, `max_budget must not exceed ${config.maxBudget}`)\n .nullable()\n .optional(),\n tpm_limit: z\n .number()\n .int('tpm_limit must be an integer')\n .positive('tpm_limit must be a positive number')\n .max(config.maxTpm, `tpm_limit must not exceed ${config.maxTpm}`)\n .optional(),\n rpm_limit: z\n .number()\n .int('rpm_limit must be an integer')\n .positive('rpm_limit must be a positive number')\n .max(config.maxRpm, `rpm_limit must not exceed ${config.maxRpm}`)\n .optional(),\n })\n .strict();\n}\n\nexport type UpdateKeyInput = z.infer<\n ReturnType<typeof createUpdateKeyInputSchema>\n>;\n\n/**\n * Default key duration presets. Used as server-side defaults and UI options.\n */\nexport const DEFAULT_KEY_DURATIONS = ['1d', '7d', '30d', '90d'];\n\n/**\n * Default `budget_duration` values a key may be generated with.\n */\nexport const DEFAULT_KEY_BUDGET_DURATIONS = ['1d', '7d', '30d', '1mo'];\n", "/** Budget warning threshold (percent of cap). */\nexport const BUDGET_WARNING_PCT = 80;\n\n/** Budget danger threshold (percent of cap, at or over cap). */\nexport const BUDGET_DANGER_PCT = 100;\n\n/** Consumption status derived from spend vs. cap. */\nexport type BudgetStatus = 'ok' | 'near' | 'over';\n\n/**\n * Compute budget status from spend percentage of cap.\n * Thresholds: near >= 80%, over >= 100%.\n */\nexport function budgetStatusForPct(pct: number): BudgetStatus {\n if (pct >= BUDGET_DANGER_PCT) return 'over';\n if (pct >= BUDGET_WARNING_PCT) return 'near';\n return 'ok';\n}\n\n/**\n * Format a duration string to a human-readable label.\n * E.g., \"1d\" \u2192 \"1 Day\", \"7d\" \u2192 \"7 Days\".\n */\nexport function formatDurationLabel(duration: string): string {\n const match = /^(\\d+)([smhdwy])$/.exec(duration);\n if (!match) return duration;\n\n const [, num, unit] = match;\n const n = Number(num);\n\n const unitLabels: Record<string, string> = {\n s: 'Second',\n m: 'Minute',\n h: 'Hour',\n d: 'Day',\n w: 'Week',\n y: 'Year',\n };\n\n const label = unitLabels[unit] || unit;\n return n === 1 ? `1 ${label}` : `${n} ${label}s`;\n}\n", "/**\n * LiteLLM sentinel: a team's (or user's) `models` list is `[\"all-proxy-models\"]`\n * when it isn't restricted to specific models.\n */\nexport const ALL_PROXY_MODELS = 'all-proxy-models';\n\n/**\n * Whether a model is allowed by an allow-list. The list is unrestricted when it\n * is empty/missing or contains the `all-proxy-models` sentinel. Entries may be\n * literal model names OR access-group names (litellm model_info.access_groups),\n * so a model also passes when one of its access groups is listed.\n */\nexport function isModelAllowed(\n model: { model_name: string; access_groups?: string[] },\n allowed?: string[],\n): boolean {\n if (!allowed || allowed.length === 0) return true;\n if (allowed.includes(ALL_PROXY_MODELS)) return true;\n if (allowed.includes(model.model_name)) return true;\n return !!model.access_groups?.some(group => allowed.includes(group));\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,sCAAiC;AAE1B,IAAM,iCAA6B,kDAAiB;AAAA,EACzD,MAAM;AAAA,EACN,YAAY,EAAE,QAAQ,SAAS;AACjC,CAAC;AAEM,IAAM,iCAA6B,kDAAiB;AAAA,EACzD,MAAM;AAAA,EACN,YAAY,EAAE,QAAQ,SAAS;AACjC,CAAC;AAEM,IAAM,iCAA6B,kDAAiB;AAAA,EACzD,MAAM;AAAA,EACN,YAAY,EAAE,QAAQ,SAAS;AACjC,CAAC;AAEM,IAAM,qCAAiC,kDAAiB;AAAA,EAC7D,MAAM;AAAA,EACN,YAAY,EAAE,QAAQ,SAAS;AACjC,CAAC;AAEM,IAAM,kCAA8B,kDAAiB;AAAA,EAC1D,MAAM;AAAA,EACN,YAAY,EAAE,QAAQ,SAAS;AACjC,CAAC;AAEM,IAAM,iCAA6B,kDAAiB;AAAA,EACzD,MAAM;AAAA,EACN,YAAY,EAAE,QAAQ,OAAO;AAC/B,CAAC;AAGM,IAAM,kCAA8B,kDAAiB;AAAA,EAC1D,MAAM;AAAA,EACN,YAAY,EAAE,QAAQ,SAAS;AACjC,CAAC;AAEM,IAAM,kCAA8B,kDAAiB;AAAA,EAC1D,MAAM;AAAA,EACN,YAAY,EAAE,QAAQ,SAAS;AACjC,CAAC;AAEM,IAAM,yCAAqC,kDAAiB;AAAA,EACjE,MAAM;AAAA,EACN,YAAY,EAAE,QAAQ,SAAS;AACjC,CAAC;AAEM,IAAM,+CAA2C,kDAAiB;AAAA,EACvE,MAAM;AAAA,EACN,YAAY,EAAE,QAAQ,SAAS;AACjC,CAAC;AAEM,IAAM,qCAAiC,kDAAiB;AAAA,EAC7D,MAAM;AAAA,EACN,YAAY,EAAE,QAAQ,SAAS;AACjC,CAAC;AAEM,IAAM,kCAA8B,kDAAiB;AAAA,EAC1D,MAAM;AAAA,EACN,YAAY,EAAE,QAAQ,SAAS;AACjC,CAAC;AAOM,IAAM,qCAAiC,kDAAiB;AAAA,EAC7D,MAAM;AAAA,EACN,YAAY,EAAE,QAAQ,OAAO;AAC/B,CAAC;AAEM,IAAM,qBAAqB;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;;;ACvFA,iBAAkB;AAUX,IAAM,yBAAyB,CAAC,cAAc,YAAY;AACjE,IAAM,6BAA6B,CAAC,YAAY,UAAU;AAEnD,SAAS,qBAAqB,UAA6C;AAChF,SAAO,OAAO,KAAK,QAAQ,EAAE;AAAA,IAC3B,OACG,uBAA6C,SAAS,CAAC,KACxD,2BAA2B,KAAK,OAAK,EAAE,WAAW,CAAC,CAAC;AAAA,EACxD;AACF;
|
|
4
|
+
"sourcesContent": ["export * from './permissions';\nexport * from './keySchemas';\nexport * from './types';\nexport * from './budgets';\nexport * from './modelAccess';\n", "import { createPermission } from '@backstage/plugin-permission-common';\n\nexport const litellmKeyCreatePermission = createPermission({\n name: 'litellm.key.create',\n attributes: { action: 'create' },\n});\n\nexport const litellmKeyRevokePermission = createPermission({\n name: 'litellm.key.revoke',\n attributes: { action: 'delete' },\n});\n\nexport const litellmKeyManagePermission = createPermission({\n name: 'litellm.key.manage',\n attributes: { action: 'update' },\n});\n\nexport const litellmKeyResetSpendPermission = createPermission({\n name: 'litellm.key.resetSpend',\n attributes: { action: 'update' },\n});\n\nexport const litellmKeyUnblockPermission = createPermission({\n name: 'litellm.key.unblock',\n attributes: { action: 'update' },\n});\n\nexport const litellmAuditReadPermission = createPermission({\n name: 'litellm.audit.read',\n attributes: { action: 'read' },\n});\n\n// Team-management permissions \u2014 delegated to litellm-team-admins group\nexport const litellmTeamCreatePermission = createPermission({\n name: 'litellm.team.create',\n attributes: { action: 'create' },\n});\n\nexport const litellmTeamManagePermission = createPermission({\n name: 'litellm.team.manage',\n attributes: { action: 'update' },\n});\n\nexport const litellmTeamMembersManagePermission = createPermission({\n name: 'litellm.team.members.manage',\n attributes: { action: 'update' },\n});\n\nexport const litellmTeamKnowledgebaseManagePermission = createPermission({\n name: 'litellm.team.knowledgebase.manage',\n attributes: { action: 'update' },\n});\n\nexport const litellmTeamMcpManagePermission = createPermission({\n name: 'litellm.team.mcp.manage',\n attributes: { action: 'update' },\n});\n\nexport const litellmTeamDeletePermission = createPermission({\n name: 'litellm.team.delete',\n attributes: { action: 'delete' },\n});\n\n/**\n * Read the per-member usage breakdown of a team the caller belongs to.\n * Only consulted when `litellm.teamUsage.memberBreakdown.enabled` is true and\n * the permission framework is enabled.\n */\nexport const litellmTeamUsageReadPermission = createPermission({\n name: 'litellm.team.usage.read',\n attributes: { action: 'read' },\n});\n\nexport const litellmPermissions = [\n litellmKeyCreatePermission,\n litellmKeyRevokePermission,\n litellmKeyManagePermission,\n litellmKeyResetSpendPermission,\n litellmKeyUnblockPermission,\n litellmAuditReadPermission,\n litellmTeamCreatePermission,\n litellmTeamManagePermission,\n litellmTeamMembersManagePermission,\n litellmTeamKnowledgebaseManagePermission,\n litellmTeamMcpManagePermission,\n litellmTeamDeletePermission,\n litellmTeamUsageReadPermission,\n];\n", "import { z } from 'zod';\n\n/**\n * Configuration for key validation ceilings and allowed values.\n */\n/**\n * Metadata keys the server owns. Clients must not set them: `blocked_by` /\n * `blocked_at` decide who may unblock a key, and `created_*` / `updated_*`\n * record provenance.\n */\nexport const RESERVED_METADATA_KEYS = ['blocked_by', 'blocked_at'] as const;\nconst RESERVED_METADATA_PREFIXES = ['created_', 'updated_'] as const;\n\nexport function reservedMetadataKeys(metadata: Record<string, unknown>): string[] {\n return Object.keys(metadata).filter(\n k =>\n (RESERVED_METADATA_KEYS as readonly string[]).includes(k) ||\n RESERVED_METADATA_PREFIXES.some(p => k.startsWith(p)),\n );\n}\n\nexport interface KeyValidationConfig {\n maxBudget: number;\n maxTpm: number;\n maxRpm: number;\n allowedDurations: string[];\n /**\n * Allowed `budget_duration` values (how often a key's spend resets). An\n * empty list allows any value matching `<n><s|m|h|d|mo>`. Omitted means\n * {@link DEFAULT_KEY_BUDGET_DURATIONS}.\n */\n allowedBudgetDurations?: string[];\n}\n\n/** Allowed characters in an MCP server name sent to or listed by the bridge. */\nexport const MCP_SERVER_NAME_RE = /^[A-Za-z0-9._-]+$/;\n\n/**\n * Creates a strict Zod schema for GenerateKeyInput validation.\n * Rejects unknown fields and enforces server-side ceilings.\n *\n * @param config Server configuration with ceiling values and allowed durations\n * @returns A Zod object schema that validates the input\n */\nexport function createGenerateKeyInputSchema(config: KeyValidationConfig) {\n // When allowedDurations is set, restrict to that list.\n // Otherwise, allow any string matching the duration format regex.\n const durationSchema = config.allowedDurations.length > 0\n ? z.string().refine(\n (val) => config.allowedDurations.includes(val),\n {\n message: `duration must be one of: ${config.allowedDurations.join(', ')}`,\n },\n )\n : z.string().regex(/^\\d+[smhdwy]$/, {\n message: 'duration must match format <number><unit> (e.g. \"30d\", \"1h\")',\n });\n\n const budgetDurations = config.allowedBudgetDurations ?? DEFAULT_KEY_BUDGET_DURATIONS;\n const budgetDurationSchema = budgetDurations.length > 0\n ? z.string().refine(\n (val) => budgetDurations.includes(val),\n {\n message: `budget_duration must be one of: ${budgetDurations.join(', ')}`,\n },\n )\n : z.string().regex(/^\\d+(s|m|h|d|mo)$/, {\n message: 'budget_duration must match format <number><unit> (e.g. \"30d\", \"1mo\")',\n });\n\n return z\n .object({\n alias: z\n .string()\n .trim()\n .min(1, 'alias is required')\n .max(128, 'alias must be at most 128 characters'),\n models: z\n .array(z.string())\n .max(100, 'models array is too large')\n .optional(),\n duration: durationSchema.optional(),\n max_budget: z\n .number()\n .positive('max_budget must be a positive number')\n .max(config.maxBudget, `max_budget must not exceed ${config.maxBudget}`)\n .nullable()\n .optional(),\n budget_duration: budgetDurationSchema.optional(),\n tpm_limit: z\n .number()\n .int('tpm_limit must be an integer')\n .positive('tpm_limit must be a positive number')\n .max(config.maxTpm, `tpm_limit must not exceed ${config.maxTpm}`)\n .optional(),\n rpm_limit: z\n .number()\n .int('rpm_limit must be an integer')\n .positive('rpm_limit must be a positive number')\n .max(config.maxRpm, `rpm_limit must not exceed ${config.maxRpm}`)\n .optional(),\n team_id: z.string().optional(),\n // MCP servers the key may use; must be a subset of the team's (checked\n // server-side). [] denies every MCP server; omitted leaves LiteLLM's\n // default. Names only, so the value is safe to forward as-is.\n mcp_servers: z\n .array(\n z\n .string()\n .min(1)\n .max(128)\n .regex(MCP_SERVER_NAME_RE, 'mcp_servers entries may only use letters, digits, \".\", \"_\" and \"-\"'),\n )\n .max(50, 'mcp_servers array is too large')\n .optional(),\n // The UI always sends 'llm_api'; no other key type may be requested.\n key_type: z.literal('llm_api').optional(),\n metadata: z\n .record(z.string(), z.string())\n .refine(m => reservedMetadataKeys(m).length === 0, {\n message:\n 'metadata may not set server-owned keys (blocked_by, blocked_at, created_*, updated_*)',\n })\n .optional(),\n })\n .strict();\n}\n\nexport type GenerateKeyInput = z.infer<\n ReturnType<typeof createGenerateKeyInputSchema>\n>;\n\n/**\n * Creates a strict Zod schema for UpdateKeyInput validation.\n * Only allows key_alias, models, max_budget, tpm_limit, and rpm_limit fields.\n * Rejects all other fields including team_id, user_id, spend, blocked, key, and budget_duration.\n *\n * @param config Server configuration with ceiling values\n * @returns A Zod object schema that validates the input\n */\nexport function createUpdateKeyInputSchema(config: KeyValidationConfig) {\n return z\n .object({\n key_alias: z\n .string()\n .trim()\n .min(1, 'key_alias is required')\n .max(128, 'key_alias must be at most 128 characters')\n .optional(),\n models: z\n .array(z.string())\n .max(100, 'models array is too large')\n .optional(),\n max_budget: z\n .number()\n .positive('max_budget must be a positive number')\n .max(config.maxBudget, `max_budget must not exceed ${config.maxBudget}`)\n .nullable()\n .optional(),\n tpm_limit: z\n .number()\n .int('tpm_limit must be an integer')\n .positive('tpm_limit must be a positive number')\n .max(config.maxTpm, `tpm_limit must not exceed ${config.maxTpm}`)\n .optional(),\n rpm_limit: z\n .number()\n .int('rpm_limit must be an integer')\n .positive('rpm_limit must be a positive number')\n .max(config.maxRpm, `rpm_limit must not exceed ${config.maxRpm}`)\n .optional(),\n })\n .strict();\n}\n\nexport type UpdateKeyInput = z.infer<\n ReturnType<typeof createUpdateKeyInputSchema>\n>;\n\n/**\n * Default key duration presets. Used as server-side defaults and UI options.\n */\nexport const DEFAULT_KEY_DURATIONS = ['1d', '7d', '30d', '90d'];\n\n/**\n * Default `budget_duration` values a key may be generated with.\n */\nexport const DEFAULT_KEY_BUDGET_DURATIONS = ['1d', '7d', '30d', '1mo'];\n", "/** Budget warning threshold (percent of cap). */\nexport const BUDGET_WARNING_PCT = 80;\n\n/** Budget danger threshold (percent of cap, at or over cap). */\nexport const BUDGET_DANGER_PCT = 100;\n\n/** Consumption status derived from spend vs. cap. */\nexport type BudgetStatus = 'ok' | 'near' | 'over';\n\n/**\n * Compute budget status from spend percentage of cap.\n * Thresholds: near >= 80%, over >= 100%.\n */\nexport function budgetStatusForPct(pct: number): BudgetStatus {\n if (pct >= BUDGET_DANGER_PCT) return 'over';\n if (pct >= BUDGET_WARNING_PCT) return 'near';\n return 'ok';\n}\n\n/**\n * Format a duration string to a human-readable label.\n * E.g., \"1d\" \u2192 \"1 Day\", \"7d\" \u2192 \"7 Days\".\n */\nexport function formatDurationLabel(duration: string): string {\n const match = /^(\\d+)([smhdwy])$/.exec(duration);\n if (!match) return duration;\n\n const [, num, unit] = match;\n const n = Number(num);\n\n const unitLabels: Record<string, string> = {\n s: 'Second',\n m: 'Minute',\n h: 'Hour',\n d: 'Day',\n w: 'Week',\n y: 'Year',\n };\n\n const label = unitLabels[unit] || unit;\n return n === 1 ? `1 ${label}` : `${n} ${label}s`;\n}\n", "/**\n * LiteLLM sentinel: a team's (or user's) `models` list is `[\"all-proxy-models\"]`\n * when it isn't restricted to specific models.\n */\nexport const ALL_PROXY_MODELS = 'all-proxy-models';\n\n/**\n * Whether a model is allowed by an allow-list. The list is unrestricted when it\n * is empty/missing or contains the `all-proxy-models` sentinel. Entries may be\n * literal model names OR access-group names (litellm model_info.access_groups),\n * so a model also passes when one of its access groups is listed.\n */\nexport function isModelAllowed(\n model: { model_name: string; access_groups?: string[] },\n allowed?: string[],\n): boolean {\n if (!allowed || allowed.length === 0) return true;\n if (allowed.includes(ALL_PROXY_MODELS)) return true;\n if (allowed.includes(model.model_name)) return true;\n return !!model.access_groups?.some(group => allowed.includes(group));\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/keySchemas.d.ts
CHANGED
|
@@ -21,6 +21,8 @@ export interface KeyValidationConfig {
|
|
|
21
21
|
*/
|
|
22
22
|
allowedBudgetDurations?: string[];
|
|
23
23
|
}
|
|
24
|
+
/** Allowed characters in an MCP server name sent to or listed by the bridge. */
|
|
25
|
+
export declare const MCP_SERVER_NAME_RE: RegExp;
|
|
24
26
|
/**
|
|
25
27
|
* Creates a strict Zod schema for GenerateKeyInput validation.
|
|
26
28
|
* Rejects unknown fields and enforces server-side ceilings.
|
|
@@ -37,6 +39,7 @@ export declare function createGenerateKeyInputSchema(config: KeyValidationConfig
|
|
|
37
39
|
tpm_limit: z.ZodOptional<z.ZodNumber>;
|
|
38
40
|
rpm_limit: z.ZodOptional<z.ZodNumber>;
|
|
39
41
|
team_id: z.ZodOptional<z.ZodString>;
|
|
42
|
+
mcp_servers: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
40
43
|
key_type: z.ZodOptional<z.ZodLiteral<"llm_api">>;
|
|
41
44
|
metadata: z.ZodOptional<z.ZodEffects<z.ZodRecord<z.ZodString, z.ZodString>, Record<string, string>, Record<string, string>>>;
|
|
42
45
|
}, "strict", z.ZodTypeAny, {
|
|
@@ -48,6 +51,7 @@ export declare function createGenerateKeyInputSchema(config: KeyValidationConfig
|
|
|
48
51
|
tpm_limit?: number | undefined;
|
|
49
52
|
rpm_limit?: number | undefined;
|
|
50
53
|
team_id?: string | undefined;
|
|
54
|
+
mcp_servers?: string[] | undefined;
|
|
51
55
|
key_type?: "llm_api" | undefined;
|
|
52
56
|
metadata?: Record<string, string> | undefined;
|
|
53
57
|
}, {
|
|
@@ -59,6 +63,7 @@ export declare function createGenerateKeyInputSchema(config: KeyValidationConfig
|
|
|
59
63
|
tpm_limit?: number | undefined;
|
|
60
64
|
rpm_limit?: number | undefined;
|
|
61
65
|
team_id?: string | undefined;
|
|
66
|
+
mcp_servers?: string[] | undefined;
|
|
62
67
|
key_type?: "llm_api" | undefined;
|
|
63
68
|
metadata?: Record<string, string> | undefined;
|
|
64
69
|
}>;
|
package/dist/keySchemas.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DEFAULT_KEY_BUDGET_DURATIONS = exports.DEFAULT_KEY_DURATIONS = exports.RESERVED_METADATA_KEYS = void 0;
|
|
3
|
+
exports.DEFAULT_KEY_BUDGET_DURATIONS = exports.DEFAULT_KEY_DURATIONS = exports.MCP_SERVER_NAME_RE = exports.RESERVED_METADATA_KEYS = void 0;
|
|
4
4
|
exports.reservedMetadataKeys = reservedMetadataKeys;
|
|
5
5
|
exports.createGenerateKeyInputSchema = createGenerateKeyInputSchema;
|
|
6
6
|
exports.createUpdateKeyInputSchema = createUpdateKeyInputSchema;
|
|
@@ -19,6 +19,8 @@ function reservedMetadataKeys(metadata) {
|
|
|
19
19
|
return Object.keys(metadata).filter(k => exports.RESERVED_METADATA_KEYS.includes(k) ||
|
|
20
20
|
RESERVED_METADATA_PREFIXES.some(p => k.startsWith(p)));
|
|
21
21
|
}
|
|
22
|
+
/** Allowed characters in an MCP server name sent to or listed by the bridge. */
|
|
23
|
+
exports.MCP_SERVER_NAME_RE = /^[A-Za-z0-9._-]+$/;
|
|
22
24
|
/**
|
|
23
25
|
* Creates a strict Zod schema for GenerateKeyInput validation.
|
|
24
26
|
* Rejects unknown fields and enforces server-side ceilings.
|
|
@@ -76,6 +78,17 @@ function createGenerateKeyInputSchema(config) {
|
|
|
76
78
|
.max(config.maxRpm, `rpm_limit must not exceed ${config.maxRpm}`)
|
|
77
79
|
.optional(),
|
|
78
80
|
team_id: zod_1.z.string().optional(),
|
|
81
|
+
// MCP servers the key may use; must be a subset of the team's (checked
|
|
82
|
+
// server-side). [] denies every MCP server; omitted leaves LiteLLM's
|
|
83
|
+
// default. Names only, so the value is safe to forward as-is.
|
|
84
|
+
mcp_servers: zod_1.z
|
|
85
|
+
.array(zod_1.z
|
|
86
|
+
.string()
|
|
87
|
+
.min(1)
|
|
88
|
+
.max(128)
|
|
89
|
+
.regex(exports.MCP_SERVER_NAME_RE, 'mcp_servers entries may only use letters, digits, ".", "_" and "-"'))
|
|
90
|
+
.max(50, 'mcp_servers array is too large')
|
|
91
|
+
.optional(),
|
|
79
92
|
// The UI always sends 'llm_api'; no other key type may be requested.
|
|
80
93
|
key_type: zod_1.z.literal('llm_api').optional(),
|
|
81
94
|
metadata: zod_1.z
|
package/dist/types.d.ts
CHANGED
|
@@ -192,6 +192,10 @@ export interface GenerateKeyRequest {
|
|
|
192
192
|
rpm_limit?: number;
|
|
193
193
|
key_type?: string;
|
|
194
194
|
metadata?: Record<string, string>;
|
|
195
|
+
/** Backend-only: MCP permissions forwarded to LiteLLM, built from the validated `mcp_servers`. */
|
|
196
|
+
object_permission?: {
|
|
197
|
+
mcp_servers?: string[];
|
|
198
|
+
};
|
|
195
199
|
/** Backend-only: user ID for the key owner (set from authenticated principal). */
|
|
196
200
|
user_id?: string;
|
|
197
201
|
}
|