@acarmisc/backstage-plugin-litellm-backend 0.20.0 → 0.21.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.
@@ -19,6 +19,11 @@ export interface OpencodeConfig {
19
19
  keyDuration: string;
20
20
  /** Default max budget (USD) for keys created via the connect flow. Default 50. */
21
21
  maxBudget: number;
22
+ /**
23
+ * Reset period of maxBudget (e.g. "30d"). Unset: maxBudget covers the key's whole life,
24
+ * which runs out early on long-lived keys.
25
+ */
26
+ budgetDuration?: string;
22
27
  /**
23
28
  * When true, a team query parameter must reference a team the user is a
24
29
  * member of (or manage); otherwise the key is personal-only.
package/dist/opencode.js CHANGED
@@ -7,6 +7,7 @@ function readOpencodeConfig(config) {
7
7
  enabled,
8
8
  keyDuration: config.getOptionalString('litellm.opencode.keyDuration') ?? '30d',
9
9
  maxBudget: config.getOptionalNumber('litellm.opencode.maxBudget') ?? 50,
10
+ budgetDuration: config.getOptionalString('litellm.opencode.budgetDuration'),
10
11
  requireTeam: config.getOptionalBoolean('litellm.opencode.requireTeam') ?? false,
11
12
  metadata: config.getOptional('litellm.opencode.metadata') ?? {},
12
13
  };
@@ -156,6 +156,7 @@ function registerOpencodeRoutes(router, ctx) {
156
156
  alias,
157
157
  duration: opencodeCfg.keyDuration,
158
158
  max_budget: opencodeCfg.maxBudget,
159
+ ...(opencodeCfg.budgetDuration && { budget_duration: opencodeCfg.budgetDuration }),
159
160
  team_id: teamId,
160
161
  metadata: {
161
162
  ...opencodeCfg.metadata,
@@ -202,15 +203,18 @@ function registerOpencodeRoutes(router, ctx) {
202
203
  res.status(502).json({ error: 'LiteLLM returned no key material' });
203
204
  return;
204
205
  }
206
+ logger.info('opencode.connect', { userId, team: teamId ?? null, rotated });
207
+ // The Backstage connect page calls this with fetchApi (JSON + Bearer) and does the
208
+ // loopback redirect itself, so it can collect one key per team in a single trip.
209
+ if (req.is('application/json')) {
210
+ res.set('Cache-Control', 'no-store');
211
+ res.json({ key, team: teamId ?? null });
212
+ return;
213
+ }
205
214
  const url = new URL(redirectUri);
206
215
  url.searchParams.set('key', key);
207
216
  if (teamId)
208
217
  url.searchParams.set('team', teamId);
209
- logger.info('opencode.connect', {
210
- userId,
211
- team: teamId ?? null,
212
- rotated,
213
- });
214
218
  res.redirect(302, url.href);
215
219
  }
216
220
  catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@acarmisc/backstage-plugin-litellm-backend",
3
- "version": "0.20.0",
3
+ "version": "0.21.0",
4
4
  "description": "The Backstage backend plugin for LiteLLM governance",
5
5
  "backstage": {
6
6
  "role": "backend-plugin",