@jeffusion/bungee-core 5.9.0 → 5.11.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.
Files changed (53) hide show
  1. package/dist/main.js +31348 -17337
  2. package/dist/plugins/ai-transformer/index.js.map +2 -2
  3. package/dist/plugins/anthropic-request-sanitizer/index.js.map +2 -2
  4. package/dist/plugins/anthropic-tool-name-transformer/index.js.map +2 -2
  5. package/dist/plugins/chatgpt-oauth/control.js +513 -1
  6. package/dist/plugins/chatgpt-oauth/control.js.map +12 -4
  7. package/dist/plugins/chatgpt-oauth/index.js +511 -4
  8. package/dist/plugins/chatgpt-oauth/index.js.map +13 -5
  9. package/dist/plugins/chatgpt-oauth/manifest.json +19 -0
  10. package/dist/plugins/deepseek-reasoning-fix/index.js.map +2 -2
  11. package/dist/plugins/key-access/control.js.map +1 -1
  12. package/dist/plugins/key-access/index.js.map +3 -3
  13. package/dist/plugins/key-access/ingress.js.map +1 -1
  14. package/dist/plugins/key-rate-limit/index.js.map +2 -2
  15. package/dist/plugins/local-accounts/control.js +68 -18
  16. package/dist/plugins/local-accounts/control.js.map +4 -4
  17. package/dist/plugins/local-accounts/index.js.map +2 -2
  18. package/dist/plugins/local-accounts/manifest.json +48 -2
  19. package/dist/plugins/local-accounts/ui/Settings.svelte +73 -1
  20. package/dist/plugins/model-mapping/control.js +72 -17741
  21. package/dist/plugins/model-mapping/control.js.map +6 -58
  22. package/dist/plugins/model-mapping/index.js +32 -17433
  23. package/dist/plugins/model-mapping/index.js.map +6 -58
  24. package/dist/plugins/model-mapping/manifest.json +30 -14
  25. package/dist/plugins/models-dev/control.js +1042 -0
  26. package/dist/plugins/models-dev/control.js.map +16 -0
  27. package/dist/plugins/models-dev/index.js +15418 -0
  28. package/dist/plugins/models-dev/index.js.map +134 -0
  29. package/dist/plugins/models-dev/manifest.json +199 -0
  30. package/dist/plugins/models-dev/ui/ModelsDevSettings.svelte +120 -0
  31. package/dist/plugins/openai-messages-to-chat/index.js.map +2 -2
  32. package/dist/plugins/signature-repair/index.js.map +2 -2
  33. package/dist/plugins/token-budget/control.js +524 -9
  34. package/dist/plugins/token-budget/control.js.map +12 -4
  35. package/dist/plugins/token-budget/index.js +519 -12
  36. package/dist/plugins/token-budget/index.js.map +13 -6
  37. package/dist/plugins/token-budget/manifest.json +16 -2
  38. package/dist/plugins/token-metering/index.js +93 -2
  39. package/dist/plugins/token-metering/index.js.map +12 -6
  40. package/dist/plugins/token-stats/control.js +174 -312
  41. package/dist/plugins/token-stats/control.js.map +7 -7
  42. package/dist/plugins/token-stats/index.js +283 -486
  43. package/dist/plugins/token-stats/index.js.map +16 -12
  44. package/dist/plugins/token-stats/manifest.json +99 -12
  45. package/dist/plugins/token-stats/ui/ClientModelPicker.svelte +76 -0
  46. package/dist/plugins/token-stats/ui/PriceModelPicker.svelte +87 -0
  47. package/dist/plugins/token-stats/ui/TokenStatsSettings.svelte +136 -80
  48. package/dist/plugins/token-stats/ui/client-model-search.test.ts +58 -0
  49. package/dist/plugins/token-stats/ui/client-model-search.ts +16 -0
  50. package/dist/plugins/token-stats/ui/model-search.ts +45 -0
  51. package/dist/plugins/token-stats/ui/pricing-catalog.test.ts +114 -0
  52. package/dist/plugins/token-stats/ui/pricing-catalog.ts +17 -0
  53. package/package.json +2 -2
@@ -47,6 +47,16 @@
47
47
  "execution": "control",
48
48
  "capability": "self.password"
49
49
  },
50
+ {
51
+ "path": "/session-policy",
52
+ "methods": [
53
+ "GET",
54
+ "PUT"
55
+ ],
56
+ "handler": "sessionPolicy",
57
+ "execution": "control",
58
+ "capability": "config.write"
59
+ },
50
60
  {
51
61
  "path": "/logout",
52
62
  "methods": [
@@ -120,7 +130,25 @@
120
130
  "settings.confirmation": "确认新密码",
121
131
  "settings.updating": "正在更新…",
122
132
  "settings.submit": "修改并重新登录",
123
- "settings.logout": "退出当前会话"
133
+ "settings.logout": "退出当前会话",
134
+ "session.title": "登录会话",
135
+ "session.tag": "有效期",
136
+ "session.description": "保存后仅影响新登录的会话,已有会话保留登录时的设置。时长设为 0 可关闭对应超时。",
137
+ "session.idle": "空闲超时",
138
+ "session.absolute": "登录最长有效期",
139
+ "session.minutes": "分钟",
140
+ "session.hours": "小时",
141
+ "session.days": "天",
142
+ "session.unit": "时长单位",
143
+ "session.browserNote": "关闭超时不影响退出登录、改密和身份恢复时撤销会话。浏览器仍可能清理 Cookie;正常核验会话时会续期 Cookie,但不会延长登录最长有效期。",
144
+ "session.loading": "正在读取会话设置",
145
+ "session.save": "保存会话设置",
146
+ "session.saving": "正在保存…",
147
+ "session.saved": "已保存,下次登录生效。",
148
+ "session.invalid": "请输入非负整数时长;0 表示关闭超时。",
149
+ "session.conflict": "设置已被其他页面修改。请重新读取后再保存。",
150
+ "session.failed": "会话设置读取或保存失败。",
151
+ "session.reload": "重新读取"
124
152
  },
125
153
  "en": {
126
154
  "metadata.name": "Management authentication",
@@ -165,7 +193,25 @@
165
193
  "settings.confirmation": "Confirm new password",
166
194
  "settings.updating": "Updating…",
167
195
  "settings.submit": "Change and sign in again",
168
- "settings.logout": "Sign out of current session"
196
+ "settings.logout": "Sign out of current session",
197
+ "session.title": "Sign-in sessions",
198
+ "session.tag": "LIFETIME",
199
+ "session.description": "Saved settings apply only to new sign-ins. Existing sessions retain their original settings. Set a duration to 0 to disable that timeout.",
200
+ "session.idle": "Idle timeout",
201
+ "session.absolute": "Maximum sign-in lifetime",
202
+ "session.minutes": "Minutes",
203
+ "session.hours": "Hours",
204
+ "session.days": "Days",
205
+ "session.unit": "Duration unit",
206
+ "session.browserNote": "Disabling timeouts does not prevent sign-out, password changes or identity recovery from revoking sessions. Browsers may still clear cookies. Session verification renews the cookie without extending the maximum sign-in lifetime.",
207
+ "session.loading": "Loading session settings",
208
+ "session.save": "Save session settings",
209
+ "session.saving": "Saving…",
210
+ "session.saved": "Saved. Applies to your next sign-in.",
211
+ "session.invalid": "Enter a non-negative whole-number duration. 0 disables the timeout.",
212
+ "session.conflict": "Another page changed these settings. Reload before saving.",
213
+ "session.failed": "Could not read or save session settings.",
214
+ "session.reload": "Reload settings"
169
215
  }
170
216
  }
171
217
  }
@@ -4,15 +4,62 @@
4
4
  import { onMount } from 'svelte';
5
5
  import { requestPluginControl } from '$api/client';
6
6
  import { logout } from '$stores/auth';
7
- import { PanelCard, LoadingIndicator } from '$components/industrial';
7
+ import { PanelCard, LoadingIndicator, BSelect } from '$components/industrial';
8
+ import { Input } from '$components/ui/input';
9
+ import { Button } from '$components/ui/button';
8
10
  import { accountError } from '$components/domain/plugin/activation-state';
9
11
  const t = (key: string) => $isLoading ? '' : getPluginText(key, 'local-accounts', (id, options) => $_(id, options));
10
12
  const core = (key: string, values: Record<string, string> = {}) => $isLoading ? '' : $_(key, { values });
11
13
  let { pluginName = 'local-accounts' }: { pluginName?: string; apiBase?: string } = $props();
12
14
  let administrator = $state<{id:string;username:string;requiresPasswordChange?:boolean} | null>(null);
13
15
  let currentPassword = $state(''), password = $state(''), confirmation = $state(''), error = $state(''), detail = $state(''), busy = $state(false), loading = $state(true);
16
+ type SessionPolicy = {idleTimeoutMinutes: number; absoluteTimeoutMinutes: number};
17
+ type PolicyResult = {version: number; policy: SessionPolicy};
18
+ const factors: Record<string, number> = {minutes: 1, hours: 60, days: 1440};
19
+ const maxMinutes = Math.floor(Number.MAX_SAFE_INTEGER / 60_000);
20
+ let durations = $state<{key: keyof SessionPolicy; label: string; value: number | undefined; unit: string}[]>([
21
+ {key: 'idleTimeoutMinutes', label: 'session.idle', value: 30, unit: 'minutes'},
22
+ {key: 'absoluteTimeoutMinutes', label: 'session.absolute', value: 8, unit: 'hours'},
23
+ ]);
24
+ let policyVersion = $state<number | null>(null), savedPolicy = $state<SessionPolicy | null>(null);
25
+ let policyLoading = $state(true), policyBusy = $state(false), policyError = $state(''), policyDetail = $state(''), policySaved = $state(false);
26
+ let units = $derived(['minutes', 'hours', 'days'].map(unit => ({value: unit, label: t('session.' + unit)})));
27
+ let draftPolicy = $derived(Object.fromEntries(durations.map(field => [field.key, field.value === undefined ? NaN : field.value * factors[field.unit]!])) as SessionPolicy);
28
+ let validPolicy = $derived(durations.every(field => Number.isSafeInteger(field.value) && field.value! >= 0)
29
+ && Object.values(draftPolicy).every(value => Number.isSafeInteger(value) && value >= 0 && value <= maxMinutes));
30
+ let policyDirty = $derived(savedPolicy !== null && (draftPolicy.idleTimeoutMinutes !== savedPolicy.idleTimeoutMinutes || draftPolicy.absoluteTimeoutMinutes !== savedPolicy.absoluteTimeoutMinutes));
31
+ function applyPolicy(result: PolicyResult) {
32
+ policyVersion = result.version; savedPolicy = result.policy;
33
+ for (const field of durations) {
34
+ const minutes = result.policy[field.key];
35
+ field.unit = minutes > 0 && minutes % 1440 === 0 ? 'days' : minutes > 0 && minutes % 60 === 0 ? 'hours' : 'minutes';
36
+ field.value = minutes / factors[field.unit]!;
37
+ }
38
+ }
39
+ function policyFailure(e: unknown) {
40
+ const result = accountError(e); policyDetail = result.detail;
41
+ policyError = result.detail === 'version_conflict' ? 'session.conflict' : result.detail === 'invalid_session_policy' ? 'session.invalid' : 'session.failed';
42
+ }
43
+ async function loadPolicy() {
44
+ if (policyBusy) return;
45
+ policyLoading = true; policyError = ''; policyDetail = ''; policySaved = false;
46
+ try { applyPolicy(await requestPluginControl<PolicyResult>(pluginName, '/session-policy', 'GET')); }
47
+ catch (e) { policyFailure(e); }
48
+ finally { policyLoading = false; }
49
+ }
50
+ async function savePolicy(event: SubmitEvent) {
51
+ event.preventDefault(); if (policyBusy || policyLoading || policyVersion === null) return;
52
+ if (!validPolicy) { policyError = 'session.invalid'; return; }
53
+ policyBusy = true; policyError = ''; policyDetail = ''; policySaved = false;
54
+ try {
55
+ applyPolicy(await requestPluginControl<PolicyResult>(pluginName, '/session-policy', 'PUT', {version: policyVersion, policy: draftPolicy}));
56
+ policySaved = true;
57
+ } catch (e) { policyFailure(e); }
58
+ finally { policyBusy = false; }
59
+ }
14
60
  function failure(e: unknown) { const result = accountError(e); error = result.key; detail = result.detail; }
15
61
  onMount(() => { void requestPluginControl<{administrator: typeof administrator}>(pluginName, '/self', 'GET').then(result => { administrator = result.administrator; }).catch(failure).finally(() => { loading = false; }); });
62
+ onMount(() => { void loadPolicy(); });
16
63
  async function change(event: SubmitEvent) {
17
64
  event.preventDefault(); if (busy) return;
18
65
  if (password !== confirmation) { failure(new Error('password_confirmation')); return; }
@@ -32,6 +79,31 @@
32
79
  <PanelCard title={t('settings.accountTitle')} tag={t('settings.accountTag')}>
33
80
  {#if loading}<LoadingIndicator label={t('settings.loading')} height="sm" />{:else if administrator}<p class="text-sm text-zinc-200">{administrator.username}</p><p class="text-sm text-zinc-400 mt-2">{t('settings.accountDescription')}</p>{/if}
34
81
  </PanelCard>
82
+ <PanelCard title={t('session.title')} tag={t('session.tag')}>
83
+ <p class="text-sm text-zinc-400 mb-4">{t('session.description')}</p>
84
+ {#if policyLoading}<LoadingIndicator label={t('session.loading')} height="sm" />{:else}
85
+ <form class="space-y-3" onsubmit={savePolicy} aria-busy={policyBusy} data-testid="session-policy-form">
86
+ <div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
87
+ {#each durations as field (field.key)}
88
+ <div class="grid gap-1 min-w-0">
89
+ <label class="nx-field-label" for={'session-' + field.key}>{t(field.label)}</label>
90
+ <div class="flex items-center gap-2">
91
+ <Input id={'session-' + field.key} type="number" min={0} max={Math.floor(maxMinutes / factors[field.unit]!)} step={1} required bind:value={field.value} disabled={policyBusy || policyVersion === null} class="min-w-0 flex-1" />
92
+ <BSelect bind:value={field.unit} options={units} ariaLabel={t(field.label) + ' · ' + t('session.unit')} width="6rem" disabled={policyBusy || policyVersion === null} />
93
+ </div>
94
+ </div>
95
+ {/each}
96
+ </div>
97
+ <p class="text-sm text-zinc-400">{t('session.browserNote')}</p>
98
+ <div class="flex flex-wrap gap-2">
99
+ <Button type="submit" disabled={policyBusy || policyVersion === null || !policyDirty || !validPolicy}>{policyBusy ? t('session.saving') : t('session.save')}</Button>
100
+ {#if policyError}<Button type="button" variant="ghost" onclick={loadPolicy} disabled={policyBusy}>{t('session.reload')}</Button>{/if}
101
+ </div>
102
+ {#if policySaved && !policyDirty}<p role="status" class="text-sm text-emerald-400">{t('session.saved')}</p>{/if}
103
+ </form>
104
+ {/if}
105
+ {#if policyError}<p role="alert" class="text-sm text-red-400 mt-3">{t(policyError)}</p><details class="text-sm text-zinc-400"><summary>{core('pluginActivation.technicalDetails')}</summary><pre class="whitespace-pre-wrap break-all">{policyDetail}</pre></details>{/if}
106
+ </PanelCard>
35
107
  <PanelCard title={t('settings.passwordTitle')} tag={t('settings.passwordTag')}>
36
108
  <p class="text-sm text-zinc-400 mb-4">{t('settings.passwordDescription')}</p>
37
109
  <form class="space-y-3" onsubmit={change} aria-busy={busy}>