@jeffusion/bungee-core 5.15.1 → 5.15.2

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.
@@ -168,11 +168,19 @@ function current(state) {
168
168
  return value === undefined ? empty() : validatePublication(value);
169
169
  }
170
170
  function routeKeyBindings(value) {
171
- return Object.fromEntries(value.protectedRouteIds.map((routeId) => [routeId, value.credentials.filter((key) => {
171
+ const routeIds = [...new Set([...value.protectedRouteIds, ...Object.values(value.byKey).flatMap((scope) => scope?.routes ?? [])])];
172
+ return Object.fromEntries(routeIds.map((routeId) => [routeId, value.credentials.filter((key) => {
172
173
  const scope = validatePolicy(value.byKey[key.id] ?? null);
173
174
  return scope?.routes == null || scope.routes.includes(routeId);
174
175
  }).map((key) => ({ id: key.id, name: key.name }))]));
175
176
  }
177
+ function routeAccess(value) {
178
+ return {
179
+ protectedRouteIds: value.protectedRouteIds,
180
+ routeKeyBindings: routeKeyBindings(value),
181
+ unrestrictedKeyIds: value.credentials.filter((key) => validatePolicy(value.byKey[key.id] ?? null)?.routes == null).map((key) => key.id)
182
+ };
183
+ }
176
184
  function metadata(key) {
177
185
  const { digest: _digest, ...value } = key;
178
186
  return value;
@@ -337,7 +345,7 @@ function createControl(host) {
337
345
  { path: "/routes", methods: ["GET", "PUT"], handler: "routeProtection", async invoke(ctx) {
338
346
  if (ctx.request.method === "GET") {
339
347
  const value = current(state);
340
- return json({ protectedRouteIds: value.protectedRouteIds, routeKeyBindings: routeKeyBindings(value), version: policy().version, ready: ready(), published: ready() });
348
+ return json({ ...routeAccess(value), version: policy().version, ready: ready(), published: ready() });
341
349
  }
342
350
  return write(async () => {
343
351
  const input = await ctx.request.json();
@@ -348,18 +356,14 @@ function createControl(host) {
348
356
  return json({ error: "reference_validator_unavailable" }, 503);
349
357
  if (!await host.validateRouteReferences(protectedRouteIds))
350
358
  return json({ error: "invalid_references" }, 422);
351
- const value = current(state), bindings = routeKeyBindings(value);
352
- const blockedRouteIds = value.protectedRouteIds.filter((id) => !protectedRouteIds.includes(id) && bindings[id].length > 0);
353
- if (blockedRouteIds.length)
354
- return json({ error: "route_has_api_keys", blockedRouteIds, routeKeyBindings: bindings }, 409);
355
- const next = { ...value, protectedRouteIds };
356
- return save(next, { protectedRouteIds, routeKeyBindings: routeKeyBindings(next) });
359
+ const next = { ...current(state), protectedRouteIds };
360
+ return save(next, routeAccess(next));
357
361
  });
358
362
  } },
359
363
  { path: "/route-key", methods: ["PUT"], handler: "applyRouteKey", async invoke(ctx) {
360
364
  return write(async () => {
361
365
  const input = await ctx.request.json();
362
- if (!input || Object.keys(input).some((k) => !["routeId", "keyId", "protect"].includes(k)) || typeof input.routeId !== "string" || typeof input.keyId !== "string" || typeof input.protect !== "boolean")
366
+ if (!input || Object.keys(input).some((k) => !["routeId", "keyId", "protect"].includes(k)) || typeof input.routeId !== "string" || typeof input.keyId !== "string" || input.protect !== undefined && typeof input.protect !== "boolean")
363
367
  return json({ error: "invalid_binding" }, 422);
364
368
  const next = current(state);
365
369
  if (!next.credentials.some((key) => key.id === input.keyId && key.revokedAt === null))
@@ -368,15 +372,12 @@ function createControl(host) {
368
372
  return json({ error: "reference_validator_unavailable" }, 503);
369
373
  if (!await host.validateRouteReferences([input.routeId]))
370
374
  return json({ error: "invalid_references" }, 422);
371
- if (!next.protectedRouteIds.includes(input.routeId) && !input.protect)
372
- return json({ error: "route_protection_confirmation_required" }, 409);
373
375
  const previous = validatePolicy(next.byKey[input.keyId] ?? null);
374
376
  const value = { routes: previous?.routes == null ? null : [...new Set([...previous.routes, input.routeId])], models: previous?.models ?? null };
375
377
  if (!await host.validateKeyPolicyReferences(input.keyId, value))
376
378
  return json({ error: "invalid_references" }, 422);
377
379
  next.byKey[input.keyId] = value;
378
- next.protectedRouteIds = [...new Set([...next.protectedRouteIds, input.routeId])];
379
- return save(next, { protectedRouteIds: next.protectedRouteIds, routeKeyBindings: routeKeyBindings(next) });
380
+ return save(next, routeAccess(next));
380
381
  });
381
382
  } },
382
383
  { path: "/keys/:keyId", methods: ["GET", "PUT"], handler: "keyPolicy", async invoke(ctx) {
@@ -434,4 +435,4 @@ export {
434
435
  verifyDataPrincipal
435
436
  };
436
437
 
437
- //# debugId=4AF8389FB39A168C64756E2164756E21
438
+ //# debugId=13B684B8C247D4F064756E2164756E21
@@ -2,7 +2,7 @@
2
2
  "version": 3,
3
3
  "sources": ["../../../../../plugins/key-access/server/control.ts", "../../../src/plugin-logger.ts", "../../../src/hooks/impl.ts", "../../../src/plugin-extensions.ts", "../../../src/plugin-durable-state.ts", "../../../src/plugin-services/host-rpc.ts", "../../../src/plugin-services/wire-contract.ts", "../../../src/plugin-services/rpc-runtime.ts", "../../../src/plugin-services/peer-channel-protocol.ts", "../../../src/plugin-services/peer-channel-hub.ts", "../../../src/plugin-services/snapshot-store.ts", "../../../src/plugin-services.ts", "../../../src/gateway/retry-action.ts", "../../../src/token-stats-window.ts", "../../../../../plugins/key-access/server/policy.ts"],
4
4
  "sourcesContent": [
5
- "import { createHash, randomBytes, randomUUID } from 'node:crypto';\nimport type { ControlHostContext, PluginControl } from '@jeffusion/bungee-core/plugin';\nimport type { PluginDurableState, DurableJson } from '@jeffusion/bungee-core/plugin';\nimport type { DataPrincipal } from '@jeffusion/bungee-core/plugin';\nimport { validatePolicy, validatePublication, stringIds, validPrincipal, type AccessPublication, type AccessCredential } from './policy';\n\ntype ReadState = Pick<PluginDurableState, 'get' | 'list'>;\nconst empty = (): AccessPublication => ({byKey: {}, protectedRouteIds: [], credentials: []});\nfunction current(state: ReadState): AccessPublication {\n const value = state.get('policies')?.value;\n return value === undefined ? empty() : validatePublication(value);\n}\nfunction routeKeyBindings(value: AccessPublication) {\n return Object.fromEntries(value.protectedRouteIds.map(routeId => [routeId, value.credentials.filter(key => {\n const scope = validatePolicy(value.byKey[key.id] ?? null);\n return scope?.routes == null || scope.routes.includes(routeId);\n }).map(key => ({id: key.id, name: key.name}))]));\n}\nfunction metadata(key: AccessCredential) { const {digest: _digest, ...value} = key; return value; }\nfunction json(value: unknown, status = 200) { return Response.json(value, {status, headers: {'cache-control': 'no-store'}}); }\nfunction keyId(request: Request): string {\n const id = decodeURIComponent(new URL(request.url).pathname.split('/').pop() ?? '');\n if (!/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(id)) throw new Error('invalid_key_id');\n return id;\n}\nexport function createControl(host: ControlHostContext): PluginControl {\n if (!host.durableState) throw new Error('key-access.durable_state_required');\n const state = host.durableState;\n let running = false, publishedVersion = -1;\n let writes: Promise<unknown> = Promise.resolve();\n const policy = () => ({version: state.get('policies')?.version ?? 0, value: current(state) as unknown as DurableJson});\n const ready = () => running && !host.signal.aborted && publishedVersion === policy().version;\n async function publish() {\n if (!host.publishPolicy) throw new Error('publication_unavailable');\n const next = policy();\n await host.publishPolicy(next);\n publishedVersion = next.version;\n }\n async function save(value: AccessPublication, result: Record<string, unknown>, status = 200): Promise<Response> {\n const old = state.get('policies');\n state.execute({commandId: randomUUID(), mutations: [{key: 'policies', expectedVersion: old?.version ?? 0, value: value as unknown as DurableJson}]});\n try { await publish(); return json({...result, version: policy().version, ready: ready(), published: true}, status); }\n catch { return json({...result, error: 'key-access.publication_pending', persisted: true, version: policy().version, ready: false, active: false, published: false}, 503); }\n }\n function write(operation: () => Promise<Response>): Promise<Response> {\n const task = writes.catch(() => undefined).then(async () => {\n if (!running || host.signal.aborted) return json({error: 'key-access.inactive'}, 503);\n try { return await operation(); } catch { return json({error: 'key-access.invalid_write'}, 422); }\n });\n writes = task; return task;\n }\n return {policy, rpc: [], api: [\n {path: '/credentials', methods: ['GET', 'POST'], handler: 'credentials', async invoke(ctx) {\n if (ctx.request.method === 'GET') {\n const keys = await Promise.all(current(state).credentials.map(async key => ({...metadata(key),\n ...(host.readResourceExtensions ? {extensions: await host.readResourceExtensions(key.id)} : {})})));\n return json({keys, ready: ready(), published: ready()});\n }\n return write(async () => {\n const input = await ctx.request.json() as {name: string; expiresAt?: number | null};\n if (!input || Object.keys(input).some(k => !['name', 'expiresAt'].includes(k)) || typeof input.name !== 'string'\n || !input.name.trim() || input.name.length > 128 || /[\\x00-\\x1f\\x7f]/.test(input.name)) return json({error: 'invalid_key_name'}, 422);\n const expiresAt = input.expiresAt ?? null, now = Date.now();\n if (expiresAt !== null && (!Number.isSafeInteger(expiresAt) || expiresAt <= now)) return json({error: 'invalid_expiration'}, 422);\n const next = current(state);\n if (next.credentials.some(key => key.name.trim() === input.name.trim())) return json({error: 'key_name_exists'}, 409);\n const token = `bng_data_${randomBytes(32).toString('base64url')}`;\n const key: AccessCredential = {id: randomUUID(), domain: 'data', name: input.name.trim(), prefix: token.slice(0, 17),\n digest: createHash('sha256').update(token).digest('hex'), createdAt: now, expiresAt, revokedAt: null, credentialVersion: 1};\n next.credentials.push(key);\n // A partially completed UI flow must never grant broader access.\n next.byKey[key.id] = {routes: [], models: null};\n try { await host.secretStore.compareAndSet('credential:' + key.id, null, token); }\n catch { return json({error: 'key_secret_unavailable'}, 503); }\n try { return await save(next, {key: metadata(key), token}, 201); }\n catch (error) {\n // A failed publication still persisted the credential; only clean up an uncommitted secret.\n if (!current(state).credentials.some(item => item.id === key.id)) {\n const stored = await host.secretStore.get('credential:' + key.id);\n if (stored) await host.secretStore.delete('credential:' + key.id, stored.version);\n }\n throw error;\n }\n });\n }},\n {path: '/credentials/:keyId', methods: ['GET', 'PUT', 'DELETE'], handler: 'credential', async invoke(ctx) {\n const id = keyId(ctx.request);\n if (ctx.request.method === 'GET') {\n const key = current(state).credentials.find(key => key.id === id);\n if (!key) return json({error: 'key_not_found'}, 404);\n try {\n const stored = await host.secretStore.get('credential:' + id);\n if (!stored) return json({error: 'key_secret_not_stored'}, 409);\n if (!current(state).credentials.some(key => key.id === id)) return json({error: 'key_not_found'}, 404);\n if (createHash('sha256').update(stored.value).digest('hex') !== key.digest) return json({error: 'key_secret_unavailable'}, 503);\n return json({token: stored.value});\n } catch { return json({error: 'key_secret_unavailable'}, 503); }\n }\n return write(async () => {\n const next = current(state), key = next.credentials.find(key => key.id === id);\n if (ctx.request.method === 'DELETE') {\n next.credentials = next.credentials.filter(key => key.id !== id);\n delete next.byKey[id];\n const result = await save(next, {deleted: true, id});\n try {\n const stored = await host.secretStore.get('credential:' + id);\n if (stored) await host.secretStore.delete('credential:' + id, stored.version);\n } catch { return json({...await result.json(),error:'key_secret_cleanup_pending', persisted:true, deleted:true, id}, 503); }\n return result;\n }\n if (!key || key.revokedAt !== null) return json({error:'key_not_found'}, 404);\n const input = await ctx.request.json();\n if (!input || Object.keys(input).some(k => !['name','expiresAt','routes','models'].includes(k)) || typeof input.name !== 'string'\n || !input.name.trim() || input.name.length > 128 || /[\\x00-\\x1f\\x7f]/.test(input.name)) return json({error:'invalid_key_name'},422);\n if (next.credentials.some(other => other.id !== id && other.name.trim() === input.name.trim())) return json({error: 'key_name_exists'}, 409);\n const expiresAt = input.expiresAt ?? null;\n if (expiresAt !== null && (!Number.isSafeInteger(expiresAt) || (expiresAt <= Date.now() && expiresAt !== key.expiresAt))) return json({error:'invalid_expiration'},422);\n const value = validatePolicy({routes: input.routes, models: input.models});\n if (!host.validateKeyPolicyReferences) return json({error:'reference_validator_unavailable'},503);\n if (!await host.validateKeyPolicyReferences(id,value)) return json({error:'invalid_references'},422);\n key.name = input.name.trim(); key.expiresAt = expiresAt; next.byKey[id] = value;\n return save(next,{key: metadata(key),value});\n });\n }},\n {path: '/routes', methods: ['GET', 'PUT'], handler: 'routeProtection', async invoke(ctx) {\n if (ctx.request.method === 'GET') { const value = current(state); return json({protectedRouteIds: value.protectedRouteIds, routeKeyBindings: routeKeyBindings(value), version: policy().version, ready: ready(), published: ready()}); }\n return write(async () => {\n const input = await ctx.request.json();\n if (!input || Object.keys(input).join() !== 'protectedRouteIds') return json({error: 'invalid_routes'}, 422);\n const protectedRouteIds = stringIds(input.protectedRouteIds);\n if (!host.validateRouteReferences) return json({error: 'reference_validator_unavailable'}, 503);\n if (!await host.validateRouteReferences(protectedRouteIds)) return json({error: 'invalid_references'}, 422);\n const value = current(state), bindings = routeKeyBindings(value);\n const blockedRouteIds = value.protectedRouteIds.filter(id => !protectedRouteIds.includes(id) && bindings[id]!.length > 0);\n if (blockedRouteIds.length) return json({error:'route_has_api_keys', blockedRouteIds, routeKeyBindings: bindings},409);\n const next = {...value, protectedRouteIds};\n return save(next, {protectedRouteIds, routeKeyBindings: routeKeyBindings(next)});\n });\n }},\n {path: '/route-key', methods: ['PUT'], handler: 'applyRouteKey', async invoke(ctx) {\n return write(async () => {\n const input = await ctx.request.json();\n if (!input || Object.keys(input).some(k => !['routeId','keyId','protect'].includes(k))\n || typeof input.routeId !== 'string' || typeof input.keyId !== 'string' || typeof input.protect !== 'boolean') return json({error:'invalid_binding'},422);\n const next = current(state);\n if (!next.credentials.some(key => key.id === input.keyId && key.revokedAt === null)) return json({error:'key_not_found'},404);\n if (!host.validateRouteReferences || !host.validateKeyPolicyReferences) return json({error:'reference_validator_unavailable'},503);\n if (!await host.validateRouteReferences([input.routeId])) return json({error:'invalid_references'},422);\n if (!next.protectedRouteIds.includes(input.routeId) && !input.protect) return json({error:'route_protection_confirmation_required'},409);\n const previous = validatePolicy(next.byKey[input.keyId] ?? null);\n const value = {routes: previous?.routes == null ? null : [...new Set([...previous.routes,input.routeId])], models:previous?.models ?? null};\n if (!await host.validateKeyPolicyReferences(input.keyId,value)) return json({error:'invalid_references'},422);\n next.byKey[input.keyId] = value;\n next.protectedRouteIds = [...new Set([...next.protectedRouteIds,input.routeId])];\n return save(next,{protectedRouteIds:next.protectedRouteIds,routeKeyBindings:routeKeyBindings(next)});\n });\n }},\n {path: '/keys/:keyId', methods: ['GET', 'PUT'], handler: 'keyPolicy', async invoke(ctx) {\n const id = keyId(ctx.request);\n if (ctx.request.method === 'GET') return json({keyId: id, version: policy().version, value: current(state).byKey[id] ?? null, active: ready(), ready: ready()});\n return write(async () => {\n const next = current(state);\n if (!next.credentials.some(key => key.id === id && key.revokedAt === null)) return json({error: 'key_not_found'}, 404);\n const value = validatePolicy(await ctx.request.json());\n if (!host.validateKeyPolicyReferences) return json({error: 'reference_validator_unavailable'}, 503);\n if (!await host.validateKeyPolicyReferences(id, value)) return json({error: 'invalid_references'}, 422);\n next.byKey[id] = value;\n return save(next, {keyId: id, value, active: true});\n });\n }},\n ],\n async start() { if (host.signal.aborted) throw new Error('inactive'); await publish(); running = true; },\n dispose() { running = false; },\n };\n}\nexport function readResource(resource: string, keyId: string, state: ReadState) {\n if (resource !== 'api-key') throw new Error('resource_not_supported');\n return {value: current(state).byKey[keyId] ?? null};\n}\nexport function readResourceCollection(resource: string, state: ReadState): readonly unknown[] {\n if (resource !== 'api-key') throw new Error('resource_not_supported');\n return current(state).credentials.map(metadata);\n}\nexport function readAdmissionRequirements(state: ReadState): readonly string[] { return current(state).protectedRouteIds; }\nexport function verifyDataPrincipal(principal: DataPrincipal, state: ReadState): boolean { return validPrincipal(principal, current(state), Date.now()); }\nexport default {createControl, readResource, readResourceCollection, readAdmissionRequirements, verifyDataPrincipal};\n",
5
+ "import { createHash, randomBytes, randomUUID } from 'node:crypto';\nimport type { ControlHostContext, PluginControl } from '@jeffusion/bungee-core/plugin';\nimport type { PluginDurableState, DurableJson } from '@jeffusion/bungee-core/plugin';\nimport type { DataPrincipal } from '@jeffusion/bungee-core/plugin';\nimport { validatePolicy, validatePublication, stringIds, validPrincipal, type AccessPublication, type AccessCredential } from './policy';\n\ntype ReadState = Pick<PluginDurableState, 'get' | 'list'>;\nconst empty = (): AccessPublication => ({byKey: {}, protectedRouteIds: [], credentials: []});\nfunction current(state: ReadState): AccessPublication {\n const value = state.get('policies')?.value;\n return value === undefined ? empty() : validatePublication(value);\n}\nfunction routeKeyBindings(value: AccessPublication) {\n const routeIds = [...new Set([...value.protectedRouteIds, ...Object.values(value.byKey).flatMap(scope => scope?.routes ?? [])])];\n return Object.fromEntries(routeIds.map(routeId => [routeId, value.credentials.filter(key => {\n const scope = validatePolicy(value.byKey[key.id] ?? null);\n return scope?.routes == null || scope.routes.includes(routeId);\n }).map(key => ({id: key.id, name: key.name}))]));\n}\nfunction routeAccess(value: AccessPublication) {\n return {protectedRouteIds: value.protectedRouteIds, routeKeyBindings: routeKeyBindings(value),\n unrestrictedKeyIds: value.credentials.filter(key => validatePolicy(value.byKey[key.id] ?? null)?.routes == null).map(key => key.id)};\n}\nfunction metadata(key: AccessCredential) { const {digest: _digest, ...value} = key; return value; }\nfunction json(value: unknown, status = 200) { return Response.json(value, {status, headers: {'cache-control': 'no-store'}}); }\nfunction keyId(request: Request): string {\n const id = decodeURIComponent(new URL(request.url).pathname.split('/').pop() ?? '');\n if (!/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(id)) throw new Error('invalid_key_id');\n return id;\n}\nexport function createControl(host: ControlHostContext): PluginControl {\n if (!host.durableState) throw new Error('key-access.durable_state_required');\n const state = host.durableState;\n let running = false, publishedVersion = -1;\n let writes: Promise<unknown> = Promise.resolve();\n const policy = () => ({version: state.get('policies')?.version ?? 0, value: current(state) as unknown as DurableJson});\n const ready = () => running && !host.signal.aborted && publishedVersion === policy().version;\n async function publish() {\n if (!host.publishPolicy) throw new Error('publication_unavailable');\n const next = policy();\n await host.publishPolicy(next);\n publishedVersion = next.version;\n }\n async function save(value: AccessPublication, result: Record<string, unknown>, status = 200): Promise<Response> {\n const old = state.get('policies');\n state.execute({commandId: randomUUID(), mutations: [{key: 'policies', expectedVersion: old?.version ?? 0, value: value as unknown as DurableJson}]});\n try { await publish(); return json({...result, version: policy().version, ready: ready(), published: true}, status); }\n catch { return json({...result, error: 'key-access.publication_pending', persisted: true, version: policy().version, ready: false, active: false, published: false}, 503); }\n }\n function write(operation: () => Promise<Response>): Promise<Response> {\n const task = writes.catch(() => undefined).then(async () => {\n if (!running || host.signal.aborted) return json({error: 'key-access.inactive'}, 503);\n try { return await operation(); } catch { return json({error: 'key-access.invalid_write'}, 422); }\n });\n writes = task; return task;\n }\n return {policy, rpc: [], api: [\n {path: '/credentials', methods: ['GET', 'POST'], handler: 'credentials', async invoke(ctx) {\n if (ctx.request.method === 'GET') {\n const keys = await Promise.all(current(state).credentials.map(async key => ({...metadata(key),\n ...(host.readResourceExtensions ? {extensions: await host.readResourceExtensions(key.id)} : {})})));\n return json({keys, ready: ready(), published: ready()});\n }\n return write(async () => {\n const input = await ctx.request.json() as {name: string; expiresAt?: number | null};\n if (!input || Object.keys(input).some(k => !['name', 'expiresAt'].includes(k)) || typeof input.name !== 'string'\n || !input.name.trim() || input.name.length > 128 || /[\\x00-\\x1f\\x7f]/.test(input.name)) return json({error: 'invalid_key_name'}, 422);\n const expiresAt = input.expiresAt ?? null, now = Date.now();\n if (expiresAt !== null && (!Number.isSafeInteger(expiresAt) || expiresAt <= now)) return json({error: 'invalid_expiration'}, 422);\n const next = current(state);\n if (next.credentials.some(key => key.name.trim() === input.name.trim())) return json({error: 'key_name_exists'}, 409);\n const token = `bng_data_${randomBytes(32).toString('base64url')}`;\n const key: AccessCredential = {id: randomUUID(), domain: 'data', name: input.name.trim(), prefix: token.slice(0, 17),\n digest: createHash('sha256').update(token).digest('hex'), createdAt: now, expiresAt, revokedAt: null, credentialVersion: 1};\n next.credentials.push(key);\n // A partially completed UI flow must never grant broader access.\n next.byKey[key.id] = {routes: [], models: null};\n try { await host.secretStore.compareAndSet('credential:' + key.id, null, token); }\n catch { return json({error: 'key_secret_unavailable'}, 503); }\n try { return await save(next, {key: metadata(key), token}, 201); }\n catch (error) {\n // A failed publication still persisted the credential; only clean up an uncommitted secret.\n if (!current(state).credentials.some(item => item.id === key.id)) {\n const stored = await host.secretStore.get('credential:' + key.id);\n if (stored) await host.secretStore.delete('credential:' + key.id, stored.version);\n }\n throw error;\n }\n });\n }},\n {path: '/credentials/:keyId', methods: ['GET', 'PUT', 'DELETE'], handler: 'credential', async invoke(ctx) {\n const id = keyId(ctx.request);\n if (ctx.request.method === 'GET') {\n const key = current(state).credentials.find(key => key.id === id);\n if (!key) return json({error: 'key_not_found'}, 404);\n try {\n const stored = await host.secretStore.get('credential:' + id);\n if (!stored) return json({error: 'key_secret_not_stored'}, 409);\n if (!current(state).credentials.some(key => key.id === id)) return json({error: 'key_not_found'}, 404);\n if (createHash('sha256').update(stored.value).digest('hex') !== key.digest) return json({error: 'key_secret_unavailable'}, 503);\n return json({token: stored.value});\n } catch { return json({error: 'key_secret_unavailable'}, 503); }\n }\n return write(async () => {\n const next = current(state), key = next.credentials.find(key => key.id === id);\n if (ctx.request.method === 'DELETE') {\n next.credentials = next.credentials.filter(key => key.id !== id);\n delete next.byKey[id];\n const result = await save(next, {deleted: true, id});\n try {\n const stored = await host.secretStore.get('credential:' + id);\n if (stored) await host.secretStore.delete('credential:' + id, stored.version);\n } catch { return json({...await result.json(),error:'key_secret_cleanup_pending', persisted:true, deleted:true, id}, 503); }\n return result;\n }\n if (!key || key.revokedAt !== null) return json({error:'key_not_found'}, 404);\n const input = await ctx.request.json();\n if (!input || Object.keys(input).some(k => !['name','expiresAt','routes','models'].includes(k)) || typeof input.name !== 'string'\n || !input.name.trim() || input.name.length > 128 || /[\\x00-\\x1f\\x7f]/.test(input.name)) return json({error:'invalid_key_name'},422);\n if (next.credentials.some(other => other.id !== id && other.name.trim() === input.name.trim())) return json({error: 'key_name_exists'}, 409);\n const expiresAt = input.expiresAt ?? null;\n if (expiresAt !== null && (!Number.isSafeInteger(expiresAt) || (expiresAt <= Date.now() && expiresAt !== key.expiresAt))) return json({error:'invalid_expiration'},422);\n const value = validatePolicy({routes: input.routes, models: input.models});\n if (!host.validateKeyPolicyReferences) return json({error:'reference_validator_unavailable'},503);\n if (!await host.validateKeyPolicyReferences(id,value)) return json({error:'invalid_references'},422);\n key.name = input.name.trim(); key.expiresAt = expiresAt; next.byKey[id] = value;\n return save(next,{key: metadata(key),value});\n });\n }},\n {path: '/routes', methods: ['GET', 'PUT'], handler: 'routeProtection', async invoke(ctx) {\n if (ctx.request.method === 'GET') { const value = current(state); return json({...routeAccess(value), version: policy().version, ready: ready(), published: ready()}); }\n return write(async () => {\n const input = await ctx.request.json();\n if (!input || Object.keys(input).join() !== 'protectedRouteIds') return json({error: 'invalid_routes'}, 422);\n const protectedRouteIds = stringIds(input.protectedRouteIds);\n if (!host.validateRouteReferences) return json({error: 'reference_validator_unavailable'}, 503);\n if (!await host.validateRouteReferences(protectedRouteIds)) return json({error: 'invalid_references'}, 422);\n const next = {...current(state), protectedRouteIds};\n return save(next, routeAccess(next));\n });\n }},\n {path: '/route-key', methods: ['PUT'], handler: 'applyRouteKey', async invoke(ctx) {\n return write(async () => {\n const input = await ctx.request.json();\n if (!input || Object.keys(input).some(k => !['routeId','keyId','protect'].includes(k))\n || typeof input.routeId !== 'string' || typeof input.keyId !== 'string'\n || (input.protect !== undefined && typeof input.protect !== 'boolean')) return json({error:'invalid_binding'},422);\n const next = current(state);\n if (!next.credentials.some(key => key.id === input.keyId && key.revokedAt === null)) return json({error:'key_not_found'},404);\n if (!host.validateRouteReferences || !host.validateKeyPolicyReferences) return json({error:'reference_validator_unavailable'},503);\n if (!await host.validateRouteReferences([input.routeId])) return json({error:'invalid_references'},422);\n const previous = validatePolicy(next.byKey[input.keyId] ?? null);\n const value = {routes: previous?.routes == null ? null : [...new Set([...previous.routes,input.routeId])], models:previous?.models ?? null};\n if (!await host.validateKeyPolicyReferences(input.keyId,value)) return json({error:'invalid_references'},422);\n next.byKey[input.keyId] = value;\n // Legacy callers may send protect; Key grants never change route public access.\n return save(next,routeAccess(next));\n });\n }},\n {path: '/keys/:keyId', methods: ['GET', 'PUT'], handler: 'keyPolicy', async invoke(ctx) {\n const id = keyId(ctx.request);\n if (ctx.request.method === 'GET') return json({keyId: id, version: policy().version, value: current(state).byKey[id] ?? null, active: ready(), ready: ready()});\n return write(async () => {\n const next = current(state);\n if (!next.credentials.some(key => key.id === id && key.revokedAt === null)) return json({error: 'key_not_found'}, 404);\n const value = validatePolicy(await ctx.request.json());\n if (!host.validateKeyPolicyReferences) return json({error: 'reference_validator_unavailable'}, 503);\n if (!await host.validateKeyPolicyReferences(id, value)) return json({error: 'invalid_references'}, 422);\n next.byKey[id] = value;\n return save(next, {keyId: id, value, active: true});\n });\n }},\n ],\n async start() { if (host.signal.aborted) throw new Error('inactive'); await publish(); running = true; },\n dispose() { running = false; },\n };\n}\nexport function readResource(resource: string, keyId: string, state: ReadState) {\n if (resource !== 'api-key') throw new Error('resource_not_supported');\n return {value: current(state).byKey[keyId] ?? null};\n}\nexport function readResourceCollection(resource: string, state: ReadState): readonly unknown[] {\n if (resource !== 'api-key') throw new Error('resource_not_supported');\n return current(state).credentials.map(metadata);\n}\nexport function readAdmissionRequirements(state: ReadState): readonly string[] { return current(state).protectedRouteIds; }\nexport function verifyDataPrincipal(principal: DataPrincipal, state: ReadState): boolean { return validPrincipal(principal, current(state), Date.now()); }\nexport default {createControl, readResource, readResourceCollection, readAdmissionRequirements, verifyDataPrincipal};\n",
6
6
  "/** Shared process sink; the public SDK must not bundle file transports into plugins. */\ntype Context = Readonly<Record<string, unknown>>;\ntype LogMethod = { (context: Context, message?: string): void; (message: string): void };\nexport interface PluginLogSink {\n info: LogMethod; warn: LogMethod; error: LogMethod; debug: LogMethod; fatal: LogMethod;\n child(bindings: Context): PluginLogSink;\n}\nconst sinkKey = Symbol.for('@jeffusion/bungee/plugin-log-sink');\nexport function installPluginLogSink(sink: PluginLogSink): void {\n (globalThis as Record<symbol, unknown>)[sinkKey] = sink;\n}\nfunction createLogger(bindings: Context = {}): PluginLogSink {\n const method = (level: 'info' | 'warn' | 'error' | 'debug' | 'fatal'): LogMethod =>\n (value: Context | string, message?: string) => {\n const sink = (globalThis as Record<symbol, unknown>)[sinkKey] as PluginLogSink | undefined;\n if (sink) {\n if (typeof value === 'string') sink[level](bindings, value);\n else sink[level]({...bindings, ...value}, message);\n } else if (level !== 'debug' || process.env.LOG_LEVEL === 'debug') {\n const write = level === 'fatal' ? console.error : console[level];\n write(message ?? (typeof value === 'string' ? value : ''), typeof value === 'string' ? bindings : {...bindings, ...value});\n }\n };\n return {info: method('info'), warn: method('warn'), error: method('error'), debug: method('debug'),\n fatal: method('fatal'), child: extra => createLogger({...bindings, ...extra})};\n}\nexport const logger = createLogger();\n",
7
7
  "/**\n * Hook 实现类\n *\n * 提供 4 种 Hook 类型的实现:\n * - AsyncParallelHook: 异步并行执行\n * - AsyncSeriesHook: 异步串行执行\n * - AsyncSeriesBailHook: 异步串行可中断\n * - AsyncSeriesWaterfallHook: 异步串行瀑布\n */\n\nimport { logger } from '../plugin-logger';\n/** Host-supplied view refresh, run between every waterfall tap. */\nexport const WATERFALL_VIEW_TRANSFORM = Symbol('waterfall-view-transform');\nimport type {\n TapInfo,\n Tap,\n IAsyncParallelHook,\n IAsyncSeriesHook,\n IAsyncSeriesBailHook,\n IAsyncSeriesWaterfallHook,\n IAsyncSeriesMapHook,\n HookStats,\n} from './types';\n\n/**\n * 标准化 TapInfo\n */\nfunction normalizeTapInfo(info: TapInfo | string): TapInfo {\n if (typeof info === 'string') {\n return { name: info, stage: 0 };\n }\n return { ...info, stage: info.stage ?? 0 };\n}\n\n/**\n * 基础 Hook 类\n */\nabstract class BaseHook<T extends any[]> {\n protected taps: Tap<T, any>[] = [];\n protected name: string;\n protected callCount = 0;\n protected totalTimeMs = 0;\n\n constructor(name?: string) {\n this.name = name ?? 'anonymous';\n }\n\n /**\n * 同步注册\n */\n tap(info: TapInfo | string, fn: (...args: T) => any): void {\n const tapInfo = normalizeTapInfo(info);\n this.taps.push({ info: tapInfo, type: 'sync', fn });\n this.sortTaps();\n }\n\n /**\n * 异步回调风格注册\n */\n tapAsync(info: TapInfo | string, fn: (...args: any[]) => void): void {\n const tapInfo = normalizeTapInfo(info);\n this.taps.push({ info: tapInfo, type: 'async', fn });\n this.sortTaps();\n }\n\n /**\n * Promise 风格注册\n */\n tapPromise(info: TapInfo | string, fn: (...args: T) => Promise<any>): void {\n const tapInfo = normalizeTapInfo(info);\n this.taps.push({ info: tapInfo, type: 'promise', fn });\n this.sortTaps();\n }\n\n /**\n * 检查是否有注册的回调\n */\n hasCallbacks(): boolean {\n return this.taps.length > 0;\n }\n\n /**\n * 获取统计信息\n */\n getStats(): HookStats {\n return {\n name: this.name,\n tapCount: this.taps.length,\n callCount: this.callCount,\n totalTimeMs: this.totalTimeMs,\n avgTimeMs: this.callCount > 0 ? this.totalTimeMs / this.callCount : 0,\n };\n }\n\n /**\n * 重置统计信息\n */\n resetStats(): void {\n this.callCount = 0;\n this.totalTimeMs = 0;\n }\n\n /**\n * 清空所有注册的回调\n */\n clear(): void {\n this.taps = [];\n }\n\n /**\n * 按 stage 排序(稳定排序)\n */\n private sortTaps(): void {\n this.taps.sort((a, b) => (a.info.stage ?? 0) - (b.info.stage ?? 0));\n }\n\n /**\n * 执行单个 tap\n */\n protected async executeTap<R>(\n tap: Tap<T, R>,\n args: T\n ): Promise<R | undefined> {\n try {\n if (tap.type === 'sync') {\n return tap.fn(...args);\n } else if (tap.type === 'promise') {\n return await tap.fn(...args);\n } else {\n // async (callback style)\n return await new Promise<R | undefined>((resolve, reject) => {\n const done = (err?: Error, result?: R) => {\n if (err) reject(err);\n else resolve(result);\n };\n tap.fn(...args, done);\n });\n }\n } catch (error) {\n logger.error(\n { error, hookName: this.name, pluginName: tap.info.name },\n 'Error in hook callback'\n );\n throw error;\n }\n }\n}\n\n/** Synchronous providers use the same registration, ordering and statistics as async hooks. */\nexport class SyncBailHook<T extends any[] = [], R = unknown> extends BaseHook<T> {\n override tapAsync(): never { throw new Error('synchronous provider requires tap'); }\n override tapPromise(): never { throw new Error('synchronous provider requires tap'); }\n call(...args: T): R | undefined {\n const started = performance.now();\n this.callCount++;\n try {\n for (const tap of this.taps) {\n const result = tap.fn(...args);\n if (result && typeof result.then === 'function') throw new Error('synchronous provider returned a Promise');\n if (result !== undefined) return result as R;\n }\n return undefined;\n } finally { this.totalTimeMs += performance.now() - started; }\n }\n}\n\n/**\n * 异步并行 Hook\n * 所有回调并行执行,全部完成后返回\n */\nexport class AsyncParallelHook<T extends any[] = []>\n extends BaseHook<T>\n implements IAsyncParallelHook<T>\n{\n async promise(...args: T): Promise<void> {\n if (this.taps.length === 0) return;\n\n const startTime = performance.now();\n this.callCount++;\n\n try {\n // 并行执行所有回调,错误独立处理不影响其他\n await Promise.all(\n this.taps.map(async (tap) => {\n try {\n await this.executeTap(tap, args);\n } catch (error) {\n // 并行 Hook 中单个回调的错误不应影响其他回调\n logger.error(\n { error, hookName: this.name, pluginName: tap.info.name },\n 'Error in parallel hook callback (ignored)'\n );\n }\n })\n );\n } finally {\n this.totalTimeMs += performance.now() - startTime;\n }\n }\n}\n\n/**\n * 异步串行 Hook\n * 所有回调按顺序串行执行\n */\nexport class AsyncSeriesHook<T extends any[] = []>\n extends BaseHook<T>\n implements IAsyncSeriesHook<T>\n{\n async promise(...args: T): Promise<void> {\n if (this.taps.length === 0) return;\n\n const startTime = performance.now();\n this.callCount++;\n\n try {\n for (const tap of this.taps) {\n await this.executeTap(tap, args);\n }\n } finally {\n this.totalTimeMs += performance.now() - startTime;\n }\n }\n}\n\n/**\n * 异步串行可中断 Hook\n * 任意回调返回非 undefined 值则停止执行并返回该值\n */\nexport class AsyncSeriesBailHook<T extends any[] = [], R = any>\n extends BaseHook<T>\n implements IAsyncSeriesBailHook<T, R>\n{\n async promise(...args: T): Promise<R | undefined> {\n if (this.taps.length === 0) return undefined;\n\n const startTime = performance.now();\n this.callCount++;\n\n try {\n for (const tap of this.taps) {\n const result = await this.executeTap<R>(tap, args);\n if (result !== undefined) {\n logger.debug(\n { hookName: this.name, pluginName: tap.info.name },\n 'Hook bailed with result'\n );\n return result;\n }\n }\n return undefined;\n } finally {\n this.totalTimeMs += performance.now() - startTime;\n }\n }\n}\n\n/**\n * 异步串行瀑布 Hook\n * 每个回调的返回值作为下一个回调的第一个参数\n */\nexport class AsyncSeriesWaterfallHook<T, A extends any[] = []>\n extends BaseHook<[T, ...A]>\n implements IAsyncSeriesWaterfallHook<T, A>\n{\n async promise(value: T, ...args: A): Promise<T> {\n if (this.taps.length === 0) return value;\n\n const startTime = performance.now();\n this.callCount++;\n\n try {\n let current = value;\n for (const tap of this.taps) {\n const result = await this.executeTap<T>(tap, [current, ...args] as [T, ...A]);\n // 只有返回非 undefined 时才更新值\n if (result !== undefined) {\n const transform = args[0]?.[WATERFALL_VIEW_TRANSFORM];\n current = typeof transform === 'function' ? await transform(result,current) : result;\n }\n }\n return current;\n } finally {\n this.totalTimeMs += performance.now() - startTime;\n }\n }\n}\n\n/**\n * 异步串行映射 Hook\n * 支持 N:M 转换:每个回调处理输入,可以返回 0 到多个输出\n *\n * 执行流程:\n * 1. 初始输入:单个 chunk\n * 2. 每个插件处理当前的所有 chunks,返回新的 chunks 数组\n * 3. 下一个插件处理上一步的所有输出\n *\n * 返回值约定:\n * - null/undefined: 不处理,原样输出当前 chunk\n * - []: 缓冲当前 chunk,不输出(N:0)\n * - [chunk]: 1:1 转换\n * - [chunk1, chunk2, ...]: 1:M 拆分\n *\n * 性能优化:\n * - 同步优先执行:sync tap 直接同步调用,跳过 Promise 开销\n * - 适用于流式响应处理等高频调用场景\n */\nexport class AsyncSeriesMapHook<T, A extends any[] = []>\n extends BaseHook<[T, ...A]>\n implements IAsyncSeriesMapHook<T, A>\n{\n /**\n * 同步执行单个 tap(跳过 Promise 开销)\n */\n private executeTapSync<R>(tap: Tap<[T, ...A], R>, args: [T, ...A]): R | undefined {\n try {\n return tap.fn(...args);\n } catch (error) {\n logger.error(\n { error, hookName: this.name, pluginName: tap.info.name },\n 'Error in hook callback'\n );\n throw error;\n }\n }\n\n async promise(value: T, ...args: A): Promise<T[]> {\n // 如果没有注册任何回调,直接返回原始值\n if (this.taps.length === 0) return [value];\n\n const startTime = performance.now();\n this.callCount++;\n\n try {\n // 初始输入为单个 chunk\n let chunks: T[] = [value];\n\n // 依次应用每个插件的转换\n for (const tap of this.taps) {\n const newChunks: T[] = [];\n\n // 对当前的每个 chunk 应用转换\n for (const chunk of chunks) {\n try {\n // 同步优先执行:sync tap 直接同步调用,跳过 Promise 开销\n const result = tap.type === 'sync'\n ? this.executeTapSync<T[] | null | undefined>(tap, [chunk, ...args] as [T, ...A])\n : await this.executeTap<T[] | null | undefined>(tap, [chunk, ...args] as [T, ...A]);\n\n if (result === null || result === undefined) {\n // 不处理,原样输出\n newChunks.push(chunk);\n } else if (Array.isArray(result)) {\n // N:M 转换\n // [] = 缓冲(不输出)\n // [item] = 1:1 转换\n // [item1, item2, ...] = 1:M 拆分\n newChunks.push(...result);\n } else {\n // 非法返回值,警告并原样输出\n logger.warn(\n { hookName: this.name, pluginName: tap.info.name, result },\n 'AsyncSeriesMapHook callback must return an array or null'\n );\n newChunks.push(chunk);\n }\n } catch (error) {\n // 单个 chunk 处理失败,记录错误并原样输出\n logger.error(\n { error, hookName: this.name, pluginName: tap.info.name },\n 'Error in AsyncSeriesMapHook callback'\n );\n if ((args[0] as { strict?: boolean } | undefined)?.strict) {\n throw error;\n }\n newChunks.push(chunk);\n }\n }\n\n chunks = newChunks;\n }\n\n return chunks;\n } finally {\n this.totalTimeMs += performance.now() - startTime;\n }\n }\n}\n",
8
8
  "import type { PluginDurableState, DurableJson } from './plugin-durable-state';\n\n/** Anonymous is an explicit, signed data identity; it carries no key authority. */\nexport type DataPrincipal = { readonly domain: string; readonly keyId: string; readonly credentialVersion: number };\nexport const ANONYMOUS_PRINCIPAL: DataPrincipal = Object.freeze({domain: 'anonymous', keyId: '', credentialVersion: 0});\nexport interface AdmissionRequirement { readonly plugin: string; readonly routeIds: readonly string[] }\n/** Public contracts. Business rules belong to the implementing plugin. */\nexport interface ManagementSubject {\n readonly id: string;\n readonly provider: string;\n readonly capabilities: readonly string[];\n readonly requiresPasswordChange?: boolean;\n}\nexport interface ManagementProvider {\n authenticate(request: Request): Promise<ManagementSubject | null>;\n authorize(subject: ManagementSubject, capability: string): boolean;\n login(request: Request): Promise<Response>;\n logout(request: Request): Promise<Response>;\n bootstrap(input: unknown): Promise<void>;\n hasIdentity(): boolean;\n revokeSessions(): void;\n validateWrite?(request: Request): void;\n csrfToken?(request: Request): string | undefined;\n /** Optional browser cookie renewal after successful session verification. Must not extend the server-side absolute deadline. */\n sessionCookie?(request: Request): string | undefined;\n}\nexport interface AdmissionTarget {\n readonly requestId: string;\n readonly attemptId: string;\n readonly principal: DataPrincipal;\n readonly routeId: string;\n readonly serviceId: string | null;\n readonly upstreamId: string;\n readonly url: string;\n readonly model: string | null;\n readonly now: number;\n}\nexport interface AdmissionDenial {\n readonly error: string;\n readonly status: 401 | 403 | 429 | 503 | 422;\n readonly retryAfter?: number;\n}\nexport interface AdmissionPlan {\n /** A complete JSON state replacement, staged by Host before the common decision. */\n readonly state?: DurableJson;\n /** Immutable policy snapshot retained by the grant for subsequent attempts. */\n readonly snapshot?: DurableJson;\n readonly denial?: AdmissionDenial;\n}\nexport interface IngressPlugin {\n /** Effective policy demand; no policy means no content decoding. */\n bodyRequirements(target: AdmissionTarget, policy: DurableJson): { readonly request: 'none' | 'json-read' };\n /** Optional per-principal state. Policies are frozen; plans receive only this key's clones.\n * Expiry must mean absence is behaviorally equivalent to retaining the state. */\n keyedState?: {\n readonly capacity: number;\n policyForKey(policy: DurableJson, keyId: string): DurableJson;\n expiresAt(state: DurableJson): number;\n reconcile(state: DurableJson, policy: DurableJson, now: number): DurableJson;\n };\n authenticate?(request: Request, policy: DurableJson, now: number): DataPrincipal | null;\n resolveIdentity?(target: AdmissionTarget, policy: DurableJson): DataPrincipal;\n plan(target: AdmissionTarget, policy: DurableJson, state: DurableJson): AdmissionPlan;\n beforeAttempt?(target: AdmissionTarget, snapshot: DurableJson): AdmissionDenial | null;\n}\nexport interface IngressPluginModule { createIngress(): IngressPlugin }\nexport interface PluginStateRpcContext {\n readonly state: PluginDurableState;\n readonly requestId: string;\n readonly attemptId: string;\n readonly principal: DataPrincipal;\n}\nexport interface PluginPolicyPublication {\n readonly version: number;\n readonly value: DurableJson;\n}\n",
@@ -18,7 +18,7 @@
18
18
  "import type { TokenStatsRange } from './plugin.types';\n\nconst HOUR = 3_600_000;\nexport const TOKEN_STATS_RETENTION_MS = 31 * 24 * HOUR;\nexport const TOKEN_STATS_RANGES = ['1h', '12h', '24h', '1d', '7d', '30d', 'day', 'week', 'month'] as const;\n\n/** Recent page ranges are consecutive elapsed hours; calendar ranges use local dates. */\nexport function tokenStatsWindow(range: TokenStatsRange, asOfMs: number, timeZone = 'UTC') {\n if (range === '1h' || range === '12h' || range === '24h') {\n const duration = { '1h': HOUR, '12h': 12 * HOUR, '24h': 24 * HOUR };\n const bucketMs = { '1h': 300_000, '12h': HOUR, '24h': 2 * HOUR }[range];\n return { startMs: asOfMs - duration[range], endMs: asOfMs, bucketMs };\n }\n if (range === '1d' || range === '7d' || range === '30d') {\n const days = { '1d': 1, '7d': 7, '30d': 30 }[range];\n const bucketMs = range === '1d' ? HOUR : 24 * HOUR;\n const startMs = asOfMs - days * 24 * HOUR;\n const bucketStarts = Array.from({ length: range === '1d' ? 24 : days }, (_, i) => startMs + i * bucketMs);\n return { startMs, endMs: asOfMs, bucketMs, bucketStarts };\n }\n const format = new Intl.DateTimeFormat('en-US', {\n timeZone, year: 'numeric', month: 'numeric', day: 'numeric',\n hour: 'numeric', minute: 'numeric', second: 'numeric', hourCycle: 'h23',\n });\n const parts = (ms: number) => Object.fromEntries(format.formatToParts(ms)\n .filter(part => part.type !== 'literal').map(part => [part.type, Number(part.value)]));\n const today = parts(asOfMs);\n const day = Date.UTC(today.year!, today.month! - 1, today.day!);\n // Resolve each local midnight separately so DST days can contain 23 or 25 hours.\n const midnight = (date: number) => {\n let candidate = date;\n for (let i = 0; i < 4; i++) {\n const p = parts(candidate);\n const local = Date.UTC(p.year!, p.month! - 1, p.day!, p.hour!, p.minute!, p.second!);\n const next = candidate + date - local;\n if (next === candidate) return candidate;\n candidate = next;\n }\n return candidate;\n };\n if (range === 'day') {\n const startMs = midnight(day);\n const bucketEndMs = midnight(day + 24 * HOUR);\n const bucketStarts = Array.from({ length: Math.ceil((bucketEndMs - startMs) / HOUR) }, (_, i) => startMs + i * HOUR);\n return { startMs, endMs: asOfMs, bucketMs: HOUR, bucketStarts, bucketEndMs };\n }\n const firstDay = range === 'month' ? Date.UTC(today.year!, today.month! - 1, 1)\n : day - ((new Date(day).getUTCDay() + 6) % 7) * 24 * HOUR;\n const nextPeriod = range === 'month' ? Date.UTC(today.year!, today.month!, 1) : firstDay + 7 * 24 * HOUR;\n const days = (nextPeriod - firstDay) / (24 * HOUR);\n const bucketStarts = Array.from({ length: days }, (_, i) => midnight(firstDay + i * 24 * HOUR));\n return { startMs: bucketStarts[0]!, endMs: asOfMs, bucketMs: 24 * HOUR, bucketStarts, bucketEndMs: midnight(nextPeriod) };\n}\n",
19
19
  "import { createHash, timingSafeEqual } from 'node:crypto';\nimport { ANONYMOUS_PRINCIPAL, type DataPrincipal, type AdmissionTarget, type AdmissionDenial, type IngressPlugin } from '@jeffusion/bungee-core/plugin';\nimport { DataAdmissionError } from '@jeffusion/bungee-core/plugin';\n\nexport interface AccessPolicy { routes: string[] | null; models: string[] | null }\nexport interface AccessCredential {\n id: string; domain: 'data'; name: string; prefix: string; digest: string;\n createdAt: number; expiresAt: number | null; revokedAt: number | null; credentialVersion: number;\n}\nexport interface AccessPublication {\n protectedRouteIds: string[];\n byKey: Record<string, AccessPolicy | null>;\n credentials: AccessCredential[];\n}\nexport function stringIds(value: unknown): string[] {\n if (!Array.isArray(value) || value.length > 1000 || value.some(x => typeof x !== 'string' || !x.length || x.length > 256)\n || new Set(value).size !== value.length) throw new Error('invalid_policy');\n return [...value];\n}\nexport function validatePolicy(value: unknown): AccessPolicy | null {\n if (value === null) return null;\n if (!value || typeof value !== 'object' || Array.isArray(value) || Object.keys(value).some(k => !['routes', 'models'].includes(k))) throw new Error('invalid_policy');\n const p = value as AccessPolicy;\n return {routes: p.routes === null ? null : stringIds(p.routes), models: p.models === null ? null : stringIds(p.models)};\n}\nexport function validatePublication(value: unknown): AccessPublication {\n const p = value as AccessPublication;\n if (!p || typeof p !== 'object' || !p.byKey || typeof p.byKey !== 'object' || Array.isArray(p.byKey)\n || !Array.isArray(p.credentials)) throw new Error('invalid_policy');\n const protectedRouteIds = stringIds(p.protectedRouteIds);\n for (const key of p.credentials) {\n if (!key || key.domain !== 'data' || typeof key.id !== 'string' || !key.id || !/^[a-f0-9]{64}$/.test(key.digest)\n || !Number.isSafeInteger(key.credentialVersion) || key.credentialVersion < 1\n || (key.expiresAt !== null && !Number.isSafeInteger(key.expiresAt))\n || (key.revokedAt !== null && !Number.isSafeInteger(key.revokedAt))) throw new Error('invalid_credentials');\n }\n return {...p, protectedRouteIds};\n}\nexport function validPrincipal(principal: DataPrincipal, value: AccessPublication, now: number): boolean {\n const key = value.credentials.find(key => key.id === principal.keyId);\n return principal.domain === 'data' && !!key && key.credentialVersion === principal.credentialVersion\n && key.revokedAt === null && (key.expiresAt === null || key.expiresAt > now);\n}\n/** Only * is special; every other character is literal. Match the entire model. */\nfunction matchesModel(pattern: string, model: string): boolean {\n const parts = pattern.split('*');\n if (parts.length === 1) return pattern === model;\n const first = parts[0]!, last = parts[parts.length - 1]!;\n if (!model.startsWith(first) || !model.endsWith(last)) return false;\n let cursor = first.length;\n const end = model.length - last.length;\n for (const part of parts.slice(1, -1)) {\n const index = model.indexOf(part, cursor);\n if (index < 0) return false;\n cursor = index + part.length;\n }\n return cursor <= end;\n}\nfunction check(target: AdmissionTarget, policy: AccessPolicy | null): AdmissionDenial | null {\n if (target.principal.domain === 'anonymous') return null;\n if (policy && ((policy.routes !== null && !policy.routes.includes(target.routeId))\n || (policy.models !== null && (!target.model || !policy.models.some(pattern => matchesModel(pattern, target.model!)))))) return {error: 'key-access.scope_denied', status: 403};\n return null;\n}\nexport function createIngress(): IngressPlugin { return {\n bodyRequirements(target, value) {\n const publication = validatePublication(value);\n if (!publication.protectedRouteIds.includes(target.routeId) || target.principal.domain !== 'data') return { request: 'none' };\n const policy = validatePolicy(publication.byKey[target.principal.keyId] ?? null);\n const urlModel = /\\/models\\/([^/:]+):(?:streamGenerateContent|generateContent)$/.test(new URL(target.url).pathname);\n return { request: policy?.models !== null && policy?.models !== undefined && policy.models.length > 0 && !urlModel ? 'json-read' : 'none' };\n },\n authenticate(request, value, now) {\n const p = validatePublication(value);\n const match = /^Bearer (bng_data_[A-Za-z0-9_-]{43})$/i.exec(request.headers.get('authorization') ?? '');\n if (!match) return null;\n const digest = createHash('sha256').update(match[1]!).digest('hex');\n const key = p.credentials.find(key => timingSafeEqual(Buffer.from(key.digest, 'hex'), Buffer.from(digest, 'hex')));\n if (!key) return null;\n const principal = {domain: 'data', keyId: key.id, credentialVersion: key.credentialVersion};\n return validPrincipal(principal, p, now) ? principal : null;\n },\n resolveIdentity(target, value) {\n const p = validatePublication(value);\n if (!p.protectedRouteIds.includes(target.routeId)) return ANONYMOUS_PRINCIPAL;\n if (!validPrincipal(target.principal, p, target.now)) throw new DataAdmissionError(401, 'unauthorized');\n return target.principal;\n },\n plan(target, value) {\n try {\n const p = validatePublication(value);\n if (!p.protectedRouteIds.includes(target.routeId)) return {snapshot: null};\n if (target.principal.domain === 'anonymous') return {denial: {error: 'unauthorized', status: 401}};\n const policy = validatePolicy(p.byKey[target.principal.keyId] ?? null);\n const denial = check(target, policy);\n return denial ? {denial} : {snapshot: {routeId: target.routeId, policy: policy as any}};\n } catch { return {denial: {error: 'key-access.policy_unavailable', status: 503}}; }\n },\n beforeAttempt(target, snapshot) {\n if (snapshot === null) return target.principal.domain === 'anonymous' ? null : {error: 'key-access.invalid_grant', status: 503};\n try {\n const s = snapshot as {routeId: string; policy: AccessPolicy | null};\n if (!s || s.routeId !== target.routeId) return {error: 'key-access.scope_denied', status: 403};\n return check(target, validatePolicy(s.policy));\n } catch { return {error: 'key-access.policy_unavailable', status: 503}; }\n },\n}; }\n"
20
20
  ],
21
- "mappings": ";;AAAA;;;ACOA,IAAM,UAAU,OAAO,IAAI,mCAAmC;AAI9D,SAAS,YAAY,CAAC,WAAoB,CAAC,GAAkB;AAAA,EAC3D,MAAM,SAAS,CAAC,UACd,CAAC,OAAyB,YAAqB;AAAA,IAC7C,MAAM,OAAQ,WAAuC;AAAA,IACrD,IAAI,MAAM;AAAA,MACR,IAAI,OAAO,UAAU;AAAA,QAAU,KAAK,OAAO,UAAU,KAAK;AAAA,MACrD;AAAA,aAAK,OAAO,KAAI,aAAa,MAAK,GAAG,OAAO;AAAA,IACnD,EAAO,SAAI,UAAU,WAAW,QAAQ,IAAI,cAAc,SAAS;AAAA,MACjE,MAAM,QAAQ,UAAU,UAAU,QAAQ,QAAQ,QAAQ;AAAA,MAC1D,MAAM,YAAY,OAAO,UAAU,WAAW,QAAQ,KAAK,OAAO,UAAU,WAAW,WAAW,KAAI,aAAa,MAAK,CAAC;AAAA,IAC3H;AAAA;AAAA,EAEJ,OAAO;AAAA,IAAC,MAAM,OAAO,MAAM;AAAA,IAAG,MAAM,OAAO,MAAM;AAAA,IAAG,OAAO,OAAO,OAAO;AAAA,IAAG,OAAO,OAAO,OAAO;AAAA,IAC/F,OAAO,OAAO,OAAO;AAAA,IAAG,OAAO,WAAS,aAAa,KAAI,aAAa,MAAK,CAAC;AAAA,EAAC;AAAA;AAE1E,IAAM,SAAS,aAAa;;;ACd5B,IAAM,2BAA2B,OAAO,0BAA0B;;ACRlE,IAAM,sBAAqC,OAAO,OAAO,EAAC,QAAQ,aAAa,OAAO,IAAI,mBAAmB,EAAC,CAAC;;ACmCtH,IAAM,YAAY,OAAO;;ACjBzB;;;ACiGO,IAAM,qBAAqB,KAAK;AAEhC,IAAM,yBAAyB,MAAM;AAQ5C,IAAM,oBAAoB,IAAI,IAAoB,CAAC,aAAa,cAAc,cAAc,WAAW,SAAS,CAAC;AACjH,IAAM,4BAA4B,IAAI,IAA6B,CAAC,qBAAqB,qBAAqB,MAAM,CAAC;AACrH,IAAM,0BAA0B,IAAI,IAAI,CAAC,iBAAiB,qBAAqB,cAAc,gBAAgB,CAAC;;;ACxF9G,IAAM,YAAY,IAAI,IAAY,CAAC,WAAW,UAAU,SAAS,CAAC;AAClE,IAAM,SAAS,IAAI,IAAY,CAAC,UAAU,SAAS,CAAC;AACpD,IAAM,WAAW,IAAI,IAAY,CAAC,aAAa,cAAc,cAAc,WAAW,SAAS,CAAC;AAqQhG,IAAM,YAAY,IAAI;AAEtB,IAAM,UAAU,IAAI;;;AFtOpB,IAAM,yBAA8D;AAAA,EAClE,cAAc;AAAA,EACd,WAAW;AAAA,EACX,SAAS;AAAA,EACT,SAAS;AAAA,EACT,oBAAoB;AAAA,EACpB,eAAe;AAAA,EACf,eAAe;AAAA,EACf,gBAAgB;AAAA,EAChB,sBAAsB;AAAA,EACtB,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,SAAS;AAAA,EACT,SAAS;AAAA,EACT,wBAAwB;AAAA,EACxB,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,SAAS;AAAA,EACT,SAAS;AAAA,EACT,UAAU;AAAA,EACV,SAAS;AAAA,EACT,iBAAiB;AAAA,EACjB,UAAU;AAAA,EACV,qBAAqB;AAAA,EACrB,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,qBAAqB;AAAA,EACrB,sBAAsB;AACxB;AAEA,IAAM,sBAAsB,IAAI,IAAyB,OAAO,KAAK,sBAAsB,CAA0B;AA2RrH,IAAM,gBAAgC,OAAO,OAAO,CAAC,CAAC;AAWtD,IAAM,cAAc,IAAI;AA8CxB,IAAM,cAAc,IAAI;;AGnajB,IAAM,4BAA4B,IAAI;AAEtC,IAAM,2BAA2B,KAAK;AAEtC,IAAM,0BAA0B,KAAK;AAiDrC,IAAM,uBAAqD,OAAO,OAAO,CAAC,SAAS,YAAY,QAAQ,CAAC;AA4F/G,IAAM,cAAc,IAAI,IAA4B;AAAA,EAClD;AAAA,EAAe;AAAA,EAAa;AAAA,EAAgB;AAAA,EAAW;AAAA,EAAc;AAAA,EACrE;AAAA,EAAY;AAAA,EAAO;AAAA,EAAa;AAAA,EAAY;AAAA,EAAa;AAAA,EAAU;AAAA,EAAW;AAChF,CAAC;AAuCD,IAAM,qBAAoB,IAAI,IAAoB,CAAC,aAAa,cAAc,cAAc,WAAW,SAAS,CAAC;;;ACvI1G,IAAM,oCAAoC,IAAI;AAE9C,IAAM,iCAAiC,KAAK;AAE5C,IAAM,iCAAiC,KAAK,OAAO;AAEnD,IAAM,kCAAkC,IAAI;AAG5C,IAAM,sCAAsC,IAAI;AAchD,IAAM,6BAA6B,KAAK,OAAO;AAQ/C,IAAM,kCAAkC,KAAK;AAGpD,IAAM,QAAQ,IAAI,WAAW,CAAC;AA6gB9B,IAAM,uBAAuB,IAAI;;;ACjmBjC,IAAM,sBAAsB,KAAK;AAQjC,IAAM,uBAAuB,IAAI;;;AClBjC,IAAM,gBAAgB,IAAI,IAAI,CAAC,UAAU,YAAY,QAAQ,CAAC;;ACrB9D,IAAM,mBAAiB,OAAO,IAAI,wCAAwC;;ACC1E,IAAM,OAAO;AACN,IAAM,2BAA2B,KAAK,KAAK;;ACW3C,SAAS,SAAS,CAAC,OAA0B;AAAA,EAClD,IAAI,CAAC,MAAM,QAAQ,KAAK,KAAK,MAAM,SAAS,QAAQ,MAAM,KAAK,OAAK,OAAO,MAAM,YAAY,CAAC,EAAE,UAAU,EAAE,SAAS,GAAG,KACnH,IAAI,IAAI,KAAK,EAAE,SAAS,MAAM;AAAA,IAAQ,MAAM,IAAI,MAAM,gBAAgB;AAAA,EAC3E,OAAO,CAAC,GAAG,KAAK;AAAA;AAEX,SAAS,cAAc,CAAC,OAAqC;AAAA,EAClE,IAAI,UAAU;AAAA,IAAM,OAAO;AAAA,EAC3B,IAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,KAAK,OAAO,KAAK,KAAK,EAAE,KAAK,OAAK,CAAC,CAAC,UAAU,QAAQ,EAAE,SAAS,CAAC,CAAC;AAAA,IAAG,MAAM,IAAI,MAAM,gBAAgB;AAAA,EACpK,MAAM,IAAI;AAAA,EACV,OAAO,EAAC,QAAQ,EAAE,WAAW,OAAO,OAAO,UAAU,EAAE,MAAM,GAAG,QAAQ,EAAE,WAAW,OAAO,OAAO,UAAU,EAAE,MAAM,EAAC;AAAA;AAEjH,SAAS,mBAAmB,CAAC,OAAmC;AAAA,EACrE,MAAM,IAAI;AAAA,EACV,IAAI,CAAC,KAAK,OAAO,MAAM,YAAY,CAAC,EAAE,SAAS,OAAO,EAAE,UAAU,YAAY,MAAM,QAAQ,EAAE,KAAK,KAC9F,CAAC,MAAM,QAAQ,EAAE,WAAW;AAAA,IAAG,MAAM,IAAI,MAAM,gBAAgB;AAAA,EACpE,MAAM,oBAAoB,UAAU,EAAE,iBAAiB;AAAA,EACvD,WAAW,OAAO,EAAE,aAAa;AAAA,IAC/B,IAAI,CAAC,OAAO,IAAI,WAAW,UAAU,OAAO,IAAI,OAAO,YAAY,CAAC,IAAI,MAAM,CAAC,iBAAiB,KAAK,IAAI,MAAM,KAC1G,CAAC,OAAO,cAAc,IAAI,iBAAiB,KAAK,IAAI,oBAAoB,KACvE,IAAI,cAAc,QAAQ,CAAC,OAAO,cAAc,IAAI,SAAS,KAC7D,IAAI,cAAc,QAAQ,CAAC,OAAO,cAAc,IAAI,SAAS;AAAA,MAAI,MAAM,IAAI,MAAM,qBAAqB;AAAA,EAC9G;AAAA,EACA,OAAO,KAAI,GAAG,kBAAiB;AAAA;AAE1B,SAAS,cAAc,CAAC,WAA0B,OAA0B,KAAsB;AAAA,EACvG,MAAM,MAAM,MAAM,YAAY,KAAK,SAAO,IAAI,OAAO,UAAU,KAAK;AAAA,EACpE,OAAO,UAAU,WAAW,UAAU,CAAC,CAAC,OAAO,IAAI,sBAAsB,UAAU,qBAC9E,IAAI,cAAc,SAAS,IAAI,cAAc,QAAQ,IAAI,YAAY;AAAA;;;AdlC5E,IAAM,QAAQ,OAA0B,EAAC,OAAO,CAAC,GAAG,mBAAmB,CAAC,GAAG,aAAa,CAAC,EAAC;AAC1F,SAAS,OAAO,CAAC,OAAqC;AAAA,EACpD,MAAM,QAAQ,MAAM,IAAI,UAAU,GAAG;AAAA,EACrC,OAAO,UAAU,YAAY,MAAM,IAAI,oBAAoB,KAAK;AAAA;AAElE,SAAS,gBAAgB,CAAC,OAA0B;AAAA,EAClD,OAAO,OAAO,YAAY,MAAM,kBAAkB,IAAI,aAAW,CAAC,SAAS,MAAM,YAAY,OAAO,SAAO;AAAA,IACzG,MAAM,QAAQ,eAAe,MAAM,MAAM,IAAI,OAAO,IAAI;AAAA,IACxD,OAAO,OAAO,UAAU,QAAQ,MAAM,OAAO,SAAS,OAAO;AAAA,GAC9D,EAAE,IAAI,UAAQ,EAAC,IAAI,IAAI,IAAI,MAAM,IAAI,KAAI,EAAE,CAAC,CAAC,CAAC;AAAA;AAEjD,SAAS,QAAQ,CAAC,KAAuB;AAAA,EAAE,QAAO,QAAQ,YAAY,UAAS;AAAA,EAAK,OAAO;AAAA;AAC3F,SAAS,IAAI,CAAC,OAAgB,SAAS,KAAK;AAAA,EAAE,OAAO,SAAS,KAAK,OAAO,EAAC,QAAQ,SAAS,EAAC,iBAAiB,WAAU,EAAC,CAAC;AAAA;AAC1H,SAAS,KAAK,CAAC,SAA0B;AAAA,EACvC,MAAM,KAAK,mBAAmB,IAAI,IAAI,QAAQ,GAAG,EAAE,SAAS,MAAM,GAAG,EAAE,IAAI,KAAK,EAAE;AAAA,EAClF,IAAI,CAAC,sCAAsC,KAAK,EAAE;AAAA,IAAG,MAAM,IAAI,MAAM,gBAAgB;AAAA,EACrF,OAAO;AAAA;AAEF,SAAS,aAAa,CAAC,MAAyC;AAAA,EACrE,IAAI,CAAC,KAAK;AAAA,IAAc,MAAM,IAAI,MAAM,mCAAmC;AAAA,EAC3E,MAAM,QAAQ,KAAK;AAAA,EACnB,IAAI,UAAU,OAAO,mBAAmB;AAAA,EACxC,IAAI,SAA2B,QAAQ,QAAQ;AAAA,EAC/C,MAAM,SAAS,OAAO,EAAC,SAAS,MAAM,IAAI,UAAU,GAAG,WAAW,GAAG,OAAO,QAAQ,KAAK,EAA2B;AAAA,EACpH,MAAM,QAAQ,MAAM,WAAW,CAAC,KAAK,OAAO,WAAW,qBAAqB,OAAO,EAAE;AAAA,EACrF,eAAe,OAAO,GAAG;AAAA,IACvB,IAAI,CAAC,KAAK;AAAA,MAAe,MAAM,IAAI,MAAM,yBAAyB;AAAA,IAClE,MAAM,OAAO,OAAO;AAAA,IACpB,MAAM,KAAK,cAAc,IAAI;AAAA,IAC7B,mBAAmB,KAAK;AAAA;AAAA,EAE1B,eAAe,IAAI,CAAC,OAA0B,QAAiC,SAAS,KAAwB;AAAA,IAC9G,MAAM,MAAM,MAAM,IAAI,UAAU;AAAA,IAChC,MAAM,QAAQ,EAAC,WAAW,WAAW,GAAG,WAAW,CAAC,EAAC,KAAK,YAAY,iBAAiB,KAAK,WAAW,GAAG,MAAsC,CAAC,EAAC,CAAC;AAAA,IACnJ,IAAI;AAAA,MAAE,MAAM,QAAQ;AAAA,MAAG,OAAO,KAAK,KAAI,QAAQ,SAAS,OAAO,EAAE,SAAS,OAAO,MAAM,GAAG,WAAW,KAAI,GAAG,MAAM;AAAA,MAClH,MAAM;AAAA,MAAE,OAAO,KAAK,KAAI,QAAQ,OAAO,kCAAkC,WAAW,MAAM,SAAS,OAAO,EAAE,SAAS,OAAO,OAAO,QAAQ,OAAO,WAAW,MAAK,GAAG,GAAG;AAAA;AAAA;AAAA,EAE1K,SAAS,KAAK,CAAC,WAAuD;AAAA,IACpE,MAAM,OAAO,OAAO,MAAM,MAAG;AAAA,MAAG;AAAA,KAAS,EAAE,KAAK,YAAY;AAAA,MAC1D,IAAI,CAAC,WAAW,KAAK,OAAO;AAAA,QAAS,OAAO,KAAK,EAAC,OAAO,sBAAqB,GAAG,GAAG;AAAA,MACpF,IAAI;AAAA,QAAE,OAAO,MAAM,UAAU;AAAA,QAAK,MAAM;AAAA,QAAE,OAAO,KAAK,EAAC,OAAO,2BAA0B,GAAG,GAAG;AAAA;AAAA,KAC/F;AAAA,IACD,SAAS;AAAA,IAAM,OAAO;AAAA;AAAA,EAExB,OAAO;AAAA,IAAC;AAAA,IAAQ,KAAK,CAAC;AAAA,IAAG,KAAK;AAAA,MAC5B,EAAC,MAAM,gBAAgB,SAAS,CAAC,OAAO,MAAM,GAAG,SAAS,oBAAqB,OAAM,CAAC,KAAK;AAAA,QACzF,IAAI,IAAI,QAAQ,WAAW,OAAO;AAAA,UAChC,MAAM,OAAO,MAAM,QAAQ,IAAI,QAAQ,KAAK,EAAE,YAAY,IAAI,OAAM,SAAQ;AAAA,eAAI,SAAS,GAAG;AAAA,eACtF,KAAK,yBAAyB,EAAC,YAAY,MAAM,KAAK,uBAAuB,IAAI,EAAE,EAAC,IAAI,CAAC;AAAA,UAAE,EAAE,CAAC;AAAA,UACpG,OAAO,KAAK,EAAC,MAAM,OAAO,MAAM,GAAG,WAAW,MAAM,EAAC,CAAC;AAAA,QACxD;AAAA,QACA,OAAO,MAAM,YAAY;AAAA,UACvB,MAAM,QAAQ,MAAM,IAAI,QAAQ,KAAK;AAAA,UACrC,IAAI,CAAC,SAAS,OAAO,KAAK,KAAK,EAAE,KAAK,OAAK,CAAC,CAAC,QAAQ,WAAW,EAAE,SAAS,CAAC,CAAC,KAAK,OAAO,MAAM,SAAS,YACnG,CAAC,MAAM,KAAK,KAAK,KAAK,MAAM,KAAK,SAAS,OAAO,kBAAkB,KAAK,MAAM,IAAI;AAAA,YAAG,OAAO,KAAK,EAAC,OAAO,mBAAkB,GAAG,GAAG;AAAA,UACtI,MAAM,YAAY,MAAM,aAAa,MAAM,MAAM,KAAK,IAAI;AAAA,UAC1D,IAAI,cAAc,SAAS,CAAC,OAAO,cAAc,SAAS,KAAK,aAAa;AAAA,YAAM,OAAO,KAAK,EAAC,OAAO,qBAAoB,GAAG,GAAG;AAAA,UAChI,MAAM,OAAO,QAAQ,KAAK;AAAA,UAC1B,IAAI,KAAK,YAAY,KAAK,SAAO,IAAI,KAAK,KAAK,MAAM,MAAM,KAAK,KAAK,CAAC;AAAA,YAAG,OAAO,KAAK,EAAC,OAAO,kBAAiB,GAAG,GAAG;AAAA,UACpH,MAAM,QAAQ,YAAY,YAAY,EAAE,EAAE,SAAS,WAAW;AAAA,UAC9D,MAAM,MAAwB;AAAA,YAAC,IAAI,WAAW;AAAA,YAAG,QAAQ;AAAA,YAAQ,MAAM,MAAM,KAAK,KAAK;AAAA,YAAG,QAAQ,MAAM,MAAM,GAAG,EAAE;AAAA,YACjH,QAAQ,WAAW,QAAQ,EAAE,OAAO,KAAK,EAAE,OAAO,KAAK;AAAA,YAAG,WAAW;AAAA,YAAK;AAAA,YAAW,WAAW;AAAA,YAAM,mBAAmB;AAAA,UAAC;AAAA,UAC5H,KAAK,YAAY,KAAK,GAAG;AAAA,UAEzB,KAAK,MAAM,IAAI,MAAM,EAAC,QAAQ,CAAC,GAAG,QAAQ,KAAI;AAAA,UAC9C,IAAI;AAAA,YAAE,MAAM,KAAK,YAAY,cAAc,gBAAgB,IAAI,IAAI,MAAM,KAAK;AAAA,YAC9E,MAAM;AAAA,YAAE,OAAO,KAAK,EAAC,OAAO,yBAAwB,GAAG,GAAG;AAAA;AAAA,UAC1D,IAAI;AAAA,YAAE,OAAO,MAAM,KAAK,MAAM,EAAC,KAAK,SAAS,GAAG,GAAG,MAAK,GAAG,GAAG;AAAA,YAC9D,OAAO,OAAO;AAAA,YAEZ,IAAI,CAAC,QAAQ,KAAK,EAAE,YAAY,KAAK,UAAQ,KAAK,OAAO,IAAI,EAAE,GAAG;AAAA,cAChE,MAAM,SAAS,MAAM,KAAK,YAAY,IAAI,gBAAgB,IAAI,EAAE;AAAA,cAChE,IAAI;AAAA,gBAAQ,MAAM,KAAK,YAAY,OAAO,gBAAgB,IAAI,IAAI,OAAO,OAAO;AAAA,YAClF;AAAA,YACA,MAAM;AAAA;AAAA,SAET;AAAA,QACF;AAAA,MACD,EAAC,MAAM,uBAAuB,SAAS,CAAC,OAAO,OAAO,QAAQ,GAAG,SAAS,mBAAoB,OAAM,CAAC,KAAK;AAAA,QACxG,MAAM,KAAK,MAAM,IAAI,OAAO;AAAA,QAC5B,IAAI,IAAI,QAAQ,WAAW,OAAO;AAAA,UAChC,MAAM,MAAM,QAAQ,KAAK,EAAE,YAAY,KAAK,SAAO,IAAI,OAAO,EAAE;AAAA,UAChE,IAAI,CAAC;AAAA,YAAK,OAAO,KAAK,EAAC,OAAO,gBAAe,GAAG,GAAG;AAAA,UACnD,IAAI;AAAA,YACF,MAAM,SAAS,MAAM,KAAK,YAAY,IAAI,gBAAgB,EAAE;AAAA,YAC5D,IAAI,CAAC;AAAA,cAAQ,OAAO,KAAK,EAAC,OAAO,wBAAuB,GAAG,GAAG;AAAA,YAC9D,IAAI,CAAC,QAAQ,KAAK,EAAE,YAAY,KAAK,SAAO,IAAI,OAAO,EAAE;AAAA,cAAG,OAAO,KAAK,EAAC,OAAO,gBAAe,GAAG,GAAG;AAAA,YACrG,IAAI,WAAW,QAAQ,EAAE,OAAO,OAAO,KAAK,EAAE,OAAO,KAAK,MAAM,IAAI;AAAA,cAAQ,OAAO,KAAK,EAAC,OAAO,yBAAwB,GAAG,GAAG;AAAA,YAC9H,OAAO,KAAK,EAAC,OAAO,OAAO,MAAK,CAAC;AAAA,YACjC,MAAM;AAAA,YAAE,OAAO,KAAK,EAAC,OAAO,yBAAwB,GAAG,GAAG;AAAA;AAAA,QAC9D;AAAA,QACA,OAAO,MAAM,YAAY;AAAA,UACvB,MAAM,OAAO,QAAQ,KAAK,GAAG,MAAM,KAAK,YAAY,KAAK,SAAO,IAAI,OAAO,EAAE;AAAA,UAC7E,IAAI,IAAI,QAAQ,WAAW,UAAU;AAAA,YACnC,KAAK,cAAc,KAAK,YAAY,OAAO,SAAO,IAAI,OAAO,EAAE;AAAA,YAC/D,OAAO,KAAK,MAAM;AAAA,YAClB,MAAM,SAAS,MAAM,KAAK,MAAM,EAAC,SAAS,MAAM,GAAE,CAAC;AAAA,YACnD,IAAI;AAAA,cACF,MAAM,SAAS,MAAM,KAAK,YAAY,IAAI,gBAAgB,EAAE;AAAA,cAC5D,IAAI;AAAA,gBAAQ,MAAM,KAAK,YAAY,OAAO,gBAAgB,IAAI,OAAO,OAAO;AAAA,cAC5E,MAAM;AAAA,cAAE,OAAO,KAAK,KAAI,MAAM,OAAO,KAAK,GAAE,OAAM,8BAA8B,WAAU,MAAM,SAAQ,MAAM,GAAE,GAAG,GAAG;AAAA;AAAA,YACxH,OAAO;AAAA,UACT;AAAA,UACA,IAAI,CAAC,OAAO,IAAI,cAAc;AAAA,YAAM,OAAO,KAAK,EAAC,OAAM,gBAAe,GAAG,GAAG;AAAA,UAC5E,MAAM,QAAQ,MAAM,IAAI,QAAQ,KAAK;AAAA,UACrC,IAAI,CAAC,SAAS,OAAO,KAAK,KAAK,EAAE,KAAK,OAAK,CAAC,CAAC,QAAO,aAAY,UAAS,QAAQ,EAAE,SAAS,CAAC,CAAC,KAAK,OAAO,MAAM,SAAS,YACpH,CAAC,MAAM,KAAK,KAAK,KAAK,MAAM,KAAK,SAAS,OAAO,kBAAkB,KAAK,MAAM,IAAI;AAAA,YAAG,OAAO,KAAK,EAAC,OAAM,mBAAkB,GAAE,GAAG;AAAA,UACpI,IAAI,KAAK,YAAY,KAAK,WAAS,MAAM,OAAO,MAAM,MAAM,KAAK,KAAK,MAAM,MAAM,KAAK,KAAK,CAAC;AAAA,YAAG,OAAO,KAAK,EAAC,OAAO,kBAAiB,GAAG,GAAG;AAAA,UAC3I,MAAM,YAAY,MAAM,aAAa;AAAA,UACrC,IAAI,cAAc,SAAS,CAAC,OAAO,cAAc,SAAS,KAAM,aAAa,KAAK,IAAI,KAAK,cAAc,IAAI;AAAA,YAAa,OAAO,KAAK,EAAC,OAAM,qBAAoB,GAAE,GAAG;AAAA,UACtK,MAAM,QAAQ,eAAe,EAAC,QAAQ,MAAM,QAAQ,QAAQ,MAAM,OAAM,CAAC;AAAA,UACzE,IAAI,CAAC,KAAK;AAAA,YAA6B,OAAO,KAAK,EAAC,OAAM,kCAAiC,GAAE,GAAG;AAAA,UAChG,IAAI,CAAC,MAAM,KAAK,4BAA4B,IAAG,KAAK;AAAA,YAAG,OAAO,KAAK,EAAC,OAAM,qBAAoB,GAAE,GAAG;AAAA,UACnG,IAAI,OAAO,MAAM,KAAK,KAAK;AAAA,UAAG,IAAI,YAAY;AAAA,UAAW,KAAK,MAAM,MAAM;AAAA,UAC1E,OAAO,KAAK,MAAK,EAAC,KAAK,SAAS,GAAG,GAAE,MAAK,CAAC;AAAA,SAC5C;AAAA,QACF;AAAA,MACD,EAAC,MAAM,WAAW,SAAS,CAAC,OAAO,KAAK,GAAG,SAAS,wBAAyB,OAAM,CAAC,KAAK;AAAA,QACvF,IAAI,IAAI,QAAQ,WAAW,OAAO;AAAA,UAAE,MAAM,QAAQ,QAAQ,KAAK;AAAA,UAAG,OAAO,KAAK,EAAC,mBAAmB,MAAM,mBAAmB,kBAAkB,iBAAiB,KAAK,GAAG,SAAS,OAAO,EAAE,SAAS,OAAO,MAAM,GAAG,WAAW,MAAM,EAAC,CAAC;AAAA,QAAG;AAAA,QACvO,OAAO,MAAM,YAAY;AAAA,UACvB,MAAM,QAAQ,MAAM,IAAI,QAAQ,KAAK;AAAA,UACrC,IAAI,CAAC,SAAS,OAAO,KAAK,KAAK,EAAE,KAAK,MAAM;AAAA,YAAqB,OAAO,KAAK,EAAC,OAAO,iBAAgB,GAAG,GAAG;AAAA,UAC3G,MAAM,oBAAoB,UAAU,MAAM,iBAAiB;AAAA,UAC3D,IAAI,CAAC,KAAK;AAAA,YAAyB,OAAO,KAAK,EAAC,OAAO,kCAAiC,GAAG,GAAG;AAAA,UAC9F,IAAI,CAAC,MAAM,KAAK,wBAAwB,iBAAiB;AAAA,YAAG,OAAO,KAAK,EAAC,OAAO,qBAAoB,GAAG,GAAG;AAAA,UAC1G,MAAM,QAAQ,QAAQ,KAAK,GAAG,WAAW,iBAAiB,KAAK;AAAA,UAC/D,MAAM,kBAAkB,MAAM,kBAAkB,OAAO,QAAM,CAAC,kBAAkB,SAAS,EAAE,KAAK,SAAS,IAAK,SAAS,CAAC;AAAA,UACxH,IAAI,gBAAgB;AAAA,YAAQ,OAAO,KAAK,EAAC,OAAM,sBAAsB,iBAAiB,kBAAkB,SAAQ,GAAE,GAAG;AAAA,UACrH,MAAM,OAAO,KAAI,OAAO,kBAAiB;AAAA,UACzC,OAAO,KAAK,MAAM,EAAC,mBAAmB,kBAAkB,iBAAiB,IAAI,EAAC,CAAC;AAAA,SAChF;AAAA,QACF;AAAA,MACD,EAAC,MAAM,cAAc,SAAS,CAAC,KAAK,GAAG,SAAS,sBAAuB,OAAM,CAAC,KAAK;AAAA,QACjF,OAAO,MAAM,YAAY;AAAA,UACvB,MAAM,QAAQ,MAAM,IAAI,QAAQ,KAAK;AAAA,UACrC,IAAI,CAAC,SAAS,OAAO,KAAK,KAAK,EAAE,KAAK,OAAK,CAAC,CAAC,WAAU,SAAQ,SAAS,EAAE,SAAS,CAAC,CAAC,KAChF,OAAO,MAAM,YAAY,YAAY,OAAO,MAAM,UAAU,YAAY,OAAO,MAAM,YAAY;AAAA,YAAW,OAAO,KAAK,EAAC,OAAM,kBAAiB,GAAE,GAAG;AAAA,UAC1J,MAAM,OAAO,QAAQ,KAAK;AAAA,UAC1B,IAAI,CAAC,KAAK,YAAY,KAAK,SAAO,IAAI,OAAO,MAAM,SAAS,IAAI,cAAc,IAAI;AAAA,YAAG,OAAO,KAAK,EAAC,OAAM,gBAAe,GAAE,GAAG;AAAA,UAC5H,IAAI,CAAC,KAAK,2BAA2B,CAAC,KAAK;AAAA,YAA6B,OAAO,KAAK,EAAC,OAAM,kCAAiC,GAAE,GAAG;AAAA,UACjI,IAAI,CAAC,MAAM,KAAK,wBAAwB,CAAC,MAAM,OAAO,CAAC;AAAA,YAAG,OAAO,KAAK,EAAC,OAAM,qBAAoB,GAAE,GAAG;AAAA,UACtG,IAAI,CAAC,KAAK,kBAAkB,SAAS,MAAM,OAAO,KAAK,CAAC,MAAM;AAAA,YAAS,OAAO,KAAK,EAAC,OAAM,yCAAwC,GAAE,GAAG;AAAA,UACvI,MAAM,WAAW,eAAe,KAAK,MAAM,MAAM,UAAU,IAAI;AAAA,UAC/D,MAAM,QAAQ,EAAC,QAAQ,UAAU,UAAU,OAAO,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,SAAS,QAAO,MAAM,OAAO,CAAC,CAAC,GAAG,QAAO,UAAU,UAAU,KAAI;AAAA,UAC1I,IAAI,CAAC,MAAM,KAAK,4BAA4B,MAAM,OAAM,KAAK;AAAA,YAAG,OAAO,KAAK,EAAC,OAAM,qBAAoB,GAAE,GAAG;AAAA,UAC5G,KAAK,MAAM,MAAM,SAAS;AAAA,UAC1B,KAAK,oBAAoB,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,KAAK,mBAAkB,MAAM,OAAO,CAAC,CAAC;AAAA,UAC/E,OAAO,KAAK,MAAK,EAAC,mBAAkB,KAAK,mBAAkB,kBAAiB,iBAAiB,IAAI,EAAC,CAAC;AAAA,SACpG;AAAA,QACF;AAAA,MACD,EAAC,MAAM,gBAAgB,SAAS,CAAC,OAAO,KAAK,GAAG,SAAS,kBAAmB,OAAM,CAAC,KAAK;AAAA,QACtF,MAAM,KAAK,MAAM,IAAI,OAAO;AAAA,QAC5B,IAAI,IAAI,QAAQ,WAAW;AAAA,UAAO,OAAO,KAAK,EAAC,OAAO,IAAI,SAAS,OAAO,EAAE,SAAS,OAAO,QAAQ,KAAK,EAAE,MAAM,OAAO,MAAM,QAAQ,MAAM,GAAG,OAAO,MAAM,EAAC,CAAC;AAAA,QAC9J,OAAO,MAAM,YAAY;AAAA,UACvB,MAAM,OAAO,QAAQ,KAAK;AAAA,UAC1B,IAAI,CAAC,KAAK,YAAY,KAAK,SAAO,IAAI,OAAO,MAAM,IAAI,cAAc,IAAI;AAAA,YAAG,OAAO,KAAK,EAAC,OAAO,gBAAe,GAAG,GAAG;AAAA,UACrH,MAAM,QAAQ,eAAe,MAAM,IAAI,QAAQ,KAAK,CAAC;AAAA,UACrD,IAAI,CAAC,KAAK;AAAA,YAA6B,OAAO,KAAK,EAAC,OAAO,kCAAiC,GAAG,GAAG;AAAA,UAClG,IAAI,CAAC,MAAM,KAAK,4BAA4B,IAAI,KAAK;AAAA,YAAG,OAAO,KAAK,EAAC,OAAO,qBAAoB,GAAG,GAAG;AAAA,UACtG,KAAK,MAAM,MAAM;AAAA,UACjB,OAAO,KAAK,MAAM,EAAC,OAAO,IAAI,OAAO,QAAQ,KAAI,CAAC;AAAA,SACnD;AAAA,QACF;AAAA,IACH;AAAA,SACQ,MAAK,GAAG;AAAA,MAAE,IAAI,KAAK,OAAO;AAAA,QAAS,MAAM,IAAI,MAAM,UAAU;AAAA,MAAG,MAAM,QAAQ;AAAA,MAAG,UAAU;AAAA;AAAA,IACjG,OAAO,GAAG;AAAA,MAAE,UAAU;AAAA;AAAA,EACxB;AAAA;AAEK,SAAS,YAAY,CAAC,UAAkB,OAAe,OAAkB;AAAA,EAC9E,IAAI,aAAa;AAAA,IAAW,MAAM,IAAI,MAAM,wBAAwB;AAAA,EACpE,OAAO,EAAC,OAAO,QAAQ,KAAK,EAAE,MAAM,UAAU,KAAI;AAAA;AAE7C,SAAS,sBAAsB,CAAC,UAAkB,OAAsC;AAAA,EAC7F,IAAI,aAAa;AAAA,IAAW,MAAM,IAAI,MAAM,wBAAwB;AAAA,EACpE,OAAO,QAAQ,KAAK,EAAE,YAAY,IAAI,QAAQ;AAAA;AAEzC,SAAS,yBAAyB,CAAC,OAAqC;AAAA,EAAE,OAAO,QAAQ,KAAK,EAAE;AAAA;AAChG,SAAS,mBAAmB,CAAC,WAA0B,OAA2B;AAAA,EAAE,OAAO,eAAe,WAAW,QAAQ,KAAK,GAAG,KAAK,IAAI,CAAC;AAAA;AACtJ,IAAe,oBAAC,eAAe,cAAc,wBAAwB,2BAA2B,oBAAmB;",
22
- "debugId": "4AF8389FB39A168C64756E2164756E21",
21
+ "mappings": "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",
22
+ "debugId": "13B684B8C247D4F064756E2164756E21",
23
23
  "names": []
24
24
  }
@@ -132,12 +132,8 @@
132
132
  "ui.configureDescription": "Configure access credentials for route {path}.",
133
133
  "ui.keyCount": "{count, plural, one {# KEY} other {# KEYS}}",
134
134
  "ui.routeCount": "{count, plural, one {# ROUTE} other {# ROUTES}}",
135
- "ui.cannotMakePublic": "{path} cannot be made public",
136
135
  "ui.isRoutePublic": "Public access for {path}",
137
136
  "ui.deletedRoute": "Deleted route: {id} (remove it)",
138
- "ui.selectedPublicWarning": "{count, plural, one {The selected public route will stop} other {The # selected public routes will stop}} accepting anonymous requests after confirmation.",
139
- "ui.reviewWarning": "{count, plural, one {The following route will stop} other {The following # routes will stop}} accepting anonymous requests. Callers must provide a valid, authorized Key.",
140
- "ui.applyRouteWarning": "{path} will stop accepting anonymous requests and allow {name} to call it.",
141
137
  "ui.inactive": "The plugin is disabled; Keys are read-only. Enable it to manage Keys and route protection.",
142
138
  "ui.configurationPending": "Configuration saved; publication is still pending.",
143
139
  "ui.expirationFuture": "The expiration time must be later than now.",
@@ -145,31 +141,24 @@
145
141
  "ui.secretFailed": "Unable to read the Key. Please try again later.",
146
142
  "ui.deletedRouteError": "The allowed list contains deleted routes. Remove them before saving.",
147
143
  "ui.keyRoutesPending": "Allowed routes saved, but publication is still pending.",
148
- "ui.protectionPending": "Route protection saved, but publication is still pending.",
149
- "ui.createdPending": "Key created and saved, but publication is still pending. Allowed routes and route protection have not been configured. Save the plaintext, then edit this Key after publication recovers to finish setup.",
150
- "ui.policyPublished": "Allowed routes and selected protection settings saved and published.",
144
+ "ui.createdPending": "Key created and saved, but publication is still pending. Allowed routes have not been configured. Save the plaintext, then edit this Key after publication recovers to finish setup.",
145
+ "ui.policyPublished": "Key permissions saved and published. Route public access stays unchanged.",
151
146
  "ui.routeHelp": "Manage access to Bungee Routes. Public routes need no credentials; protected routes only accept valid, authorized Keys. Deleting a Key does not make routes public.",
152
- "ui.reviewHelp": "The access permissions for this Key will also be saved. Existing authorized Keys retain access; other routes keep their current access settings.",
153
147
  "ui.deleteHelp": "After deletion, this Key cannot start new requests; accepted requests continue. Route public access stays unchanged.",
154
- "ui.allRoutesHelp": "Allow access to current and future routes; only current public routes will be changed to require a Key now.",
148
+ "ui.allRoutesHelp": "Allow access to current and future routes. Route public access stays unchanged.",
155
149
  "ui.keyHelp": "Keys provide access to routes that require them. Public routes need no credentials.",
156
- "ui.allowedRoutesHelp": "Select the Bungee routes that this Key can call.",
150
+ "ui.allowedRoutesHelp": "Select the Bungee routes that this Key can call. This does not change public access.",
157
151
  "ui.modelsHelp": "Supports * wildcards and is case-sensitive. Validation uses the model actually sent upstream.",
158
152
  "ui.editHelp": "Change the name, expiration, and access permissions. Removing authorization does not make routes public.",
159
153
  "ui.noRoutesSelected": "No routes selected: this Key has no access to protected routes yet.",
160
154
  "ui.createHelp": "Assign credentials to an app. You can view the Key from the list at any time.",
161
- "ui.applyHelp": "Apply directly to this route while preserving the Key’s other permissions.",
162
- "ui.createReviewHelp": "Create the Key and apply the following access changes after confirmation.",
155
+ "ui.applyHelp": "Allow this Key to call the route while preserving its other permissions and the route’s public access setting.",
163
156
  "ui.notReady": "The plugin is not ready or configuration is pending publication. Refresh the runtime status.",
164
- "ui.publicBlockedTitle": "This route is linked to API Keys and cannot be made public",
165
- "ui.editReviewHelp": "Save permissions and apply the following access changes after confirmation.",
166
- "ui.publicBlockedHelp": "Remove authorization for this route or delete the Keys first.",
167
157
  "ui.allowedModels": "Allowed models (one per line; leave blank to allow all)",
168
158
  "ui.createRoutesFirst": "No routes yet. Create one in route management first.",
169
159
  "ui.noUsableKeys": "No Keys available to configure. Create one first.",
170
160
  "ui.chooseExistingKey": "Choose an existing API Key",
171
161
  "ui.noKeys": "No API Keys yet.",
172
- "ui.publicToProtected": "Public → Requires Key",
173
162
  "ui.createKey": "Create API Key",
174
163
  "ui.configureKey": "Configure API Key",
175
164
  "ui.viewKey": "View API Key",
@@ -180,13 +169,11 @@
180
169
  "ui.loadingKeys": "Loading Keys",
181
170
  "ui.expiration": "Expiration time (optional)",
182
171
  "ui.modelScopeOptional": "Model scope (optional)",
183
- "ui.reviewTitle": "Confirm route access changes",
184
172
  "ui.copied": "Copied to clipboard.",
185
173
  "ui.keys": "API Key",
186
174
  "ui.settings": "Access control settings",
187
175
  "ui.loadingRoutes": "Loading routes",
188
176
  "ui.requiresKey": "Requires Key",
189
- "ui.keyName": "Key name",
190
177
  "ui.loadingPolicy": "Loading policy",
191
178
  "ui.noRoutes": "No routes yet.",
192
179
  "ui.allowedRoutes": "Allowed routes",
@@ -196,15 +183,11 @@
196
183
  "ui.public": "Public access",
197
184
  "ui.basicInfo": "Basic information",
198
185
  "ui.allRoutes": "Any route",
199
- "ui.modelScope": "Model scope",
200
- "ui.allModels": "All models",
201
186
  "ui.saving": "Saving…",
202
187
  "ui.copyAgain": "Copy again",
203
188
  "ui.copyPlaintext": "Copy plaintext",
204
189
  "ui.confirmDelete": "Confirm deletion",
205
190
  "ui.validity": "Validity",
206
- "ui.back": "Back",
207
- "ui.next": "Next",
208
191
  "ui.applied": "Applied",
209
192
  "ui.refresh": "Refresh",
210
193
  "ui.name": "Name",
@@ -219,17 +202,13 @@
219
202
  "ui.cancel": "Cancel",
220
203
  "ui.save": "Save",
221
204
  "ui.apply": "Apply",
222
- "ui.publicBlockedReason": "Linked API Keys: {keys}. Remove authorization for this route or delete the Keys before making it public.",
223
205
  "ui.keyNameExists": "An API Key with this name already exists. Use another name.",
224
- "ui.routeHasKeys": "This route is linked to API Keys. Remove authorization or delete the Keys before making it public.",
225
206
  "ui.persistedPending": "Configuration saved, but publication is still pending. Refresh the runtime status and confirm it has taken effect before continuing.",
226
207
  "ui.operationFailed": "Operation failed",
227
208
  "ui.stageCreate": "Creating the Key",
228
- "ui.stageSavePolicy": "Saving allowed routes and route protection",
229
- "ui.createdProtected": "Key created; allowed routes saved and published. Protection for the selected public routes has been published.",
209
+ "ui.stageSavePolicy": "Saving Key permissions",
230
210
  "ui.createdPublic": "Key created; allowed routes saved and published. Public routes remain public.",
231
211
  "ui.createdIncomplete": "Key created. {stage} is incomplete: {reason} Save the plaintext, then edit this Key to finish setup.",
232
- "ui.keySavedProtectionIncomplete": "Allowed routes saved; route protection is incomplete: {reason}",
233
212
  "ui.clipboardUnavailable": "Clipboard unavailable. Copy the plaintext manually.",
234
213
  "ui.publicationPending": "Saved; publication is still pending. Refresh the runtime status.",
235
214
  "ui.publicationComplete": "Saved and published.",
@@ -253,12 +232,8 @@
253
232
  "ui.configureDescription": "为路由 {path} 配置访问凭证。",
254
233
  "ui.keyCount": "{count} 个 Key",
255
234
  "ui.routeCount": "{count} 条路由",
256
- "ui.cannotMakePublic": "{path} 无法设为公开",
257
235
  "ui.isRoutePublic": "{path} 是否公开",
258
236
  "ui.deletedRoute": "已删除路由:{id}(请移除)",
259
- "ui.selectedPublicWarning": "选中的 {count} 条公开路由将在提交确认后停止匿名访问。",
260
- "ui.reviewWarning": "以下 {count} 条路由将停止匿名访问。调用方必须携带有效且获授权的 Key。",
261
- "ui.applyRouteWarning": "{path} 将停止匿名访问,并允许 {name} 调用。",
262
237
  "ui.inactive": "插件已停用,Key 只读。启用插件后可管理 Key 与路由保护。",
263
238
  "ui.configurationPending": "配置已保存,当前发布尚未完成。",
264
239
  "ui.expirationFuture": "到期时间必须晚于当前时间。",
@@ -266,31 +241,24 @@
266
241
  "ui.secretFailed": "无法读取 Key,请稍后重试。",
267
242
  "ui.deletedRouteError": "允许列表包含已删除的路由,请移除后保存。",
268
243
  "ui.keyRoutesPending": "Key 允许路由已保存,但发布尚未完成。",
269
- "ui.protectionPending": "路由保护已保存,但发布尚未完成。",
270
- "ui.createdPending": "Key 已创建并保存,发布尚未完成;允许路由和路由保护步骤尚未执行。请保存明文,发布恢复后编辑此 Key 完成设置。",
271
- "ui.policyPublished": "Key 允许路由与所选保护设置已保存并发布。",
244
+ "ui.createdPending": "Key 已创建并保存,发布尚未完成;允许路由尚未配置。请保存明文,发布恢复后编辑此 Key 完成设置。",
245
+ "ui.policyPublished": "Key 权限已保存并发布,路由公开状态保持不变。",
272
246
  "ui.routeHelp": "管理 Bungee Route 的访问方式。公开路由无需凭证;需要 Key 的路由只接受有效且获授权的 Key。删除 Key 不会让路由变为公开。",
273
- "ui.reviewHelp": "本次同时保存此 Key 的访问权限。已有获授权的 Key 仍可访问;其他路由保持原来的访问方式。",
274
247
  "ui.deleteHelp": "删除后此 Key 无法再发起新请求,已接纳的请求继续。路由的公开状态保持不变。",
275
- "ui.allRoutesHelp": "包含当前和后续新增路由的访问权限;本次只将当前公开路由改为需要 Key。",
248
+ "ui.allRoutesHelp": "包含当前和后续新增路由的访问权限,路由公开状态保持不变。",
276
249
  "ui.keyHelp": "Key 用于访问需要 Key 的路由。公开路由无需凭证。",
277
- "ui.allowedRoutesHelp": "从 Bungee 路由中选择此 Key 可以调用的入口。",
250
+ "ui.allowedRoutesHelp": "从 Bungee 路由中选择此 Key 可以调用的入口,不改变路由的公开状态。",
278
251
  "ui.modelsHelp": "支持 * 通配符,区分大小写;按实际发往上游的模型校验。",
279
252
  "ui.editHelp": "修改名称、有效期和访问权限。取消授权不会将路由改为公开。",
280
253
  "ui.noRoutesSelected": "未选择路由:此 Key 暂无受保护路由的访问权限。",
281
254
  "ui.createHelp": "为应用分配调用凭证。可在列表中随时查看 Key。",
282
- "ui.applyHelp": "直接应用到当前路由,保留 Key 的其他权限。",
283
- "ui.createReviewHelp": "确认后创建 Key,并应用下列访问方式变更。",
255
+ "ui.applyHelp": "允许此 Key 调用当前路由,保留其他权限和路由的公开状态。",
284
256
  "ui.notReady": "插件尚未就绪或配置待发布;请刷新运行状态。",
285
- "ui.publicBlockedTitle": "此路由已关联 API Key,无法设为公开",
286
- "ui.editReviewHelp": "确认后保存权限,并应用下列访问方式变更。",
287
- "ui.publicBlockedHelp": "请先解除该路由的授权或删除 Key。",
288
257
  "ui.allowedModels": "允许模型(每行一个,留空允许全部)",
289
258
  "ui.createRoutesFirst": "尚无路由,请先在路由管理中创建。",
290
259
  "ui.noUsableKeys": "暂无可配置的 Key,请先新建。",
291
260
  "ui.chooseExistingKey": "选择已有 API Key",
292
261
  "ui.noKeys": "尚无 API Key。",
293
- "ui.publicToProtected": "公开 → 需要 Key",
294
262
  "ui.createKey": "创建 API Key",
295
263
  "ui.configureKey": "配置 API Key",
296
264
  "ui.viewKey": "查看 API Key",
@@ -301,13 +269,11 @@
301
269
  "ui.loadingKeys": "正在读取 Key",
302
270
  "ui.expiration": "到期时间(可选)",
303
271
  "ui.modelScopeOptional": "模型范围(可选)",
304
- "ui.reviewTitle": "确认路由访问变更",
305
272
  "ui.copied": "已复制到剪贴板。",
306
273
  "ui.keys": "API Key",
307
274
  "ui.settings": "访问控制设置",
308
275
  "ui.loadingRoutes": "正在读取路由",
309
276
  "ui.requiresKey": "需要 Key",
310
- "ui.keyName": "Key 名称",
311
277
  "ui.loadingPolicy": "正在读取策略",
312
278
  "ui.noRoutes": "尚无路由。",
313
279
  "ui.allowedRoutes": "可访问路由",
@@ -317,15 +283,11 @@
317
283
  "ui.public": "是否公开",
318
284
  "ui.basicInfo": "基本信息",
319
285
  "ui.allRoutes": "任意路由",
320
- "ui.modelScope": "模型范围",
321
- "ui.allModels": "全部模型",
322
286
  "ui.saving": "保存中…",
323
287
  "ui.copyAgain": "再次复制",
324
288
  "ui.copyPlaintext": "复制明文",
325
289
  "ui.confirmDelete": "确认删除",
326
290
  "ui.validity": "有效期",
327
- "ui.back": "上一步",
328
- "ui.next": "下一步",
329
291
  "ui.applied": "已应用",
330
292
  "ui.refresh": "刷新",
331
293
  "ui.name": "名称",
@@ -340,17 +302,13 @@
340
302
  "ui.cancel": "取消",
341
303
  "ui.save": "保存",
342
304
  "ui.apply": "应用",
343
- "ui.publicBlockedReason": "已关联 API Key:{keys}。请先解除该路由的授权或删除 Key,再设为公开。",
344
305
  "ui.keyNameExists": "API Key 名称已存在,请使用其他名称。",
345
- "ui.routeHasKeys": "此路由已关联 API Key,请先解除授权或删除 Key,再设为公开。",
346
306
  "ui.persistedPending": "配置已保存,但发布尚未完成。请刷新运行状态,确认生效后继续。",
347
307
  "ui.operationFailed": "操作失败",
348
308
  "ui.stageCreate": "创建 Key",
349
- "ui.stageSavePolicy": "保存 Key 允许路由与路由保护",
350
- "ui.createdProtected": "Key 已创建,允许路由已保存并发布。所选公开路由的保护已发布。",
309
+ "ui.stageSavePolicy": "保存 Key 权限",
351
310
  "ui.createdPublic": "Key 已创建,允许路由已保存并发布。公开路由保持公开。",
352
311
  "ui.createdIncomplete": "Key 已创建。{stage}未完成:{reason} 请保存明文,随后编辑此 Key 完成设置。",
353
- "ui.keySavedProtectionIncomplete": "Key 允许路由已保存;路由保护未完成:{reason}",
354
312
  "ui.clipboardUnavailable": "剪贴板不可用,请手动复制明文。",
355
313
  "ui.publicationPending": "已保存,发布尚未完成;请刷新运行状态。",
356
314
  "ui.publicationComplete": "已保存并发布。",