@acarmisc/backstage-plugin-litellm 0.32.0 → 0.34.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.esm.js CHANGED
@@ -1823,8 +1823,6 @@ var init_KeysTable = __esm({
1823
1823
  setPruneSubmitting(true);
1824
1824
  try {
1825
1825
  await onPruneExpiredKeys();
1826
- } catch (error) {
1827
- console.error("Failed to prune expired keys:", error);
1828
1826
  } finally {
1829
1827
  setPruneSubmitting(false);
1830
1828
  setPruneConfirmCount(null);
@@ -1893,7 +1891,8 @@ var init_KeysTable = __esm({
1893
1891
  bgcolor: alpha7(theme.palette.text.primary, theme.palette.mode === "dark" ? 0.08 : 0.05),
1894
1892
  px: 0.75,
1895
1893
  py: 0.375,
1896
- borderRadius: "5px"
1894
+ borderRadius: "5px",
1895
+ whiteSpace: "nowrap"
1897
1896
  }),
1898
1897
  children: shortKeyId(keyId)
1899
1898
  }
@@ -3184,6 +3183,7 @@ var init_ManageTeamDialog = __esm({
3184
3183
  const [memberBudget, setMemberBudget] = useState6("");
3185
3184
  const [memberBusy, setMemberBusy] = useState6(false);
3186
3185
  const [memberError, setMemberError] = useState6(null);
3186
+ const [pendingMembers, setPendingMembers] = useState6([]);
3187
3187
  const [kbIds, setKbIds] = useState6([]);
3188
3188
  const [kbBusy, setKbBusy] = useState6(false);
3189
3189
  const [kbError, setKbError] = useState6(null);
@@ -3194,8 +3194,17 @@ var init_ManageTeamDialog = __esm({
3194
3194
  const maxBudgetCeiling = config?.teamManagement?.maxBudgetCeiling ?? FALLBACK_BUDGET_CEILING;
3195
3195
  const hideBudgetForManagers = config?.display?.hideTeamBudgetForManagers ?? false;
3196
3196
  const budgetWriteOnly = hideBudgetForManagers && mode === "edit";
3197
- const members = useMemo4(() => team?.members_with_roles ?? [], [team]);
3198
- const showMembers = mode === "edit" && !!canManageMembers || membersOnly;
3197
+ const creating = mode === "create";
3198
+ const members = useMemo4(
3199
+ () => creating ? pendingMembers.map((p) => ({
3200
+ user_id: p.userEntityRef,
3201
+ user_email: p.label,
3202
+ role: "user"
3203
+ })) : team?.members_with_roles ?? [],
3204
+ [creating, pendingMembers, team]
3205
+ );
3206
+ const showMembers = !!canManageMembers || !!membersOnly;
3207
+ const wide = showMembers && !membersOnly;
3199
3208
  const catalogApi = useApi3(catalogApiRef);
3200
3209
  const [searchUsers, setSearchUsers] = useState6([]);
3201
3210
  const [usersLoading, setUsersLoading] = useState6(false);
@@ -3240,8 +3249,8 @@ var init_ManageTeamDialog = __esm({
3240
3249
  email,
3241
3250
  label: email ? `${displayName} (${email})` : displayName
3242
3251
  };
3243
- }).filter((o) => !(o.email && existingEmails.has(o.email.toLowerCase()))).sort((a, b) => a.label.localeCompare(b.label));
3244
- }, [searchUsers, members]);
3252
+ }).filter((o) => !(o.email && existingEmails.has(o.email.toLowerCase()))).filter((o) => !pendingMembers.some((p) => p.userEntityRef === o.ref)).sort((a, b) => a.label.localeCompare(b.label));
3253
+ }, [searchUsers, members, pendingMembers]);
3245
3254
  useEffect4(() => {
3246
3255
  return () => {
3247
3256
  if (debounceTimerRef.current) {
@@ -3275,6 +3284,7 @@ var init_ManageTeamDialog = __esm({
3275
3284
  setMemberRef("");
3276
3285
  setMemberBudget("");
3277
3286
  setMemberError(null);
3287
+ setPendingMembers([]);
3278
3288
  setKbIds(team?.object_permission?.vector_stores ?? []);
3279
3289
  setKbError(null);
3280
3290
  setMcpIds(team?.object_permission?.mcp_servers ?? []);
@@ -3333,7 +3343,13 @@ var init_ManageTeamDialog = __esm({
3333
3343
  ...budgetDuration && { budget_duration: budgetDuration }
3334
3344
  };
3335
3345
  }
3336
- await onSubmit(payload);
3346
+ await onSubmit(
3347
+ payload,
3348
+ creating ? pendingMembers.map(({ userEntityRef, maxBudgetInTeam }) => ({
3349
+ userEntityRef,
3350
+ ...maxBudgetInTeam !== void 0 && { maxBudgetInTeam }
3351
+ })) : void 0
3352
+ );
3337
3353
  onClose();
3338
3354
  } catch (err) {
3339
3355
  setError(err instanceof Error ? err.message : "An error occurred");
@@ -3342,13 +3358,26 @@ var init_ManageTeamDialog = __esm({
3342
3358
  }
3343
3359
  };
3344
3360
  const handleAddMember = async () => {
3345
- if (!onAddMember || !memberRef.trim()) return;
3361
+ if (!memberRef.trim() || !creating && !onAddMember) return;
3346
3362
  setMemberError(null);
3347
3363
  const budget = !membersOnly && memberBudget ? parseFloat(memberBudget) : void 0;
3348
3364
  if (budget !== void 0 && (isNaN(budget) || budget <= 0)) {
3349
3365
  setMemberError("Max budget in team must be a positive number");
3350
3366
  return;
3351
3367
  }
3368
+ if (creating) {
3369
+ const ref = memberRef.trim();
3370
+ if (!pendingMembers.some((p) => p.userEntityRef === ref)) {
3371
+ setPendingMembers((list) => [
3372
+ ...list,
3373
+ { userEntityRef: ref, label: memberQuery.trim() || ref, maxBudgetInTeam: budget }
3374
+ ]);
3375
+ }
3376
+ setMemberQuery("");
3377
+ setMemberRef("");
3378
+ setMemberBudget("");
3379
+ return;
3380
+ }
3352
3381
  try {
3353
3382
  setMemberBusy(true);
3354
3383
  await onAddMember(memberRef.trim(), budget);
@@ -3362,6 +3391,10 @@ var init_ManageTeamDialog = __esm({
3362
3391
  }
3363
3392
  };
3364
3393
  const handleRemoveMember = async (userId) => {
3394
+ if (creating) {
3395
+ setPendingMembers((list) => list.filter((p) => p.userEntityRef !== userId));
3396
+ return;
3397
+ }
3365
3398
  if (!onRemoveMember) return;
3366
3399
  setMemberError(null);
3367
3400
  try {
@@ -3415,13 +3448,14 @@ var init_ManageTeamDialog = __esm({
3415
3448
  }
3416
3449
  return { disabled: false };
3417
3450
  };
3451
+ const submitLabel = creating ? "Create team" : "Save";
3418
3452
  const getDialogTitle = () => {
3419
3453
  if (membersOnly) {
3420
3454
  return `Manage members \u2014 ${team?.team_alias || "Untitled team"}`;
3421
3455
  }
3422
3456
  return mode === "create" ? "Create Team" : "Edit Team";
3423
3457
  };
3424
- return /* @__PURE__ */ jsxs10(Dialog3, { open, onClose, maxWidth: "sm", fullWidth: true, children: [
3458
+ return /* @__PURE__ */ jsxs10(Dialog3, { open, onClose, maxWidth: wide ? "md" : "sm", fullWidth: true, children: [
3425
3459
  /* @__PURE__ */ jsx11(DialogTitle3, { children: getDialogTitle() }),
3426
3460
  /* @__PURE__ */ jsxs10(DialogContent3, { sx: { pt: 2, display: "flex", flexDirection: "column", gap: 2 }, children: [
3427
3461
  error && /* @__PURE__ */ jsx11(Alert2, { severity: "error", children: error }),
@@ -3430,186 +3464,215 @@ var init_ManageTeamDialog = __esm({
3430
3464
  team?.members_with_roles?.find((m) => m.user_id === currentUserId)?.role || "member",
3431
3465
  " you can add or remove members. Budgets and models are managed by your platform admins."
3432
3466
  ] }),
3433
- !membersOnly && /* @__PURE__ */ jsxs10(Fragment4, { children: [
3434
- /* @__PURE__ */ jsx11(
3435
- TextField3,
3436
- {
3437
- label: "Team Alias",
3438
- value: alias,
3439
- onChange: (e) => setAlias(e.target.value),
3440
- disabled: submitting,
3441
- fullWidth: true
3442
- }
3443
- ),
3444
- /* @__PURE__ */ jsx11(
3445
- Autocomplete2,
3446
- {
3447
- multiple: true,
3448
- options: modelNames,
3449
- value: models,
3450
- onChange: (_, newValue) => setModels(newValue),
3451
- disabled: submitting,
3452
- renderInput: (params) => /* @__PURE__ */ jsx11(
3453
- TextField3,
3454
- {
3455
- ...params,
3456
- label: "Models",
3457
- required: true,
3458
- helperText: "At least one model is required here \u2014 this delegated flow only grants access to models your admin has allow-listed for team creation, not every proxy model. (Teams with no model restriction shown elsewhere were configured directly in LiteLLM.)"
3459
- }
3460
- )
3461
- }
3462
- ),
3463
- /* @__PURE__ */ jsx11(
3464
- TextField3,
3465
- {
3466
- label: budgetWriteOnly ? "New Max Budget ($)" : "Max Budget ($)",
3467
- type: "number",
3468
- value: maxBudget,
3469
- onChange: (e) => setMaxBudget(e.target.value),
3470
- disabled: submitting || unlimited,
3471
- inputProps: { min: 0, max: maxBudgetCeiling },
3472
- helperText: budgetWriteOnly ? "Current budget is hidden \u2014 leave blank to keep it, or enter a new value" : `Maximum: $${maxBudgetCeiling}`,
3473
- fullWidth: true
3474
- }
3475
- ),
3476
- allowUnlimitedBudget && /* @__PURE__ */ jsx11(
3477
- FormControlLabel2,
3478
- {
3479
- control: /* @__PURE__ */ jsx11(Checkbox2, { checked: unlimited, onChange: (e) => setUnlimited(e.target.checked), disabled: submitting }),
3480
- label: "Unlimited Budget"
3481
- }
3482
- ),
3483
- /* @__PURE__ */ jsx11(
3484
- TextField3,
3485
- {
3486
- select: true,
3487
- label: "Budget Duration",
3488
- value: budgetDuration,
3489
- onChange: (e) => setBudgetDuration(e.target.value),
3490
- disabled: submitting || unlimited,
3491
- helperText: "Spend-reset period for the team budget",
3492
- fullWidth: true,
3493
- children: durationOptions.map((o) => /* @__PURE__ */ jsx11(MenuItem2, { value: o.value, children: o.label }, o.value))
3494
- }
3495
- )
3496
- ] }),
3497
- showMembers && /* @__PURE__ */ jsxs10(Box10, { children: [
3498
- /* @__PURE__ */ jsx11(Divider2, { sx: { my: 1 } }),
3499
- /* @__PURE__ */ jsx11(Typography8, { variant: "subtitle2", sx: { mb: 1 }, children: "Members" }),
3500
- memberError && /* @__PURE__ */ jsx11(Alert2, { severity: "error", sx: { mb: 1 }, onClose: () => setMemberError(null), children: memberError }),
3501
- members.length === 0 ? /* @__PURE__ */ jsx11(Typography8, { variant: "body2", color: "text.secondary", sx: { mb: 1 }, children: "No members yet." }) : /* @__PURE__ */ jsx11(TableContainer2, { component: Paper4, variant: "outlined", sx: { borderRadius: 1.5, mb: 1.5 }, children: /* @__PURE__ */ jsxs10(Table2, { size: "small", sx: dataTableSx, children: [
3502
- /* @__PURE__ */ jsx11(TableHead2, { children: /* @__PURE__ */ jsxs10(TableRow2, { children: [
3503
- /* @__PURE__ */ jsx11(TableCell2, { children: "User" }),
3504
- /* @__PURE__ */ jsx11(TableCell2, { align: "right", children: "Role" }),
3505
- /* @__PURE__ */ jsx11(TableCell2, { align: "right", sx: { width: 40 } })
3506
- ] }) }),
3507
- /* @__PURE__ */ jsx11(TableBody2, { children: members.map((m) => /* @__PURE__ */ jsxs10(TableRow2, { children: [
3508
- /* @__PURE__ */ jsx11(TableCell2, { children: m.user_email ? /* @__PURE__ */ jsxs10(Fragment4, { children: [
3509
- /* @__PURE__ */ jsx11(Typography8, { variant: "body2", children: m.user_email }),
3467
+ /* @__PURE__ */ jsxs10(
3468
+ Box10,
3469
+ {
3470
+ sx: {
3471
+ display: "grid",
3472
+ gridTemplateColumns: {
3473
+ xs: "1fr",
3474
+ md: wide ? "minmax(0, 1fr) minmax(0, 1fr)" : "1fr"
3475
+ },
3476
+ gap: 3,
3477
+ alignItems: "start"
3478
+ },
3479
+ children: [
3480
+ !membersOnly && /* @__PURE__ */ jsxs10(Box10, { sx: { display: "flex", flexDirection: "column", gap: 2 }, children: [
3481
+ wide && /* @__PURE__ */ jsx11(Typography8, { variant: "subtitle2", children: "Settings" }),
3510
3482
  /* @__PURE__ */ jsx11(
3511
- Typography8,
3483
+ TextField3,
3512
3484
  {
3513
- variant: "caption",
3514
- color: "text.secondary",
3515
- sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 },
3516
- children: m.user_id
3485
+ label: "Team Alias",
3486
+ value: alias,
3487
+ onChange: (e) => setAlias(e.target.value),
3488
+ disabled: submitting,
3489
+ fullWidth: true
3517
3490
  }
3518
- )
3519
- ] }) : /* @__PURE__ */ jsx11(
3520
- Typography8,
3521
- {
3522
- variant: "body2",
3523
- sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" },
3524
- children: m.user_id
3525
- }
3526
- ) }),
3527
- /* @__PURE__ */ jsx11(TableCell2, { align: "right", children: /* @__PURE__ */ jsx11(StatusPill, { label: m.role, tone: m.role === "admin" ? "accent" : "neutral", dot: false }) }),
3528
- /* @__PURE__ */ jsx11(TableCell2, { align: "right", children: (() => {
3529
- const { disabled, reason } = isRemovalDisabled(m.user_id, m.role);
3530
- return /* @__PURE__ */ jsx11(Tooltip4, { title: reason ?? "", children: /* @__PURE__ */ jsx11("span", { children: /* @__PURE__ */ jsx11(
3531
- IconButton4,
3491
+ ),
3492
+ /* @__PURE__ */ jsx11(
3493
+ Autocomplete2,
3532
3494
  {
3533
- edge: "end",
3534
- size: "small",
3535
- "aria-label": `remove ${m.user_id}`,
3536
- disabled: memberBusy || disabled,
3537
- onClick: () => handleRemoveMember(m.user_id),
3538
- children: /* @__PURE__ */ jsx11(DeleteIcon, { fontSize: "small" })
3539
- }
3540
- ) }) });
3541
- })() })
3542
- ] }, m.user_id)) })
3543
- ] }) }),
3544
- /* @__PURE__ */ jsxs10(Box10, { sx: { display: "flex", gap: 1, alignItems: "flex-start", flexWrap: "wrap" }, children: [
3545
- /* @__PURE__ */ jsx11(
3546
- Autocomplete2,
3547
- {
3548
- freeSolo: true,
3549
- autoHighlight: true,
3550
- options: userOptions,
3551
- loading: usersLoading,
3552
- inputValue: memberQuery,
3553
- onInputChange: (_, newInputValue, reason) => {
3554
- setMemberQuery(newInputValue);
3555
- if (reason === "input" || reason === "clear") {
3556
- setMemberRef(newInputValue);
3557
- handleUserSearch(newInputValue);
3495
+ multiple: true,
3496
+ options: modelNames,
3497
+ value: models,
3498
+ onChange: (_, newValue) => setModels(newValue),
3499
+ disabled: submitting,
3500
+ renderInput: (params) => /* @__PURE__ */ jsx11(
3501
+ TextField3,
3502
+ {
3503
+ ...params,
3504
+ label: "Models",
3505
+ required: true,
3506
+ helperText: "Only models your admin allow-listed for teams. At least one is required."
3507
+ }
3508
+ )
3558
3509
  }
3559
- },
3560
- onChange: (_, newValue) => {
3561
- if (newValue && typeof newValue !== "string") {
3562
- setMemberQuery(newValue.label);
3563
- setMemberRef(newValue.ref);
3510
+ ),
3511
+ /* @__PURE__ */ jsxs10(
3512
+ Box10,
3513
+ {
3514
+ sx: {
3515
+ display: "grid",
3516
+ gridTemplateColumns: { xs: "1fr", sm: "1fr 1fr" },
3517
+ gap: 2
3518
+ },
3519
+ children: [
3520
+ /* @__PURE__ */ jsx11(
3521
+ TextField3,
3522
+ {
3523
+ label: budgetWriteOnly ? "New Max Budget ($)" : "Max Budget ($)",
3524
+ type: "number",
3525
+ value: maxBudget,
3526
+ onChange: (e) => setMaxBudget(e.target.value),
3527
+ disabled: submitting || unlimited,
3528
+ inputProps: { min: 0, max: maxBudgetCeiling },
3529
+ helperText: budgetWriteOnly ? "Current budget is hidden \u2014 leave blank to keep it, or enter a new value" : `Maximum: $${maxBudgetCeiling}`,
3530
+ fullWidth: true
3531
+ }
3532
+ ),
3533
+ /* @__PURE__ */ jsx11(
3534
+ TextField3,
3535
+ {
3536
+ select: true,
3537
+ label: "Budget Duration",
3538
+ value: budgetDuration,
3539
+ onChange: (e) => setBudgetDuration(e.target.value),
3540
+ disabled: submitting || unlimited,
3541
+ helperText: "Spend-reset period for the team budget",
3542
+ fullWidth: true,
3543
+ children: durationOptions.map((o) => /* @__PURE__ */ jsx11(MenuItem2, { value: o.value, children: o.label }, o.value))
3544
+ }
3545
+ )
3546
+ ]
3564
3547
  }
3565
- },
3566
- getOptionLabel: (o) => typeof o === "string" ? o : o.label,
3567
- isOptionEqualToValue: (o, v) => o.ref === (typeof v === "string" ? v : v.ref),
3568
- disabled: memberBusy,
3569
- sx: { flex: 1, minWidth: 260 },
3570
- renderInput: (params) => /* @__PURE__ */ jsx11(
3571
- TextField3,
3548
+ ),
3549
+ allowUnlimitedBudget && /* @__PURE__ */ jsx11(
3550
+ FormControlLabel2,
3572
3551
  {
3573
- ...params,
3574
- label: "Add member",
3575
- placeholder: "Search by name or email\u2026",
3576
- size: "small",
3577
- helperText: "Pick a catalog user, or paste a user entity ref",
3578
- InputProps: {
3579
- ...params.InputProps,
3580
- endAdornment: /* @__PURE__ */ jsxs10(Fragment4, { children: [
3581
- usersLoading ? /* @__PURE__ */ jsx11(CircularProgress3, { color: "inherit", size: 14 }) : null,
3582
- params.InputProps.endAdornment
3583
- ] })
3584
- }
3552
+ control: /* @__PURE__ */ jsx11(Checkbox2, { checked: unlimited, onChange: (e) => setUnlimited(e.target.checked), disabled: submitting }),
3553
+ label: "Unlimited Budget"
3585
3554
  }
3586
3555
  )
3587
- }
3588
- ),
3589
- !membersOnly && /* @__PURE__ */ jsx11(
3590
- TextField3,
3591
- {
3592
- label: "Max budget in team ($)",
3593
- type: "number",
3594
- value: memberBudget,
3595
- onChange: (e) => setMemberBudget(e.target.value),
3596
- disabled: memberBusy,
3597
- size: "small",
3598
- sx: { width: 160 }
3599
- }
3600
- ),
3601
- /* @__PURE__ */ jsx11(
3602
- Button4,
3603
- {
3604
- onClick: handleAddMember,
3605
- disabled: memberBusy || !memberRef.trim(),
3606
- variant: "outlined",
3607
- sx: { mt: 0.5 },
3608
- children: "Add"
3609
- }
3610
- )
3611
- ] })
3612
- ] }),
3556
+ ] }),
3557
+ showMembers && /* @__PURE__ */ jsxs10(Box10, { children: [
3558
+ /* @__PURE__ */ jsx11(Typography8, { variant: "subtitle2", sx: { mb: creating ? 0.25 : 1 }, children: creating ? "Initial members" : "Members" }),
3559
+ creating && /* @__PURE__ */ jsx11(Typography8, { variant: "body2", color: "text.secondary", sx: { mb: 1 }, children: "Added as team users right after the team is created." }),
3560
+ memberError && /* @__PURE__ */ jsx11(Alert2, { severity: "error", sx: { mb: 1 }, onClose: () => setMemberError(null), children: memberError }),
3561
+ members.length === 0 ? /* @__PURE__ */ jsx11(Typography8, { variant: "body2", color: "text.secondary", sx: { mb: 1 }, children: creating ? "No members picked yet \u2014 you can also add them later." : "No members yet." }) : /* @__PURE__ */ jsx11(TableContainer2, { component: Paper4, variant: "outlined", sx: { borderRadius: 1.5, mb: 1.5 }, children: /* @__PURE__ */ jsxs10(Table2, { size: "small", sx: dataTableSx, children: [
3562
+ /* @__PURE__ */ jsx11(TableHead2, { children: /* @__PURE__ */ jsxs10(TableRow2, { children: [
3563
+ /* @__PURE__ */ jsx11(TableCell2, { children: "User" }),
3564
+ /* @__PURE__ */ jsx11(TableCell2, { align: "right", children: "Role" }),
3565
+ /* @__PURE__ */ jsx11(TableCell2, { align: "right", sx: { width: 40 } })
3566
+ ] }) }),
3567
+ /* @__PURE__ */ jsx11(TableBody2, { children: members.map((m) => /* @__PURE__ */ jsxs10(TableRow2, { children: [
3568
+ /* @__PURE__ */ jsx11(TableCell2, { children: m.user_email ? /* @__PURE__ */ jsxs10(Fragment4, { children: [
3569
+ /* @__PURE__ */ jsx11(Typography8, { variant: "body2", children: m.user_email }),
3570
+ /* @__PURE__ */ jsx11(
3571
+ Typography8,
3572
+ {
3573
+ variant: "caption",
3574
+ color: "text.secondary",
3575
+ sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 },
3576
+ children: m.user_id
3577
+ }
3578
+ )
3579
+ ] }) : /* @__PURE__ */ jsx11(
3580
+ Typography8,
3581
+ {
3582
+ variant: "body2",
3583
+ sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" },
3584
+ children: m.user_id
3585
+ }
3586
+ ) }),
3587
+ /* @__PURE__ */ jsx11(TableCell2, { align: "right", children: /* @__PURE__ */ jsx11(StatusPill, { label: m.role, tone: m.role === "admin" ? "accent" : "neutral", dot: false }) }),
3588
+ /* @__PURE__ */ jsx11(TableCell2, { align: "right", children: (() => {
3589
+ const { disabled, reason } = isRemovalDisabled(m.user_id, m.role);
3590
+ return /* @__PURE__ */ jsx11(Tooltip4, { title: reason ?? "", children: /* @__PURE__ */ jsx11("span", { children: /* @__PURE__ */ jsx11(
3591
+ IconButton4,
3592
+ {
3593
+ edge: "end",
3594
+ size: "small",
3595
+ "aria-label": `remove ${m.user_id}`,
3596
+ disabled: memberBusy || disabled,
3597
+ onClick: () => handleRemoveMember(m.user_id),
3598
+ children: /* @__PURE__ */ jsx11(DeleteIcon, { fontSize: "small" })
3599
+ }
3600
+ ) }) });
3601
+ })() })
3602
+ ] }, m.user_id)) })
3603
+ ] }) }),
3604
+ /* @__PURE__ */ jsxs10(Box10, { sx: { display: "flex", gap: 1, alignItems: "flex-start", flexWrap: "wrap" }, children: [
3605
+ /* @__PURE__ */ jsx11(
3606
+ Autocomplete2,
3607
+ {
3608
+ freeSolo: true,
3609
+ autoHighlight: true,
3610
+ options: userOptions,
3611
+ loading: usersLoading,
3612
+ inputValue: memberQuery,
3613
+ onInputChange: (_, newInputValue, reason) => {
3614
+ setMemberQuery(newInputValue);
3615
+ if (reason === "input" || reason === "clear") {
3616
+ setMemberRef(newInputValue);
3617
+ handleUserSearch(newInputValue);
3618
+ }
3619
+ },
3620
+ onChange: (_, newValue) => {
3621
+ if (newValue && typeof newValue !== "string") {
3622
+ setMemberQuery(newValue.label);
3623
+ setMemberRef(newValue.ref);
3624
+ }
3625
+ },
3626
+ getOptionLabel: (o) => typeof o === "string" ? o : o.label,
3627
+ isOptionEqualToValue: (o, v) => o.ref === (typeof v === "string" ? v : v.ref),
3628
+ disabled: memberBusy,
3629
+ sx: { flex: "1 1 240px" },
3630
+ renderInput: (params) => /* @__PURE__ */ jsx11(
3631
+ TextField3,
3632
+ {
3633
+ ...params,
3634
+ label: "Add member",
3635
+ placeholder: "Search by name or email\u2026",
3636
+ size: "small",
3637
+ helperText: "Pick a catalog user, or paste a user entity ref",
3638
+ InputProps: {
3639
+ ...params.InputProps,
3640
+ endAdornment: /* @__PURE__ */ jsxs10(Fragment4, { children: [
3641
+ usersLoading ? /* @__PURE__ */ jsx11(CircularProgress3, { color: "inherit", size: 14 }) : null,
3642
+ params.InputProps.endAdornment
3643
+ ] })
3644
+ }
3645
+ }
3646
+ )
3647
+ }
3648
+ ),
3649
+ !membersOnly && /* @__PURE__ */ jsx11(
3650
+ TextField3,
3651
+ {
3652
+ label: "Max budget in team ($)",
3653
+ type: "number",
3654
+ value: memberBudget,
3655
+ onChange: (e) => setMemberBudget(e.target.value),
3656
+ disabled: memberBusy,
3657
+ size: "small",
3658
+ sx: { width: 150 }
3659
+ }
3660
+ ),
3661
+ /* @__PURE__ */ jsx11(
3662
+ Button4,
3663
+ {
3664
+ onClick: handleAddMember,
3665
+ disabled: memberBusy || !memberRef.trim(),
3666
+ variant: "outlined",
3667
+ sx: { mt: 0.5 },
3668
+ children: "Add"
3669
+ }
3670
+ )
3671
+ ] })
3672
+ ] })
3673
+ ]
3674
+ }
3675
+ ),
3613
3676
  !membersOnly && showKnowledgeBases && /* @__PURE__ */ jsxs10(Box10, { children: [
3614
3677
  /* @__PURE__ */ jsx11(Divider2, { sx: { my: 1 } }),
3615
3678
  /* @__PURE__ */ jsx11(Typography8, { variant: "subtitle2", sx: { mb: 1 }, children: "Knowledge Bases" }),
@@ -3667,7 +3730,7 @@ var init_ManageTeamDialog = __esm({
3667
3730
  ] }),
3668
3731
  /* @__PURE__ */ jsxs10(DialogActions3, { children: [
3669
3732
  /* @__PURE__ */ jsx11(Button4, { onClick: onClose, disabled: submitting, children: membersOnly ? "Done" : "Cancel" }),
3670
- !membersOnly && /* @__PURE__ */ jsx11(Button4, { onClick: handleSubmit, disabled: submitting, variant: "contained", children: submitting ? "Saving..." : "Save" })
3733
+ !membersOnly && /* @__PURE__ */ jsx11(Button4, { onClick: handleSubmit, disabled: submitting, variant: "contained", children: submitting ? "Saving..." : submitLabel })
3671
3734
  ] })
3672
3735
  ] });
3673
3736
  };
@@ -5836,28 +5899,6 @@ var init_dates = __esm({
5836
5899
  }
5837
5900
  });
5838
5901
 
5839
- // src/permissions.ts
5840
- import {
5841
- litellmKeyCreatePermission,
5842
- litellmKeyRevokePermission as litellmKeyRevokePermission2,
5843
- litellmKeyManagePermission as litellmKeyManagePermission2,
5844
- litellmKeyResetSpendPermission as litellmKeyResetSpendPermission2,
5845
- litellmKeyUnblockPermission as litellmKeyUnblockPermission2,
5846
- litellmAuditReadPermission,
5847
- litellmTeamCreatePermission,
5848
- litellmTeamManagePermission,
5849
- litellmTeamMembersManagePermission,
5850
- litellmTeamKnowledgebaseManagePermission,
5851
- litellmTeamMcpManagePermission,
5852
- litellmTeamDeletePermission,
5853
- litellmPermissions
5854
- } from "@acarmisc/backstage-plugin-litellm-common";
5855
- var init_permissions = __esm({
5856
- "src/permissions.ts"() {
5857
- "use strict";
5858
- }
5859
- });
5860
-
5861
5902
  // src/feedback.ts
5862
5903
  function toastFor(kind, errorMsg) {
5863
5904
  switch (kind) {
@@ -5969,6 +6010,13 @@ import { useAsync as useAsync2, useAsyncRetry } from "react-use";
5969
6010
  import { useApi as useApi4, alertApiRef as alertApiRef2 } from "@backstage/core-plugin-api";
5970
6011
  import { Content, Progress } from "@backstage/core-components";
5971
6012
  import { usePermission as usePermission3 } from "@backstage/plugin-permission-react";
6013
+ import {
6014
+ litellmTeamCreatePermission,
6015
+ litellmTeamManagePermission,
6016
+ litellmTeamMembersManagePermission,
6017
+ litellmTeamKnowledgebaseManagePermission,
6018
+ litellmTeamMcpManagePermission
6019
+ } from "@acarmisc/backstage-plugin-litellm-common";
5972
6020
  import { jsx as jsx18, jsxs as jsxs17 } from "react/jsx-runtime";
5973
6021
  function initDateRange() {
5974
6022
  let preset = "7d";
@@ -5999,7 +6047,6 @@ var init_LiteLLMPage = __esm({
5999
6047
  init_AuditLog();
6000
6048
  init_api();
6001
6049
  init_dates();
6002
- init_permissions();
6003
6050
  init_feedback();
6004
6051
  init_teamMemberManager();
6005
6052
  init_unprovisionedCopy();
@@ -6521,7 +6568,7 @@ var init_LiteLLMPage = __esm({
6521
6568
  membersOnly: manageTeam?.mode === "members",
6522
6569
  currentUserId: userInfo?.user_id,
6523
6570
  memberManagerRoles: liteLlmConfig?.teamManagement?.memberManagerRoles,
6524
- onSubmit: async (payload) => {
6571
+ onSubmit: async (payload, initialMembers) => {
6525
6572
  try {
6526
6573
  if (manageTeam?.mode === "edit" && manageTeam.team) {
6527
6574
  const loadedAt = manageTeam.team.metadata?.updated_at_iso;
@@ -6531,7 +6578,23 @@ var init_LiteLLMPage = __esm({
6531
6578
  ...typeof loadedAt === "string" ? { expectedUpdatedAtIso: loadedAt } : {}
6532
6579
  });
6533
6580
  } else {
6534
- await api.createTeam(payload);
6581
+ const created = await api.createTeam(payload);
6582
+ const failed = [];
6583
+ for (const m of initialMembers ?? []) {
6584
+ try {
6585
+ await api.addTeamMember(created.team_id, m);
6586
+ } catch (e) {
6587
+ failed.push(`${m.userEntityRef} (${e.message})`);
6588
+ }
6589
+ }
6590
+ if (failed.length) {
6591
+ alertApi.post({
6592
+ message: `Team created, but some members could not be added: ${failed.join(", ")}`,
6593
+ severity: "warning"
6594
+ });
6595
+ refreshTeams();
6596
+ return;
6597
+ }
6535
6598
  }
6536
6599
  const alert = toastFor("teamSaveSuccess");
6537
6600
  if (alert) alertApi.post(alert);
@@ -7536,36 +7599,18 @@ var LiteLLMBudgetGauges = ({
7536
7599
  // src/index.ts
7537
7600
  init_KeyFormDialog();
7538
7601
  init_GenerateKeyButton();
7539
-
7540
- // src/components/GenerateKeyDialog.tsx
7541
- init_KeyFormDialog();
7542
- import { jsx as jsx22 } from "react/jsx-runtime";
7543
- var GenerateKeyDialog = (props) => /* @__PURE__ */ jsx22(
7544
- KeyFormDialog,
7545
- {
7546
- open: props.open,
7547
- onClose: props.onClose,
7548
- mode: "create",
7549
- keys: props.keys,
7550
- models: props.models,
7551
- teams: props.teams,
7552
- username: props.username,
7553
- keyGenerationSettings: props.keyGenerationSettings,
7554
- onCreateKey: props.onGenerateKey,
7555
- onUpdateKey: async () => {
7556
- },
7557
- onGetConfig: props.onGetConfig
7558
- }
7559
- );
7560
-
7561
- // src/index.ts
7562
7602
  init_ManageTeamDialog();
7563
7603
  init_api();
7564
- init_permissions();
7604
+ import {
7605
+ litellmTeamCreatePermission as litellmTeamCreatePermission2,
7606
+ litellmTeamManagePermission as litellmTeamManagePermission2,
7607
+ litellmTeamMembersManagePermission as litellmTeamMembersManagePermission2,
7608
+ litellmTeamKnowledgebaseManagePermission as litellmTeamKnowledgebaseManagePermission2,
7609
+ litellmTeamMcpManagePermission as litellmTeamMcpManagePermission2
7610
+ } from "@acarmisc/backstage-plugin-litellm-common";
7565
7611
  export {
7566
7612
  DashboardHeader,
7567
7613
  GenerateKeyButton,
7568
- GenerateKeyDialog,
7569
7614
  KeyFormDialog,
7570
7615
  KeysTable,
7571
7616
  LiteLLMBudgetGauges,
@@ -7576,12 +7621,13 @@ export {
7576
7621
  ManageTeamDialog,
7577
7622
  TeamUsage,
7578
7623
  UsageStats,
7624
+ litellmPlugin as default,
7579
7625
  liteLlmApiRef,
7580
7626
  litellmPlugin,
7581
- litellmTeamCreatePermission,
7582
- litellmTeamKnowledgebaseManagePermission,
7583
- litellmTeamManagePermission,
7584
- litellmTeamMcpManagePermission,
7585
- litellmTeamMembersManagePermission
7627
+ litellmTeamCreatePermission2 as litellmTeamCreatePermission,
7628
+ litellmTeamKnowledgebaseManagePermission2 as litellmTeamKnowledgebaseManagePermission,
7629
+ litellmTeamManagePermission2 as litellmTeamManagePermission,
7630
+ litellmTeamMcpManagePermission2 as litellmTeamMcpManagePermission,
7631
+ litellmTeamMembersManagePermission2 as litellmTeamMembersManagePermission
7586
7632
  };
7587
7633
  //# sourceMappingURL=index.esm.js.map