@dungsil/dsh-plugin-cli-proxy-api 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/client.js ADDED
@@ -0,0 +1,978 @@
1
+ window.__ModuleLoader__.load({ id: "@dungsil/dsh-plugin-cli-proxy-api", factory(require) { const module = { exports: {} }; const exports = module.exports;
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ let react = require("react");
4
+ let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
5
+ let react_jsx_runtime = require("react/jsx-runtime");
6
+ // ../../tools/ui/src/client.ts
7
+ function useLocaleSnapshot(locale) {
8
+ const subscribe = (0, react.useCallback)((listener) => locale.subscribe(listener), [locale]);
9
+ const getSnapshot = (0, react.useCallback)(() => locale.getSnapshot(), [locale]);
10
+ return (0, react.useSyncExternalStore)(subscribe, getSnapshot);
11
+ }
12
+ /** Each effect owns its sheet, even when another plugin installs the same defaults. */
13
+ function installStyles(css) {
14
+ const style = document.createElement("style");
15
+ style.textContent = css;
16
+ document.head.append(style);
17
+ return () => style.remove();
18
+ }
19
+ // ../../tools/ui/src/settings.tsx
20
+ const settingsStyles = `
21
+ .dsh-plugin-settings-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 16px 0; border-bottom: .5px solid var(--dsw-alias-border-l2); }
22
+ .dsh-plugin-settings-text { flex: 1; min-width: 0; }
23
+ .dsh-plugin-settings-picker { min-width: 0; }
24
+ .dsh-plugin-settings-selector { box-sizing: border-box; display: inline-flex; align-items: center; gap: 12px; max-width: 100%; height: 36px; padding: 0 14px; border: none; border-radius: var(--dsw-radius-md); background: var(--dsw-alias-bg-module-platform); color: inherit; font: inherit; cursor: pointer; }
25
+ .dsh-plugin-settings-value { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
26
+ .dsh-plugin-settings-selector svg { flex: none; }
27
+ .dsh-plugin-settings-selector:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover); }
28
+ .dsh-plugin-settings-selector:disabled { opacity: .5; cursor: default; }
29
+ .dsh-plugin-settings-selector:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary-new-colorprimary-new-color); outline-offset: 2px; }
30
+ `;
31
+ function SettingsRow({ label, children }) {
32
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
33
+ className: "dsh-plugin-settings-row",
34
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
35
+ className: "dsh-plugin-settings-text",
36
+ children: label
37
+ }), children]
38
+ });
39
+ }
40
+ // src/protocol.ts
41
+ const channel = "/api";
42
+ const endpointPrefix = "dungsil-cli-proxy-api/";
43
+ // src/copy.ts
44
+ const copy = {
45
+ en: {
46
+ quota: "Quota",
47
+ quotaView: "Quota view",
48
+ quotaCards: "Cards",
49
+ quotaTable: "Table",
50
+ quotaAccount: "Account",
51
+ quotaProvider: "Provider",
52
+ quotaUsage: "Used · remaining",
53
+ quotaStatus: "Observation",
54
+ quotaActions: "Lookup",
55
+ quotaAvailable: "Below 80%",
56
+ quotaWarning: "Near limit",
57
+ quotaExhausted: "Exhausted",
58
+ quotaUnsupported: "Unsupported",
59
+ quotaUnqueried: "Not queried",
60
+ quotaUnknown: "Unknown",
61
+ quotaError: "Lookup failed",
62
+ quotaReset: "Clear routing cooldown",
63
+ quotaLoadTitle: "Load accounts to get started",
64
+ quotaLoadHint: "Load the account list, then query quota per account. Switching views keeps your results.",
65
+ quotaHint: "Load accounts, then refresh quota individually. Cooldown reset only clears proxy routing blocks; it does not refill quota. No paid inference or reset credits are used.",
66
+ refreshQuotaAccounts: "Refresh accounts",
67
+ searchAccounts: "Search name / provider",
68
+ refreshQuota: "Refresh quota",
69
+ unloadedQuota: "Quota is loaded only when you refresh.",
70
+ unsupportedQuota: "Quota lookup is not supported for this account.",
71
+ unknownQuota: "No quota observation available",
72
+ quotaFailed: "Quota lookup failed. Refresh accounts before trying again if credentials changed.",
73
+ used: "Used",
74
+ remaining: "Remaining",
75
+ resetsAt: "Resets at",
76
+ observedAt: "Checked at",
77
+ plan: "Plan",
78
+ fiveHour: "5 hours",
79
+ sevenDay: "7 days",
80
+ oauthApps: "OAuth apps",
81
+ primaryWindow: "Primary limit",
82
+ secondaryWindow: "Secondary limit",
83
+ codeReview: "Code review",
84
+ daily: "Daily",
85
+ monthly: "Monthly",
86
+ credits: "Credits",
87
+ hours: "h",
88
+ previous: "Previous",
89
+ next: "Next",
90
+ connection: "Connection",
91
+ baseURL: "Base URL",
92
+ apiKey: "API key",
93
+ managementKey: "Management key",
94
+ saved: "Saved · leave unchanged",
95
+ notSaved: "Not saved",
96
+ remove: "Remove saved key",
97
+ removed: "Will be removed on save",
98
+ serverHint: "Requires CLIProxyAPI v8. Use the server root without /v1. Remote servers require HTTPS.",
99
+ keyHint: "Blank fields keep saved keys. When the URL changes, only newly entered keys are used.",
100
+ save: "Save connection",
101
+ refresh: "Refresh",
102
+ busy: "Working…",
103
+ connectionLocked: "Connection could not be confirmed. Refresh the connection to enable saving.",
104
+ error: "The request could not be confirmed. It will not be retried automatically. Refresh to check before trying again.",
105
+ success: "Request completed. Refresh the relevant list to confirm the latest state.",
106
+ savedConnection: "Connection saved and checked.",
107
+ managementHint: "Save a management key to enable management controls.",
108
+ draftHint: "Save the connection before using these controls.",
109
+ connectionSettingsHint: "Configure the server URL and keys in Settings → CLIProxyAPI. Connection information updates automatically.",
110
+ emptyAccounts: "No accounts available.",
111
+ disabled: "Disabled"
112
+ },
113
+ ko: {
114
+ quota: "할당량",
115
+ quotaView: "할당량 보기",
116
+ quotaCards: "카드",
117
+ quotaTable: "테이블",
118
+ quotaAccount: "계정",
119
+ quotaProvider: "공급자",
120
+ quotaUsage: "사용률 · 잔여량",
121
+ quotaStatus: "조회 상태",
122
+ quotaActions: "조회",
123
+ quotaAvailable: "80% 미만",
124
+ quotaWarning: "주의",
125
+ quotaExhausted: "소진",
126
+ quotaUnsupported: "미지원",
127
+ quotaUnqueried: "미조회",
128
+ quotaUnknown: "미확인",
129
+ quotaError: "조회 실패",
130
+ quotaReset: "라우팅 대기 해제",
131
+ quotaLoadTitle: "계정을 불러와 시작하세요",
132
+ quotaLoadHint: "계정 목록을 불러온 뒤 할당량을 개별 조회합니다. 보기를 전환해도 조회 결과는 유지합니다.",
133
+ quotaHint: "계정을 불러온 뒤 할당량을 개별 새로고침하세요. 대기 시간 초기화는 프록시의 라우팅 차단만 해제하며 할당량을 충전하지 않습니다. 유료 추론이나 초기화 크레딧을 사용하지 않습니다.",
134
+ refreshQuotaAccounts: "계정 새로고침",
135
+ searchAccounts: "이름 / 공급자 검색",
136
+ refreshQuota: "할당량 새로고침",
137
+ unloadedQuota: "새로고침을 눌렀을 때만 할당량을 조회합니다.",
138
+ unsupportedQuota: "이 계정은 할당량 조회를 지원하지 않습니다.",
139
+ unknownQuota: "확인된 할당량이 없습니다",
140
+ quotaFailed: "할당량을 조회하지 못했습니다. 인증 정보가 바뀌었다면 계정을 새로고침한 뒤 다시 시도하세요.",
141
+ used: "사용률",
142
+ remaining: "잔여량",
143
+ resetsAt: "초기화 시각",
144
+ observedAt: "확인 시각",
145
+ plan: "요금제",
146
+ fiveHour: "5시간",
147
+ sevenDay: "7일",
148
+ oauthApps: "OAuth 앱",
149
+ primaryWindow: "주요 한도",
150
+ secondaryWindow: "보조 한도",
151
+ codeReview: "코드 검토",
152
+ daily: "일간",
153
+ monthly: "월간",
154
+ credits: "크레딧",
155
+ hours: "시간",
156
+ previous: "이전",
157
+ next: "다음",
158
+ connection: "연결 정보",
159
+ baseURL: "기본 URL",
160
+ apiKey: "API 키",
161
+ managementKey: "관리 키",
162
+ saved: "저장됨 · 변경하지 않음",
163
+ notSaved: "저장되지 않음",
164
+ remove: "저장된 키 삭제",
165
+ removed: "저장 시 삭제됩니다",
166
+ serverHint: "CLIProxyAPI v8이 필요합니다. /v1을 제외한 서버 주소를 입력하세요. 원격 서버는 HTTPS가 필요합니다.",
167
+ keyHint: "빈 입력란은 기존 키를 유지합니다. URL을 바꾸면 새로 입력한 키만 사용합니다.",
168
+ save: "연결 저장",
169
+ refresh: "새로고침",
170
+ busy: "처리 중…",
171
+ connectionLocked: "연결 정보를 확인하지 못했습니다. 연결을 새로고침하면 저장할 수 있습니다.",
172
+ error: "요청 결과를 확인하지 못했습니다. 자동으로 재시도하지 않습니다. 다시 시도하기 전에 새로고침하여 확인하세요.",
173
+ success: "요청을 완료했습니다. 해당 목록을 새로고침하여 최신 상태를 확인하세요.",
174
+ savedConnection: "연결을 저장하고 확인했습니다.",
175
+ managementHint: "관리 기능을 사용하려면 관리 키를 저장하세요.",
176
+ draftHint: "이 기능을 사용하기 전에 연결을 저장하세요.",
177
+ connectionSettingsHint: "설정 → CLIProxyAPI에서 서버 주소와 키를 설정하세요. 연결 정보는 자동으로 갱신됩니다.",
178
+ emptyAccounts: "사용 가능한 계정이 없습니다.",
179
+ disabled: "비활성"
180
+ },
181
+ zh: {
182
+ quota: "配额",
183
+ quotaView: "配额视图",
184
+ quotaCards: "卡片",
185
+ quotaTable: "表格",
186
+ quotaAccount: "账户",
187
+ quotaProvider: "提供商",
188
+ quotaUsage: "已用 · 剩余",
189
+ quotaStatus: "查询状态",
190
+ quotaActions: "查询",
191
+ quotaAvailable: "低于 80%",
192
+ quotaWarning: "接近上限",
193
+ quotaExhausted: "已耗尽",
194
+ quotaUnsupported: "不支持",
195
+ quotaUnqueried: "未查询",
196
+ quotaUnknown: "未知",
197
+ quotaError: "查询失败",
198
+ quotaReset: "解除路由冷却",
199
+ quotaLoadTitle: "加载账户以开始",
200
+ quotaLoadHint: "加载账户列表后逐个查询配额。切换视图会保留查询结果。",
201
+ quotaHint: "加载账户后逐个刷新配额。重置冷却时间仅解除代理路由限制,不会补充配额。不会使用付费推理或重置额度。",
202
+ refreshQuotaAccounts: "刷新账户",
203
+ searchAccounts: "搜索名称 / 提供商",
204
+ refreshQuota: "刷新配额",
205
+ unloadedQuota: "仅在点击刷新时查询配额。",
206
+ unsupportedQuota: "此账户不支持配额查询。",
207
+ unknownQuota: "暂无配额观测数据",
208
+ quotaFailed: "配额查询失败。如果凭据已更改,请刷新账户后重试。",
209
+ used: "已使用",
210
+ remaining: "剩余",
211
+ resetsAt: "重置时间",
212
+ observedAt: "查询时间",
213
+ plan: "套餐",
214
+ fiveHour: "5 小时",
215
+ sevenDay: "7 天",
216
+ oauthApps: "OAuth 应用",
217
+ primaryWindow: "主要限额",
218
+ secondaryWindow: "次要限额",
219
+ codeReview: "代码审查",
220
+ daily: "每日",
221
+ monthly: "每月",
222
+ credits: "额度",
223
+ hours: "小时",
224
+ previous: "上一页",
225
+ next: "下一页",
226
+ connection: "连接信息",
227
+ baseURL: "基础 URL",
228
+ apiKey: "API 密钥",
229
+ managementKey: "管理密钥",
230
+ saved: "已保存 · 保持不变",
231
+ notSaved: "未保存",
232
+ remove: "删除已保存的密钥",
233
+ removed: "保存时将删除",
234
+ serverHint: "需要 CLIProxyAPI v8。请输入不含 /v1 的服务器根地址。远程服务器必须使用 HTTPS。",
235
+ keyHint: "空输入框保留已保存的密钥。更改 URL 后仅使用新输入的密钥。",
236
+ save: "保存连接",
237
+ refresh: "刷新",
238
+ busy: "处理中…",
239
+ connectionLocked: "无法确认连接信息。请刷新连接以启用保存。",
240
+ error: "无法确认请求结果。不会自动重试。请先刷新确认状态,再尝试操作。",
241
+ success: "请求已完成。请刷新相应列表以确认最新状态。",
242
+ savedConnection: "连接已保存并确认。",
243
+ managementHint: "请保存管理密钥以启用管理功能。",
244
+ draftHint: "使用这些功能前请保存连接。",
245
+ connectionSettingsHint: "请在设置 → CLIProxyAPI 中配置服务器地址和密钥。连接信息会自动更新。",
246
+ emptyAccounts: "没有可用账户。",
247
+ disabled: "已禁用"
248
+ }
249
+ };
250
+ const localizedCopy = (language) => copy[language.startsWith("ko") ? "ko" : language.startsWith("zh") ? "zh" : "en"];
251
+ // src/quota-panel.tsx
252
+ const quotaStyles = `
253
+ .dsh-cli-quota { margin-top: 20px; }
254
+ .dsh-cli-quota .quota-toolbar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 20px; }
255
+ .dsh-cli-quota .quota-search { box-sizing: border-box; flex: 1; min-width: 160px; }
256
+ .dsh-cli-quota .quota-toolbar-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
257
+ .dsh-cli-quota .quota-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
258
+ .dsh-cli-quota .quota-card { display: flex; flex-direction: column; min-width: 0; padding: 16px; border: .5px solid var(--dsw-alias-border-l2); border-radius: var(--dsw-radius-md); }
259
+ .dsh-cli-quota .quota-card-header { display: flex; align-items: flex-start; gap: 12px; margin-bottom: 20px; }
260
+ .dsh-cli-quota .quota-identity { flex: 1; min-width: 0; overflow-wrap: anywhere; }
261
+ .dsh-cli-quota .quota-identity strong { display: block; font-weight: 500; }
262
+ .dsh-cli-quota .quota-name { color: var(--dsw-alias-label-secondary); font-size: 12px; line-height: 20px; overflow-wrap: anywhere; }
263
+ .dsh-cli-quota .quota-badge { flex-shrink: 0; }
264
+ .dsh-cli-quota .quota-window { padding: 12px 0; border-top: 1px solid var(--dsw-alias-border-l2); }
265
+ .dsh-cli-quota .quota-heading { gap: 4px 12px; font-size: 12px; }
266
+ .dsh-cli-quota .quota-heading strong { font-weight: 500; }
267
+ .dsh-cli-quota progress { height: 6px; margin: 10px 0; }
268
+ .dsh-cli-quota [data-tone=warning] progress::-webkit-progress-value { background: var(--dsw-alias-state-warn-primary); }
269
+ .dsh-cli-quota [data-tone=danger] progress::-webkit-progress-value { background: var(--dsw-alias-state-error-primary); }
270
+ .dsh-cli-quota [data-tone=warning] progress::-moz-progress-bar { background: var(--dsw-alias-state-warn-primary); }
271
+ .dsh-cli-quota [data-tone=danger] progress::-moz-progress-bar { background: var(--dsw-alias-state-error-primary); }
272
+ .dsh-cli-quota .quota-card-footer { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 8px; margin-top: auto; padding-top: 16px; }
273
+ .dsh-cli-quota .quota-table-scroll { overflow-x: auto; border: .5px solid var(--dsw-alias-border-l2); border-radius: var(--dsw-radius-md); }
274
+ .dsh-cli-quota .quota-table-scroll button:not(.quota-expand) { white-space: nowrap; }
275
+ .dsh-cli-quota table { width: 100%; min-width: 760px; border-collapse: collapse; text-align: start; font-size: 12px; }
276
+ .dsh-cli-quota th { background: var(--dsw-alias-bg-module-platform); color: var(--dsw-alias-label-secondary); font-weight: 500; white-space: nowrap; }
277
+ .dsh-cli-quota th, .dsh-cli-quota td { padding: 14px 12px; border-bottom: 1px solid var(--dsw-alias-border-l2); vertical-align: middle; }
278
+ .dsh-cli-quota tbody:last-child tr:last-child td { border-bottom: 0; }
279
+ .dsh-cli-quota .quota-table-account { max-width: 190px; }
280
+ .dsh-cli-quota .quota-expand { max-width: 100%; height: auto; min-height: 28px; justify-content: flex-start; text-align: start; overflow-wrap: anywhere; }
281
+ .dsh-cli-quota .quota-expand svg { flex: none; transform: rotate(-90deg); }
282
+ .dsh-cli-quota .quota-expand[aria-expanded=true] svg { transform: none; }
283
+ .dsh-cli-quota .quota-summary { min-width: 135px; }
284
+ .dsh-cli-quota .quota-summary progress { width: 100%; margin: 6px 0; }
285
+ .dsh-cli-quota .quota-table-detail { padding: 4px 12px 12px; }
286
+ .dsh-cli-quota .quota-detail-header { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 12px; align-items: center; margin-bottom: 12px; }
287
+ .dsh-cli-quota .quota-detail-windows { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 24px; }
288
+ .dsh-cli-quota .quota-empty { padding: 32px 16px; text-align: center; color: var(--dsw-alias-label-secondary); }
289
+ .dsh-cli-quota .quota-pagination { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 16px; }
290
+ .dsh-cli-quota .quota-pagination > div { display: flex; gap: 8px; }
291
+ .dsh-cli-quota .quota-note { margin-top: 20px; color: var(--dsw-alias-label-secondary); font-size: 12px; line-height: 20px; }
292
+ @container (max-width: 680px) {
293
+ .dsh-cli-quota .quota-grid { grid-template-columns: minmax(0, 1fr); }
294
+ .dsh-cli-quota .quota-search { width: 100%; flex-basis: 100%; }
295
+ .dsh-cli-quota .quota-toolbar-actions { width: 100%; justify-content: space-between; }
296
+ .dsh-cli-quota .quota-card { padding: 16px; }
297
+ }
298
+ `;
299
+ function QuotaPanel({ accounts, quotas, language, text, busy, canLoad, managementDisabled, onLoad, onFetch, onReset }) {
300
+ const id = (0, react.useId)();
301
+ const [layout, setLayout] = (0, react.useState)("cards");
302
+ const [search, setSearch] = (0, react.useState)("");
303
+ const [page, setPage] = (0, react.useState)(0);
304
+ const [expanded, setExpanded] = (0, react.useState)(/* @__PURE__ */ new Set());
305
+ const filtered = accounts?.filter((account) => `${account.name} ${account.provider}`.toLowerCase().includes(search.trim().toLowerCase())) ?? [];
306
+ const pages = Math.max(1, Math.ceil(filtered.length / 20));
307
+ const currentPage = Math.min(page, pages - 1);
308
+ const labels = {
309
+ five_hour: text.fiveHour,
310
+ seven_day: text.sevenDay,
311
+ seven_day_oauth_apps: text.oauthApps,
312
+ seven_day_opus: "Opus",
313
+ seven_day_sonnet: "Sonnet",
314
+ seven_day_cowork: "Cowork",
315
+ primary_window: text.primaryWindow,
316
+ secondary_window: text.secondaryWindow,
317
+ code_review_primary_window: text.codeReview,
318
+ code_review_secondary_window: text.codeReview,
319
+ daily: text.daily,
320
+ weekly: text.sevenDay,
321
+ monthly: text.monthly,
322
+ credits: text.credits
323
+ };
324
+ const windowLabel = (window) => `${labels[window.id] ?? window.id}${window.periodSeconds === void 0 || [
325
+ "five_hour",
326
+ "seven_day",
327
+ "daily",
328
+ "weekly",
329
+ "monthly"
330
+ ].includes(window.id) ? "" : ` · ${window.periodSeconds / 3600}${text.hours}`}`;
331
+ const percent = (used) => `${Number(used.toFixed(1))}%`;
332
+ const date = (value) => new Date(value).toLocaleString(language);
333
+ const tone = (used) => used === void 0 ? "neutral" : used >= 100 ? "danger" : used >= 80 ? "warning" : "neutral";
334
+ const renderAccount = (account, index) => {
335
+ const key = JSON.stringify([account.name, account.authIndex]);
336
+ const result = quotas[key];
337
+ const observed = result?.windows.flatMap((window) => window.usedPercent === void 0 ? [] : [window.usedPercent]) ?? [];
338
+ const highest = observed.length ? Math.max(...observed) : void 0;
339
+ const status = account.disabled ? text.disabled : !account.supported ? text.quotaUnsupported : result === null ? text.quotaError : !result ? text.quotaUnqueried : highest === void 0 ? text.quotaUnknown : highest >= 100 ? text.quotaExhausted : highest >= 80 ? text.quotaWarning : text.quotaAvailable;
340
+ const statusTone = result === null ? "danger" : account.disabled || !account.supported ? "neutral" : tone(highest);
341
+ const badge = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, {
342
+ className: "quota-badge",
343
+ tone: statusTone,
344
+ children: status
345
+ });
346
+ const refresh = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
347
+ variant: "outline",
348
+ size: layout === "table" ? "sm" : "md",
349
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconRefreshOutlineRegular, {
350
+ size: 16,
351
+ "aria-hidden": "true"
352
+ }),
353
+ "aria-label": `${text.refreshQuota} · ${account.name}`,
354
+ disabled: managementDisabled || !account.supported || !account.authIndex,
355
+ onClick: () => onFetch(account),
356
+ children: text.refreshQuota
357
+ });
358
+ const reset = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
359
+ variant: "ghost",
360
+ size: layout === "table" ? "sm" : "md",
361
+ "aria-label": `${text.quotaReset} · ${account.name}`,
362
+ disabled: managementDisabled || !account.editable || !account.authIndex,
363
+ onClick: () => onReset(account),
364
+ children: text.quotaReset
365
+ });
366
+ const details = !account.supported || account.disabled ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
367
+ className: "hint",
368
+ children: account.disabled ? text.disabled : text.unsupportedQuota
369
+ }) : result === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
370
+ role: "alert",
371
+ children: text.quotaFailed
372
+ }) : !result ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
373
+ className: "hint",
374
+ children: text.unloadedQuota
375
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
376
+ result.plan && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
377
+ className: "hint",
378
+ children: [
379
+ text.plan,
380
+ ": ",
381
+ result.plan
382
+ ]
383
+ }),
384
+ result.windows.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
385
+ className: "hint",
386
+ children: text.unknownQuota
387
+ }),
388
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
389
+ className: layout === "table" ? "quota-detail-windows" : void 0,
390
+ children: result.windows.map((window, windowIndex) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
391
+ className: "quota-window",
392
+ "data-tone": tone(window.usedPercent),
393
+ children: [
394
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
395
+ className: "quota-heading",
396
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: windowLabel(window) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: window.usedPercent === void 0 ? text.unknownQuota : `${text.used}: ${percent(window.usedPercent)} · ${text.remaining}: ${percent(100 - window.usedPercent)}` })]
397
+ }),
398
+ window.usedPercent !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("progress", {
399
+ "aria-label": `${account.name} · ${windowLabel(window)}`,
400
+ max: 100,
401
+ value: window.usedPercent
402
+ }),
403
+ window.resetAt && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
404
+ className: "hint",
405
+ children: [
406
+ text.resetsAt,
407
+ ": ",
408
+ date(window.resetAt)
409
+ ]
410
+ })
411
+ ]
412
+ }, `${window.id}-${windowIndex}`))
413
+ }),
414
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
415
+ className: "hint",
416
+ children: [
417
+ text.observedAt,
418
+ ": ",
419
+ date(result.observedAt)
420
+ ]
421
+ })
422
+ ] });
423
+ if (layout === "cards") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("article", {
424
+ className: "quota-card",
425
+ "aria-label": `${account.provider} · ${account.name}`,
426
+ children: [
427
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
428
+ className: "quota-card-header",
429
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
430
+ className: "quota-identity",
431
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: account.provider }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
432
+ className: "quota-name",
433
+ children: account.name
434
+ })]
435
+ }), badge]
436
+ }),
437
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: details }),
438
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("footer", {
439
+ className: "quota-card-footer",
440
+ children: [refresh, reset]
441
+ })
442
+ ]
443
+ }, key);
444
+ const first = result?.windows[0];
445
+ const detailId = `${id}-account-${index}`;
446
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tbody", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [
447
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
448
+ className: "quota-table-account",
449
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Button, {
450
+ variant: "ghost",
451
+ size: "sm",
452
+ className: "quota-expand",
453
+ "aria-expanded": expanded.has(key),
454
+ "aria-controls": detailId,
455
+ onClick: () => setExpanded((current) => {
456
+ const next = new Set(current);
457
+ if (next.has(key)) next.delete(key);
458
+ else next.add(key);
459
+ return next;
460
+ }),
461
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, {
462
+ size: 14,
463
+ "aria-hidden": "true"
464
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: account.name })]
465
+ })
466
+ }),
467
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: account.provider }),
468
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
469
+ className: "quota-summary",
470
+ "data-tone": tone(first?.usedPercent),
471
+ children: first ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
472
+ className: "quota-name",
473
+ children: windowLabel(first)
474
+ }), first.usedPercent === void 0 ? text.unknownQuota : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
475
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: percent(first.usedPercent) }),
476
+ " ",
477
+ text.used,
478
+ " · ",
479
+ percent(100 - first.usedPercent),
480
+ " ",
481
+ text.remaining,
482
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("progress", {
483
+ "aria-label": `${account.name} · ${windowLabel(first)}`,
484
+ max: 100,
485
+ value: first.usedPercent
486
+ })
487
+ ] })] }) : "—"
488
+ }),
489
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: first?.resetAt ? date(first.resetAt) : "—" }),
490
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: badge }),
491
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: refresh })
492
+ ] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tr", {
493
+ id: detailId,
494
+ hidden: !expanded.has(key),
495
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
496
+ colSpan: 6,
497
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
498
+ className: "quota-table-detail",
499
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
500
+ className: "quota-detail-header",
501
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: account.name }), reset]
502
+ }), details]
503
+ })
504
+ })
505
+ })] }, key);
506
+ };
507
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
508
+ className: "dsh-cli-quota",
509
+ children: [
510
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
511
+ className: "quota-toolbar",
512
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
513
+ className: "quota-search",
514
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSearchOutlineRegular, {
515
+ size: 16,
516
+ "aria-hidden": "true"
517
+ }),
518
+ type: "search",
519
+ "aria-label": text.searchAccounts,
520
+ placeholder: text.searchAccounts,
521
+ value: search,
522
+ onChange: (event) => {
523
+ setSearch(event.target.value);
524
+ setPage(0);
525
+ }
526
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
527
+ className: "quota-toolbar-actions",
528
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.SegmentedControl, {
529
+ id,
530
+ value: layout,
531
+ label: text.quotaView,
532
+ options: [{
533
+ value: "cards",
534
+ label: text.quotaCards
535
+ }, {
536
+ value: "table",
537
+ label: text.quotaTable
538
+ }],
539
+ onChange: setLayout
540
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
541
+ variant: "outline",
542
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconRefreshOutlineRegular, {
543
+ size: 16,
544
+ "aria-hidden": "true"
545
+ }),
546
+ disabled: busy || !canLoad,
547
+ onClick: () => {
548
+ setPage(0);
549
+ setExpanded(/* @__PURE__ */ new Set());
550
+ onLoad();
551
+ },
552
+ children: text.refreshQuotaAccounts
553
+ })]
554
+ })]
555
+ }),
556
+ ["cards", "table"].map((value) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
557
+ id: `${id}-${value}-panel`,
558
+ role: "tabpanel",
559
+ "aria-labelledby": `${id}-${value}`,
560
+ hidden: layout !== value,
561
+ children: layout === value && (!accounts ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
562
+ className: "quota-empty",
563
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: text.quotaLoadTitle }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
564
+ className: "hint",
565
+ children: text.quotaLoadHint
566
+ })]
567
+ }) : filtered.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
568
+ className: "quota-empty",
569
+ children: text.emptyAccounts
570
+ }) : layout === "cards" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
571
+ className: "quota-grid",
572
+ children: filtered.slice(currentPage * 20, (currentPage + 1) * 20).map(renderAccount)
573
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
574
+ className: "quota-table-scroll",
575
+ role: "region",
576
+ "aria-label": text.quotaTable,
577
+ tabIndex: 0,
578
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("table", {
579
+ "aria-label": text.quota,
580
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tr", { children: [
581
+ text.quotaAccount,
582
+ text.quotaProvider,
583
+ text.quotaUsage,
584
+ text.resetsAt,
585
+ text.quotaStatus,
586
+ text.quotaActions
587
+ ].map((label) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", {
588
+ scope: "col",
589
+ children: label
590
+ }, label)) }) }), filtered.slice(currentPage * 20, (currentPage + 1) * 20).map(renderAccount)]
591
+ })
592
+ }))
593
+ }, value)),
594
+ accounts && filtered.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
595
+ className: "quota-pagination",
596
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
597
+ className: "quota-name",
598
+ children: [
599
+ filtered.length,
600
+ " · ",
601
+ currentPage + 1,
602
+ " / ",
603
+ pages
604
+ ]
605
+ }), pages > 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
606
+ variant: "outline",
607
+ disabled: currentPage === 0,
608
+ onClick: () => setPage(currentPage - 1),
609
+ children: text.previous
610
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
611
+ variant: "outline",
612
+ disabled: currentPage + 1 >= pages,
613
+ onClick: () => setPage(currentPage + 1),
614
+ children: text.next
615
+ })] })]
616
+ }),
617
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
618
+ className: "quota-note",
619
+ children: text.quotaHint
620
+ })
621
+ ]
622
+ });
623
+ }
624
+ // src/client.tsx
625
+ const name = "dungsil-cli-proxy-api";
626
+ const inject = [
627
+ "connection",
628
+ "locale",
629
+ "slots"
630
+ ];
631
+ const styles = `${settingsStyles}${quotaStyles}
632
+ .dsh-cli-proxy { container-type: inline-size; color: var(--dsw-alias-label-primary); font: 14px/22px var(--dsw-font-family, system-ui); }
633
+ .dsh-cli-proxy-page { height: 100%; min-height: 0; box-sizing: border-box; overflow-y: auto; padding: 24px; padding-inline-start: max(24px, var(--dsh-frame-leading-clearance, 0px)); }
634
+ .dsh-cli-proxy-page > .dsh-cli-proxy { max-width: 960px; margin: 0 auto; }
635
+ .dsh-cli-proxy h1 { font-size: 20px; line-height: 28px; font-weight: 500; margin: 0 0 24px; }
636
+ .dsh-cli-proxy h3 { margin: 24px 0 0; font: inherit; color: var(--dsw-alias-label-secondary); }
637
+ .dsh-cli-proxy .controls { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 8px; min-width: 0; max-width: 65%; }
638
+ .dsh-cli-proxy .controls > span:has(> input) { box-sizing: border-box; width: 280px; min-width: 0; max-width: 100%; }
639
+ .dsh-cli-proxy .controls > button { max-width: 100%; }
640
+ .dsh-cli-proxy .hint { color: var(--dsw-alias-label-secondary); font-size: 12px; line-height: 20px; margin: 8px 0; }
641
+ .dsh-cli-proxy [role=alert] { color: var(--dsw-alias-state-error-primary, #b54424); }
642
+ .dsh-cli-proxy p, .dsh-cli-proxy .dsh-plugin-settings-text { overflow-wrap: anywhere; }
643
+ .dsh-cli-proxy .quota-window { padding: 8px 0; }
644
+ .dsh-cli-proxy .quota-heading { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 8px; }
645
+ .dsh-cli-proxy progress { display: block; width: 100%; height: 6px; margin: 8px 0; border: 0; border-radius: 3px; overflow: hidden; appearance: none; background: var(--dsw-alias-bg-module-platform); }
646
+ .dsh-cli-proxy progress::-webkit-progress-bar { background: var(--dsw-alias-bg-module-platform); }
647
+ .dsh-cli-proxy progress::-webkit-progress-value { background: var(--dsw-alias-brand-primary-new-colorprimary-new-color); }
648
+ .dsh-cli-proxy progress::-moz-progress-bar { background: var(--dsw-alias-brand-primary-new-colorprimary-new-color); }
649
+ @container (max-width: 480px) {
650
+ .dsh-cli-proxy .dsh-plugin-settings-row { flex-direction: column; align-items: stretch; gap: 12px; }
651
+ .dsh-cli-proxy .controls { width: 100%; max-width: 100%; justify-content: flex-start; }
652
+ .dsh-cli-proxy .controls > span:has(> input) { width: 100%; }
653
+ }
654
+ `;
655
+ function ProxyPage({ ctx, settings = false }) {
656
+ const locale = useLocaleSnapshot(ctx.locale);
657
+ const text = localizedCopy(locale.active);
658
+ const id = (0, react.useId)();
659
+ const [connectionVersion, setConnectionVersion] = (0, react.useState)(0);
660
+ const connection = ctx.get("connection");
661
+ const [view, setView] = (0, react.useState)();
662
+ const [baseURL, setBaseURL] = (0, react.useState)("");
663
+ const [apiKey, setAPIKey] = (0, react.useState)("");
664
+ const [managementKey, setManagementKey] = (0, react.useState)("");
665
+ const [removeAPIKey, setRemoveAPIKey] = (0, react.useState)(false);
666
+ const [removeManagementKey, setRemoveManagementKey] = (0, react.useState)(false);
667
+ const [synchronized, setSynchronized] = (0, react.useState)(false);
668
+ const [busy, setBusy] = (0, react.useState)(true);
669
+ const [notice, setNotice] = (0, react.useState)();
670
+ const [quotaAccounts, setQuotaAccounts] = (0, react.useState)();
671
+ const [quotas, setQuotas] = (0, react.useState)({});
672
+ const [uncertain, setUncertain] = (0, react.useState)(false);
673
+ const lock = (0, react.useRef)(true);
674
+ const lifetime = (0, react.useRef)();
675
+ const call = (0, react.useCallback)(async (endpoint, payload, controller) => {
676
+ const result = await connection.rpc.call(channel, endpointPrefix + endpoint, payload, controller.signal);
677
+ if (controller.signal.aborted || lifetime.current !== controller) throw new Error("inactive");
678
+ if (!result.ok) throw new Error("request failed");
679
+ return result.value;
680
+ }, [connection]);
681
+ const describe = (0, react.useCallback)(async (controller) => {
682
+ setSynchronized(false);
683
+ const next = await call("describe", {}, controller);
684
+ setView(next);
685
+ setBaseURL(next.baseURL);
686
+ setSynchronized(true);
687
+ setAPIKey("");
688
+ setManagementKey("");
689
+ setRemoveAPIKey(false);
690
+ setRemoveManagementKey(false);
691
+ setQuotaAccounts(void 0);
692
+ setQuotas({});
693
+ }, [call]);
694
+ (0, react.useEffect)(() => {
695
+ const dispose = settings ? void 0 : ctx.on("cli-proxy-api/connection-changed", () => setConnectionVersion((version) => version + 1));
696
+ const unsubscribe = connection.generation.subscribe(() => {
697
+ if (connection.generation.getSnapshot()) setConnectionVersion((version) => version + 1);
698
+ });
699
+ return () => {
700
+ dispose?.();
701
+ unsubscribe();
702
+ };
703
+ }, [
704
+ ctx,
705
+ settings,
706
+ connection
707
+ ]);
708
+ (0, react.useEffect)(() => {
709
+ const controller = new AbortController();
710
+ lifetime.current = controller;
711
+ lock.current = true;
712
+ setBusy(true);
713
+ setSynchronized(false);
714
+ setView(void 0);
715
+ setQuotaAccounts(void 0);
716
+ setQuotas({});
717
+ setUncertain(false);
718
+ setAPIKey("");
719
+ setManagementKey("");
720
+ setNotice(void 0);
721
+ describe(controller).catch(() => {
722
+ if (!controller.signal.aborted && lifetime.current === controller) setNotice("error");
723
+ }).finally(() => {
724
+ if (!controller.signal.aborted && lifetime.current === controller) {
725
+ lock.current = false;
726
+ setBusy(false);
727
+ }
728
+ });
729
+ return () => {
730
+ controller.abort();
731
+ if (lifetime.current === controller) lifetime.current = void 0;
732
+ };
733
+ }, [describe, connectionVersion]);
734
+ const run = async (work, mutation = false) => {
735
+ const controller = lifetime.current;
736
+ if (lock.current || !controller || controller.signal.aborted) return;
737
+ lock.current = true;
738
+ setBusy(true);
739
+ setNotice(void 0);
740
+ try {
741
+ await work(controller);
742
+ } catch {
743
+ if (!controller.signal.aborted && lifetime.current === controller) {
744
+ setNotice("error");
745
+ if (mutation) {
746
+ setUncertain(true);
747
+ setQuotaAccounts(void 0);
748
+ }
749
+ }
750
+ } finally {
751
+ if (!controller.signal.aborted && lifetime.current === controller) {
752
+ lock.current = false;
753
+ setBusy(false);
754
+ }
755
+ }
756
+ };
757
+ const save = () => {
758
+ if (!view || !synchronized || lock.current) return;
759
+ const changedURL = baseURL.trim() !== view.baseURL;
760
+ const payload = {
761
+ revision: view.revision,
762
+ baseURL: baseURL.trim(),
763
+ apiKey: apiKey || (removeAPIKey || changedURL ? "" : void 0),
764
+ managementKey: managementKey || (removeManagementKey || changedURL ? "" : void 0)
765
+ };
766
+ setAPIKey("");
767
+ setManagementKey("");
768
+ run(async (controller) => {
769
+ setSynchronized(false);
770
+ let saved = false;
771
+ try {
772
+ await call("save", payload, controller);
773
+ saved = true;
774
+ } catch {
775
+ if (controller.signal.aborted || lifetime.current !== controller) return;
776
+ }
777
+ await describe(controller);
778
+ ctx.emit("cli-proxy-api/connection-changed");
779
+ setNotice(saved ? "savedConnection" : "error");
780
+ });
781
+ };
782
+ const draftChanged = !!view && (baseURL.trim() !== view.baseURL || !!apiKey || !!managementKey || removeAPIKey || removeManagementKey);
783
+ const canManage = !!view && synchronized && !draftChanged && !!view?.managementKeySaved;
784
+ const managementDisabled = busy || !canManage || uncertain;
785
+ const row = (label, children) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SettingsRow, {
786
+ label,
787
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
788
+ className: "controls",
789
+ children
790
+ })
791
+ });
792
+ const button = (label, onClick, disabled = busy) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
793
+ variant: "outline",
794
+ disabled,
795
+ onClick,
796
+ children: label
797
+ });
798
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
799
+ className: "dsh-cli-proxy",
800
+ "aria-label": "CLIProxyAPI",
801
+ "aria-busy": busy,
802
+ children: [
803
+ busy && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
804
+ role: "status",
805
+ children: text.busy
806
+ }),
807
+ notice && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
808
+ role: notice === "error" ? "alert" : "status",
809
+ children: text[notice]
810
+ }),
811
+ !synchronized && !busy && button(text.refresh, () => setConnectionVersion((version) => version + 1)),
812
+ settings ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
813
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: text.connection }),
814
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
815
+ className: "hint",
816
+ children: text.serverHint
817
+ }),
818
+ row(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
819
+ htmlFor: `${id}-url`,
820
+ children: text.baseURL
821
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
822
+ id: `${id}-url`,
823
+ type: "url",
824
+ autoComplete: "off",
825
+ spellCheck: false,
826
+ value: baseURL,
827
+ disabled: busy || !synchronized,
828
+ onChange: (event) => setBaseURL(event.target.value)
829
+ })),
830
+ row(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
831
+ htmlFor: `${id}-api`,
832
+ children: text.apiKey
833
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
834
+ id: `${id}-api`,
835
+ type: "password",
836
+ autoComplete: "off",
837
+ value: apiKey,
838
+ disabled: busy || !synchronized,
839
+ placeholder: removeAPIKey ? text.removed : view?.apiKeySaved ? text.saved : text.notSaved,
840
+ onChange: (event) => {
841
+ setAPIKey(event.target.value);
842
+ setRemoveAPIKey(false);
843
+ }
844
+ }), button(text.remove, () => {
845
+ setAPIKey("");
846
+ setRemoveAPIKey(true);
847
+ }, busy || !synchronized || !view?.apiKeySaved || removeAPIKey)] })),
848
+ row(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
849
+ htmlFor: `${id}-management`,
850
+ children: text.managementKey
851
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
852
+ id: `${id}-management`,
853
+ type: "password",
854
+ autoComplete: "off",
855
+ value: managementKey,
856
+ disabled: busy || !synchronized,
857
+ placeholder: removeManagementKey ? text.removed : view?.managementKeySaved ? text.saved : text.notSaved,
858
+ onChange: (event) => {
859
+ setManagementKey(event.target.value);
860
+ setRemoveManagementKey(false);
861
+ }
862
+ }), button(text.remove, () => {
863
+ setManagementKey("");
864
+ setRemoveManagementKey(true);
865
+ }, busy || !synchronized || !view?.managementKeySaved || removeManagementKey)] })),
866
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
867
+ className: "hint",
868
+ children: text.keyHint
869
+ }),
870
+ !synchronized && !busy && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
871
+ role: "alert",
872
+ children: text.connectionLocked
873
+ }),
874
+ row(text.connection, button(text.save, save, busy || !synchronized || !baseURL.trim())),
875
+ draftChanged && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
876
+ className: "hint",
877
+ children: text.draftHint
878
+ })
879
+ ] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
880
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
881
+ className: "hint",
882
+ children: text.connectionSettingsHint
883
+ }),
884
+ !view?.managementKeySaved && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
885
+ className: "hint",
886
+ children: text.managementHint
887
+ }),
888
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QuotaPanel, {
889
+ accounts: quotaAccounts,
890
+ quotas,
891
+ language: locale.active,
892
+ text,
893
+ busy,
894
+ canLoad: canManage,
895
+ managementDisabled,
896
+ onLoad: () => {
897
+ if (!canManage) return;
898
+ run(async (controller) => {
899
+ setQuotaAccounts(void 0);
900
+ setQuotas({});
901
+ setQuotaAccounts(await call("quota-list", {}, controller));
902
+ setUncertain(false);
903
+ });
904
+ },
905
+ onFetch: (account) => {
906
+ const key = JSON.stringify([account.name, account.authIndex]);
907
+ run(async (controller) => {
908
+ setQuotas((current) => ({
909
+ ...current,
910
+ [key]: void 0
911
+ }));
912
+ try {
913
+ const next = await call("quota-fetch", {
914
+ name: account.name,
915
+ authIndex: account.authIndex
916
+ }, controller);
917
+ setQuotas((current) => ({
918
+ ...current,
919
+ [key]: next
920
+ }));
921
+ } catch {
922
+ if (!controller.signal.aborted && lifetime.current === controller) setQuotas((current) => ({
923
+ ...current,
924
+ [key]: null
925
+ }));
926
+ throw new Error("request failed");
927
+ }
928
+ });
929
+ },
930
+ onReset: (account) => {
931
+ run(async (controller) => {
932
+ await call("reset", { authIndex: account.authIndex }, controller);
933
+ setNotice("success");
934
+ }, true);
935
+ }
936
+ })
937
+ ] })
938
+ ]
939
+ });
940
+ }
941
+ function ProxyPanel({ ctx }) {
942
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
943
+ className: "dsh-cli-proxy-page",
944
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
945
+ className: "dsh-cli-proxy",
946
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h1", { children: "CLIProxyAPI" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProxyPage, { ctx })]
947
+ })
948
+ });
949
+ }
950
+ function apply(ctx) {
951
+ ctx.effect(() => installStyles(styles), "cli-proxy-api: styles");
952
+ ctx.slots.inject("settings.section", () => ctx.slots.register({
953
+ name: "settings.section",
954
+ id: name,
955
+ order: 17,
956
+ label: () => "CLIProxyAPI",
957
+ inject: () => ({ ctx })
958
+ }, (props) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProxyPage, {
959
+ ...props,
960
+ settings: true
961
+ })));
962
+ ctx.slots.inject("main", () => ctx.slots.register({
963
+ name: "main",
964
+ key: name,
965
+ inject: () => ({ ctx })
966
+ }, (props) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProxyPanel, { ...props })));
967
+ ctx.slots.inject("sidebar.panellist", () => ctx.slots.register({
968
+ name: "sidebar.panellist",
969
+ id: name,
970
+ order: 20,
971
+ label: () => "CLIProxyAPI"
972
+ }, ({ size }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPluginPinwheelOutlineRegular, { size })));
973
+ }
974
+ exports.apply = apply;
975
+ exports.inject = inject;
976
+ exports.name = name;
977
+
978
+ return module.exports; } });