dsh-llm-ustc 0.2.0 → 0.3.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/lib/client.js CHANGED
@@ -9,13 +9,16 @@ let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-
9
9
  let react_jsx_runtime = require("react/jsx-runtime");
10
10
  //#region src/client/index.tsx
11
11
  const LOCALE_NS = "llm-ustc";
12
- const SETTINGS_ROUTE = "/_dsh/llm-ustc/settings";
12
+ const SETTINGS_ROUTE = "/api/llm-ustc/settings";
13
13
  const en = {
14
- title: "USTC LLM",
15
- intro: "Manage the USTC API credential, model catalog, and iWAN access.",
16
- expand: "Show settings",
17
- collapse: "Hide settings",
18
14
  apiKeyLabel: "USTC API Key",
15
+ keySource: "Key source",
16
+ manualKey: "Enter API Key",
17
+ tokenworksKey: "Tokenworks sign-in",
18
+ beginTokenworks: "Sign in to Tokenworks",
19
+ awaitingLogin: "Waiting for browser sign-in...",
20
+ cancelLogin: "Cancel sign-in",
21
+ signedIn: "Signed in",
19
22
  apiKeyPlaceholder: "Enter a new key",
20
23
  configured: "Configured",
21
24
  readOnly: "The active credential or settings provider is read-only.",
@@ -42,6 +45,8 @@ const en = {
42
45
  models: "Model catalog",
43
46
  syncModels: "Sync models",
44
47
  syncingModels: "Syncing...",
48
+ removeModel: "Remove model",
49
+ modelsEmpty: "No models",
45
50
  refreshedAt: "Refreshed",
46
51
  never: "Never",
47
52
  loading: "Loading...",
@@ -49,11 +54,14 @@ const en = {
49
54
  retry: "Retry"
50
55
  };
51
56
  const zh = {
52
- title: "科大大模型",
53
- intro: "管理科大 API 凭据、模型目录和 iWAN 访问。",
54
- expand: "展开设置",
55
- collapse: "收起设置",
56
57
  apiKeyLabel: "科大 API Key",
58
+ keySource: "Key 来源",
59
+ manualKey: "手动输入 API Key",
60
+ tokenworksKey: "词元工坊登录",
61
+ beginTokenworks: "登录词元工坊",
62
+ awaitingLogin: "等待浏览器登录...",
63
+ cancelLogin: "取消登录",
64
+ signedIn: "已登录",
57
65
  apiKeyPlaceholder: "输入新的 Key",
58
66
  configured: "已配置",
59
67
  readOnly: "当前凭据或设置提供方为只读。",
@@ -80,6 +88,8 @@ const zh = {
80
88
  models: "模型目录",
81
89
  syncModels: "同步模型",
82
90
  syncingModels: "正在同步...",
91
+ removeModel: "删除模型",
92
+ modelsEmpty: "暂无模型",
83
93
  refreshedAt: "刷新时间",
84
94
  never: "从未",
85
95
  loading: "正在加载...",
@@ -101,6 +111,7 @@ var UstcSettingsController = class {
101
111
  current = { status: "idle" };
102
112
  listeners = /* @__PURE__ */ new Set();
103
113
  generation = 0;
114
+ polling = false;
104
115
  subscribe = (listener) => {
105
116
  this.listeners.add(listener);
106
117
  return () => {
@@ -118,7 +129,8 @@ var UstcSettingsController = class {
118
129
  ...this.current,
119
130
  status: "loading",
120
131
  action: "load",
121
- error: void 0
132
+ error: void 0,
133
+ errorAction: void 0
122
134
  });
123
135
  try {
124
136
  const snapshot = await apiRequest();
@@ -126,7 +138,8 @@ var UstcSettingsController = class {
126
138
  this.set({
127
139
  status: "ready",
128
140
  snapshot,
129
- oidc: this.current.oidc
141
+ oidc: this.current.oidc,
142
+ tokenworksUrl: snapshot.tokenworks.pending ? this.current.tokenworksUrl : void 0
130
143
  });
131
144
  } catch (error) {
132
145
  if (generation !== this.generation) return;
@@ -134,7 +147,8 @@ var UstcSettingsController = class {
134
147
  ...this.current,
135
148
  status: "error",
136
149
  action: void 0,
137
- error: messageOf(error)
150
+ error: messageOf(error),
151
+ errorAction: "load"
138
152
  });
139
153
  }
140
154
  }
@@ -142,10 +156,12 @@ var UstcSettingsController = class {
142
156
  if (this.current.status !== "idle" && this.current.action === void 0) this.load();
143
157
  }
144
158
  async run(action, request, apply) {
159
+ ++this.generation;
145
160
  this.set({
146
161
  ...this.current,
147
162
  action,
148
- error: void 0
163
+ error: void 0,
164
+ errorAction: void 0
149
165
  });
150
166
  try {
151
167
  const value = await apiRequest({
@@ -157,7 +173,8 @@ var UstcSettingsController = class {
157
173
  this.set({
158
174
  ...this.current,
159
175
  action: void 0,
160
- error: messageOf(error)
176
+ error: messageOf(error),
177
+ errorAction: action
161
178
  });
162
179
  }
163
180
  }
@@ -165,7 +182,8 @@ var UstcSettingsController = class {
165
182
  return this.run(action, request, (snapshot, state) => ({
166
183
  status: "ready",
167
184
  snapshot,
168
- oidc: state.oidc
185
+ oidc: state.oidc,
186
+ tokenworksUrl: snapshot.tokenworks.pending ? state.tokenworksUrl : void 0
169
187
  }));
170
188
  }
171
189
  beginOidc() {
@@ -175,6 +193,37 @@ var UstcSettingsController = class {
175
193
  oidc
176
194
  }));
177
195
  }
196
+ beginTokenworks() {
197
+ return this.run("beginTokenworks", {}, (value, state) => ({
198
+ ...state,
199
+ status: "ready",
200
+ action: void 0,
201
+ snapshot: value.snapshot,
202
+ tokenworksUrl: value.authUrl
203
+ }));
204
+ }
205
+ async pollLogin() {
206
+ if (this.current.action !== void 0 || this.polling) return;
207
+ this.polling = true;
208
+ const generation = this.generation;
209
+ try {
210
+ const snapshot = await apiRequest();
211
+ if (generation !== this.generation) return;
212
+ this.set({
213
+ ...this.current,
214
+ snapshot,
215
+ tokenworksUrl: snapshot.tokenworks.pending ? this.current.tokenworksUrl : void 0
216
+ });
217
+ } catch (error) {
218
+ if (generation === this.generation) this.set({
219
+ ...this.current,
220
+ error: messageOf(error),
221
+ errorAction: "beginTokenworks"
222
+ });
223
+ } finally {
224
+ this.polling = false;
225
+ }
226
+ }
178
227
  completeOidc(callbackUrl) {
179
228
  return this.run("completeOidc", { callbackUrl }, (value) => ({
180
229
  status: "ready",
@@ -185,58 +234,28 @@ var UstcSettingsController = class {
185
234
  function messageOf(error) {
186
235
  return error instanceof Error ? error.message : String(error);
187
236
  }
188
- function IwanSettingsItem({ controller, t }) {
189
- if (controller === void 0 || t === void 0) return null;
190
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UstcCard, {
191
- controller,
192
- t
193
- });
194
- }
195
- function UstcCard({ controller, t }) {
237
+ function UstcBundleConfig({ controller, t }) {
196
238
  const state = (0, react.useSyncExternalStore)(controller.subscribe, controller.snapshot, controller.snapshot);
197
- const [open, setOpen] = (0, react.useState)(false);
198
- (0, react.useEffect)(() => {
199
- if (state.status === "idle") controller.load();
200
- }, [controller, state.status]);
201
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
202
- className: "ulu-card",
203
- "data-open": open || void 0,
204
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
205
- type: "button",
206
- className: "ulu-card-header",
207
- "aria-expanded": open,
208
- "aria-label": `${t(open ? "collapse" : "expand")}: ${t("title")}`,
209
- onClick: () => {
210
- setOpen((value) => !value);
211
- },
212
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
213
- className: "ulu-card-head-text",
214
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
215
- className: "ulu-card-name",
216
- children: t("title")
217
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
218
- className: "ulu-card-description",
219
- children: t("intro")
220
- })]
221
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { className: "ulu-card-chevron" })]
222
- }), open ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
223
- className: "ulu-card-body",
224
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LoadedSettings, {
225
- controller,
226
- t,
227
- state
228
- })
229
- }) : null]
230
- });
231
- }
232
- function LoadedSettings({ controller, t, state }) {
233
239
  const snapshot = state.snapshot;
234
240
  const [apiKey, setApiKey] = (0, react.useState)("");
235
241
  const [callbackUrl, setCallbackUrl] = (0, react.useState)("");
236
242
  const [selectedServerId, setSelectedServerId] = (0, react.useState)("");
243
+ (0, react.useEffect)(() => {
244
+ controller.load();
245
+ }, [controller]);
237
246
  (0, react.useEffect)(() => {
238
247
  setSelectedServerId(snapshot?.selectedServerId ?? "");
239
248
  }, [snapshot?.selectedServerId]);
249
+ (0, react.useEffect)(() => {
250
+ setApiKey("");
251
+ }, [snapshot?.keySource]);
252
+ (0, react.useEffect)(() => {
253
+ if (!snapshot?.tokenworks.pending) return;
254
+ const timer = setInterval(() => {
255
+ controller.pollLogin();
256
+ }, 1500);
257
+ return () => clearInterval(timer);
258
+ }, [controller, snapshot?.tokenworks.pending]);
240
259
  if ((state.status === "idle" || state.status === "loading") && snapshot === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
241
260
  className: "ulu-loading",
242
261
  children: t("loading")
@@ -260,31 +279,7 @@ function LoadedSettings({ controller, t, state }) {
260
279
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
261
280
  className: "ulu-settings",
262
281
  children: [
263
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
264
- className: "ulu-status-row",
265
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("route") }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
266
- className: "ulu-route-value",
267
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: snapshot.usingIwan ? t("iwanRoute") : t("directRoute") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
268
- label: t("refreshRoute"),
269
- side: "top",
270
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
271
- className: "ulu-route-refresh",
272
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
273
- type: "button",
274
- variant: "toolbar",
275
- size: "sm",
276
- icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconRefreshOutline14, { className: state.action === "refreshRoute" ? "ulu-spin" : void 0 }),
277
- "aria-label": t("refreshRoute"),
278
- disabled: busy,
279
- onClick: () => {
280
- controller.mutateSnapshot("refreshRoute");
281
- }
282
- })
283
- })
284
- })]
285
- })]
286
- }),
287
- !snapshot.writable || !snapshot.apiKey.writable || !snapshot.iwan.writable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
282
+ !snapshot.writable || !(snapshot.keySource === "manual" ? snapshot.apiKey.writable : snapshot.tokenworks.writable) || !snapshot.iwan.writable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
288
283
  className: "ulu-alert warning",
289
284
  children: t("readOnly")
290
285
  }) : null,
@@ -295,17 +290,36 @@ function LoadedSettings({ controller, t, state }) {
295
290
  className: "ulu-section-title",
296
291
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t("apiKeyLabel") })
297
292
  }),
298
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
293
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
294
+ className: "ulu-line-select",
295
+ "aria-label": t("keySource"),
296
+ value: snapshot.keySource,
297
+ disabled: busy || !snapshot.writable,
298
+ onChange: (event) => {
299
+ controller.mutateSnapshot("selectKeySource", {
300
+ keySource: event.target.value,
301
+ expectedRevision: snapshot.revision
302
+ });
303
+ },
304
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
305
+ value: "manual",
306
+ children: t("manualKey")
307
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
308
+ value: "tokenworks",
309
+ children: t("tokenworksKey")
310
+ })]
311
+ }),
312
+ snapshot.keySource === "manual" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
313
+ className: "ulu-input",
299
314
  type: "password",
300
- autoComplete: "off",
315
+ autoComplete: "new-password",
301
316
  "aria-label": t("apiKeyLabel"),
302
317
  placeholder: snapshot.apiKey.configured ? t("configured") : t("apiKeyPlaceholder"),
303
318
  value: apiKey,
304
319
  onChange: (event) => {
305
320
  setApiKey(event.target.value);
306
321
  }
307
- }),
308
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
322
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
309
323
  className: "ulu-actions",
310
324
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
311
325
  size: "sm",
@@ -326,9 +340,80 @@ function LoadedSettings({ controller, t, state }) {
326
340
  },
327
341
  children: t("remove")
328
342
  })]
329
- })
343
+ })] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
344
+ className: "ulu-login-row",
345
+ children: snapshot.tokenworks.pending ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [state.tokenworksUrl === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
346
+ className: "ulu-login-state",
347
+ children: t("awaitingLogin")
348
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
349
+ className: "ulu-link",
350
+ href: state.tokenworksUrl,
351
+ target: "_blank",
352
+ rel: "noopener noreferrer",
353
+ children: t("openLogin")
354
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
355
+ size: "sm",
356
+ variant: "outline",
357
+ disabled: busy,
358
+ onClick: () => {
359
+ controller.mutateSnapshot("cancelTokenworks");
360
+ },
361
+ children: t("cancelLogin")
362
+ })] }) : snapshot.tokenworks.configured ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
363
+ className: "ulu-login-state",
364
+ children: snapshot.tokenworks.name ?? t("signedIn")
365
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
366
+ size: "sm",
367
+ variant: "outline",
368
+ disabled: busy || !snapshot.tokenworks.writable,
369
+ onClick: () => {
370
+ controller.mutateSnapshot("logoutTokenworks");
371
+ },
372
+ children: state.action === "logoutTokenworks" ? t("loggingOut") : t("logoutIwan")
373
+ })] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
374
+ size: "sm",
375
+ variant: "primary",
376
+ disabled: busy || !snapshot.tokenworks.writable,
377
+ onClick: () => {
378
+ controller.beginTokenworks();
379
+ },
380
+ children: state.action === "beginTokenworks" ? t("startingOidc") : t("beginTokenworks")
381
+ })
382
+ }),
383
+ state.errorAction === "saveApiKey" || state.errorAction === "unsetApiKey" || state.errorAction === "selectKeySource" || state.errorAction === "beginTokenworks" || state.errorAction === "cancelTokenworks" || state.errorAction === "logoutTokenworks" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
384
+ className: "ulu-alert error",
385
+ children: state.error
386
+ }) : null,
387
+ snapshot.keySource === "tokenworks" && snapshot.tokenworks.error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
388
+ className: "ulu-alert error",
389
+ children: snapshot.tokenworks.error
390
+ }) : null
330
391
  ]
331
392
  }),
393
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
394
+ className: "ulu-status-row",
395
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("route") }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
396
+ className: "ulu-route-value",
397
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: snapshot.usingIwan ? t("iwanRoute") : t("directRoute") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
398
+ label: t("refreshRoute"),
399
+ side: "top",
400
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
401
+ className: "ulu-route-refresh",
402
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
403
+ type: "button",
404
+ variant: "ghost",
405
+ size: "sm",
406
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconRefreshOutlineMedium, { className: state.action === "refreshRoute" ? "ulu-spin" : void 0 }),
407
+ "aria-label": t("refreshRoute"),
408
+ disabled: busy,
409
+ onClick: () => {
410
+ controller.mutateSnapshot("refreshRoute");
411
+ }
412
+ })
413
+ })
414
+ })]
415
+ })]
416
+ }),
332
417
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
333
418
  className: "ulu-section",
334
419
  children: [
@@ -368,7 +453,8 @@ function LoadedSettings({ controller, t, state }) {
368
453
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
369
454
  htmlFor: "ulu-callback-url",
370
455
  children: t("callbackLabel")
371
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
456
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
457
+ className: "ulu-input",
372
458
  id: "ulu-callback-url",
373
459
  value: callbackUrl,
374
460
  placeholder: t("callbackPlaceholder"),
@@ -417,46 +503,91 @@ function LoadedSettings({ controller, t, state }) {
417
503
  },
418
504
  children: state.action === "selectServer" ? t("applyingLine") : t("selectLine")
419
505
  })]
506
+ }) : null,
507
+ state.errorAction === "beginOidc" || state.errorAction === "completeOidc" || state.errorAction === "logoutIwan" || state.errorAction === "selectServer" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
508
+ className: "ulu-alert error",
509
+ children: state.error
420
510
  }) : null
421
511
  ]
422
512
  }),
423
513
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
424
514
  className: "ulu-section",
425
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
426
- className: "ulu-section-title",
427
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t("models") }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", { children: [
428
- t("refreshedAt"),
429
- ": ",
430
- snapshot.modelsUpdatedAt === void 0 ? t("never") : new Date(snapshot.modelsUpdatedAt).toLocaleString()
431
- ] })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
432
- size: "sm",
433
- variant: "outline",
434
- disabled: busy || !snapshot.apiKey.configured,
435
- onClick: () => {
436
- controller.mutateSnapshot("syncModels");
437
- },
438
- children: state.action === "syncModels" ? t("syncingModels") : t("syncModels")
439
- })]
440
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
441
- className: "ulu-model-list",
442
- children: snapshot.models.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
443
- className: "ulu-model-entry",
444
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: model.id }), model.name === model.id ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: model.name })]
445
- }, model.id))
446
- })]
447
- })
515
+ children: [
516
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
517
+ className: "ulu-section-title",
518
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: t("models") }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", { children: [
519
+ t("refreshedAt"),
520
+ ": ",
521
+ snapshot.modelsUpdatedAt === void 0 ? t("never") : new Date(snapshot.modelsUpdatedAt).toLocaleString()
522
+ ] })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
523
+ size: "sm",
524
+ variant: "outline",
525
+ disabled: busy || !(snapshot.keySource === "manual" ? snapshot.apiKey.configured : snapshot.tokenworks.configured),
526
+ onClick: () => {
527
+ controller.mutateSnapshot("syncModels");
528
+ },
529
+ children: state.action === "syncModels" ? t("syncingModels") : t("syncModels")
530
+ })]
531
+ }),
532
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
533
+ className: "ulu-model-list",
534
+ children: [snapshot.models.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
535
+ className: "ulu-model-entry",
536
+ children: [
537
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: model.id }),
538
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: model.name === model.id ? "" : model.name }),
539
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
540
+ className: "ulu-model-remove",
541
+ type: "button",
542
+ title: t("removeModel"),
543
+ "aria-label": `${t("removeModel")}: ${model.id}`,
544
+ disabled: busy,
545
+ onClick: () => {
546
+ controller.mutateSnapshot("removeModel", { modelId: model.id });
547
+ },
548
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutlineRegular, {})
549
+ })
550
+ ]
551
+ }, model.id)), snapshot.models.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
552
+ className: "ulu-model-empty",
553
+ children: t("modelsEmpty")
554
+ }) : null]
555
+ }),
556
+ state.errorAction === "syncModels" || state.errorAction === "removeModel" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
557
+ className: "ulu-alert error",
558
+ children: state.error
559
+ }) : null
560
+ ]
561
+ }),
562
+ state.errorAction === "refreshRoute" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
563
+ className: "ulu-alert error",
564
+ children: state.error
565
+ }) : null
448
566
  ]
449
567
  });
450
568
  }
451
569
  const CSS = `
452
- .ulu-card{list-style:none;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-layer-3);transition:border-color .16s,background .16s}.ulu-card:hover{border-color:var(--dsw-alias-label-dimmed)}.ulu-card[data-open=true]{border-color:var(--dsw-alias-label-dimmed);background:var(--dsw-alias-bg-layer-2)}
453
- .ulu-card-header{appearance:none;display:flex;width:100%;align-items:center;gap:12px;padding:14px 16px;border:0;border-radius:12px;background:transparent;color:inherit;font:inherit;text-align:left;cursor:pointer}.ulu-card-header:focus-visible{outline:none;box-shadow:inset 2px 0 0 var(--dsw-alias-brand-primary)}.ulu-card-head-text{display:flex;min-width:0;flex:1;flex-direction:column;gap:4px}.ulu-card-name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.ulu-card-description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.ulu-card-chevron{flex:none;color:var(--dsw-alias-label-tertiary);transition:transform .16s}.ulu-card[data-open=true] .ulu-card-chevron{transform:rotate(180deg)}.ulu-card-body{margin:0 16px;padding-bottom:8px;border-top:1px solid var(--dsw-alias-border-l2)}
454
- .ulu-settings{color:var(--dsw-alias-label-primary)}
455
- .ulu-section{display:grid;min-width:0;gap:6px;padding:12px 0;border-top:1px solid var(--dsw-alias-border-l2)}.ulu-section>*{min-width:0}.ulu-section-title{display:flex;min-width:0;align-items:center;justify-content:space-between;gap:14px}.ulu-section-title>div{min-width:0}.ulu-section-title h3{margin:0;color:var(--dsw-alias-label-primary);font-size:13px;font-weight:500;letter-spacing:0;line-height:1.5}.ulu-section-title p{max-width:620px;margin:3px 0 0;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:1.5;overflow-wrap:anywhere}
456
- .ulu-row{display:grid;min-width:0;grid-template-columns:minmax(0,1fr) auto;align-items:end;gap:10px}.ulu-field{display:grid;min-width:0;gap:6px}.ulu-field>*{min-width:0;max-width:100%;box-sizing:border-box}.ulu-field>label{color:var(--dsw-alias-label-primary);font-size:13px;font-weight:500;line-height:1.5}.ulu-field>span{width:100%}.ulu-line-select{box-sizing:border-box;width:100%;height:34px;padding:0 12px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);font:inherit;font-size:13px}.ulu-line-select:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}.ulu-actions,.ulu-login-row{display:flex;min-width:0;align-items:center;gap:8px;flex-wrap:wrap}.ulu-link{display:inline-flex;min-height:34px;align-items:center;color:var(--dsw-alias-brand-primary);font-size:12px;font-weight:500;text-decoration:none}.ulu-link:hover{text-decoration:underline}
457
- .ulu-status-row{display:flex;min-height:34px;align-items:center;justify-content:space-between;gap:16px;color:var(--dsw-alias-label-secondary);font-size:12px}.ulu-status-row strong{color:var(--dsw-alias-label-primary);font-weight:500}.ulu-route-value{display:flex;align-items:center;gap:5px}.ulu-route-refresh{display:inline-flex}.ulu-route-refresh>button{width:28px;padding:0}.ulu-spin{animation:ulu-spin .7s linear infinite}@keyframes ulu-spin{to{transform:rotate(360deg)}}.ulu-alert{margin:0;padding:10px 0;border-top:1px solid var(--dsw-alias-border-l2);font-size:12px;line-height:1.5}.ulu-alert.warning{color:var(--dsw-alias-label-warning,#8c641b)}.ulu-alert.error{color:var(--dsw-alias-label-error,#a43d39)}
458
- .ulu-loading{padding:16px 0;color:var(--dsw-alias-label-tertiary);font-size:12px}.ulu-load-error{display:flex;align-items:center;justify-content:space-between;gap:12px}.ulu-load-error .ulu-alert{flex:1;border-top:0}
459
- .ulu-model-list{display:flex;max-height:300px;min-width:0;flex-direction:column;overflow:auto;border-top:1px solid var(--dsw-alias-border-l2)}.ulu-model-entry{display:grid;min-width:0;grid-template-columns:minmax(0,1.3fr) minmax(0,1fr);gap:8px;padding:9px 2px;border-bottom:1px solid var(--dsw-alias-border-l2);font-size:12px;line-height:18px}.ulu-model-entry code,.ulu-model-entry span{min-width:0;overflow-wrap:anywhere}.ulu-model-entry span{color:var(--dsw-alias-label-tertiary)}
570
+ .ulu-settings{display:flex;min-width:0;flex-direction:column;color:var(--dsw-alias-label-primary)}
571
+ .ulu-status-row{display:flex;min-height:34px;align-items:center;justify-content:space-between;gap:16px;padding:12px 0;border-top:0.5px solid var(--dsw-alias-border-l2);font-size:13px;line-height:1.5}
572
+ .ulu-status-row strong{font-weight:500}.ulu-route-value{display:flex;align-items:center;gap:5px}.ulu-route-refresh{display:inline-flex}.ulu-route-refresh>button,.ulu-route-refresh>button:hover:not(:disabled),.ulu-route-refresh>button:active:not(:disabled){width:28px;padding:0;background:transparent}
573
+ .ulu-section{display:grid;min-width:0;gap:6px;padding:12px 0;border-top:0.5px solid var(--dsw-alias-border-l2)}.ulu-section>*{min-width:0}
574
+ .ulu-section-title{display:flex;min-width:0;align-items:center;justify-content:space-between;gap:8px}.ulu-section-title>div{min-width:0}
575
+ .ulu-section-title h3,.ulu-field>label{margin:0;color:var(--dsw-alias-label-primary);font-size:13px;font-weight:500;line-height:1.5}
576
+ .ulu-section-title p{margin:3px 0 0;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:1.5;overflow-wrap:anywhere}
577
+ .ulu-input,.ulu-line-select{box-sizing:border-box;width:100%;height:34px;padding:0 12px;border:0.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;line-height:1.5}
578
+ .ulu-input::placeholder{color:var(--dsw-alias-label-dimmed)}.ulu-input:focus-visible,.ulu-line-select:focus-visible{outline:none;border-color:var(--dsw-alias-state-business-primary)}
579
+ .ulu-row{display:grid;min-width:0;grid-template-columns:minmax(0,1fr) auto;align-items:end;gap:10px}.ulu-field{display:grid;min-width:0;gap:6px}.ulu-field>*{min-width:0}
580
+ .ulu-actions,.ulu-login-row{display:flex;min-width:0;align-items:center;gap:8px}.ulu-actions{padding-top:16px}.ulu-actions button:first-child,.ulu-login-row button,.ulu-row>button{min-height:31px;padding:5px 14px;border-radius:var(--dsw-radius-md);font-size:13px;line-height:1.5}
581
+ .ulu-link{display:inline-flex;min-height:31px;align-items:center;color:var(--dsw-alias-label-secondary);font-size:13px;line-height:1.5;text-decoration:none}.ulu-link:hover{color:var(--dsw-alias-label-primary);text-decoration:underline}
582
+ .ulu-login-state{color:var(--dsw-alias-label-secondary);font-size:13px;line-height:1.5}
583
+ .ulu-spin{animation:ulu-spin .7s linear infinite}@keyframes ulu-spin{to{transform:rotate(360deg)}}
584
+ .ulu-alert{margin:0;padding:10px 0;border-top:0.5px solid var(--dsw-alias-border-l2);font-size:12px;line-height:1.5}.ulu-alert.warning{color:var(--dsw-alias-label-tertiary)}.ulu-alert.error{color:var(--dsw-alias-label-error)}
585
+ .ulu-loading{padding:12px 0;color:var(--dsw-alias-label-tertiary);font-size:12px}.ulu-load-error{display:flex;align-items:center;justify-content:space-between;gap:12px}.ulu-load-error .ulu-alert{flex:1;border-top:0}
586
+ .ulu-model-list{display:flex;max-height:300px;min-width:0;flex-direction:column;overflow:auto;border-top:0.5px solid var(--dsw-alias-border-l2)}
587
+ .ulu-model-entry{display:grid;min-width:0;grid-template-columns:minmax(0,1.3fr) minmax(0,1fr) auto;align-items:center;gap:8px;padding:9px 2px;border-bottom:0.5px solid var(--dsw-alias-border-l2);font-size:12px;line-height:18px}
588
+ .ulu-model-entry code,.ulu-model-entry span{min-width:0;overflow-wrap:anywhere}.ulu-model-entry span{color:var(--dsw-alias-label-tertiary)}
589
+ .ulu-model-remove{display:inline-flex;width:20px;height:20px;align-items:center;justify-content:center;padding:0;border:0;background:transparent;color:var(--dsw-alias-label-tertiary);cursor:pointer}.ulu-model-remove:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.ulu-model-remove:disabled{opacity:.5;cursor:default}
590
+ .ulu-model-empty{padding:9px 2px;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}
460
591
  `;
461
592
  function installStyles() {
462
593
  const id = "dsh-llm-ustc/client";
@@ -486,14 +617,14 @@ function apply(ctx) {
486
617
  dispose();
487
618
  };
488
619
  }, "dsh-llm-ustc: settings invalidations");
489
- ctx.slots.inject("settings.plugin.item", () => ctx.slots.register({
490
- name: "settings.plugin.item",
491
- key: LOCALE_NS,
620
+ ctx.slots.inject("plugins.bundle.config", () => ctx.slots.register({
621
+ name: "plugins.bundle.config",
622
+ key: "dsh-llm-ustc",
492
623
  inject: () => ({
493
624
  controller,
494
625
  t
495
626
  })
496
- }, IwanSettingsItem));
627
+ }, UstcBundleConfig));
497
628
  }
498
629
  //#endregion
499
630
  exports.UstcSettingsController = UstcSettingsController;
package/lib/config.js CHANGED
@@ -1,14 +1,17 @@
1
1
  import z from '@deepseek-ai/schemastery';
2
+ import { API_KEY_REF } from "./constants.js";
2
3
  export const Config = z.object({
3
- selectedServerId: z.string(),
4
- directRecoverySeconds: z.number().step(1).min(30).max(86_400).default(300),
4
+ apiKeyEnv: z.union([API_KEY_REF]).role('credential-ref').default(API_KEY_REF).volatile(),
5
+ keySource: z.union(['manual', 'tokenworks']).default('manual').volatile(),
6
+ selectedServerId: z.string().volatile(),
7
+ directRecoverySeconds: z.number().step(1).min(30).max(86_400).default(300).volatile(),
5
8
  });
6
9
  export function resolveConfig(config) {
7
- const directRecoverySeconds = config.directRecoverySeconds ?? 300;
10
+ const directRecoverySeconds = config.directRecoverySeconds.get();
8
11
  if (!Number.isSafeInteger(directRecoverySeconds) || directRecoverySeconds < 30 || directRecoverySeconds > 86_400) {
9
12
  throw new TypeError('llm-ustc.directRecoverySeconds must be an integer from 30 to 86400');
10
13
  }
11
- const selected = config.selectedServerId?.trim();
14
+ const selected = config.selectedServerId.get()?.trim();
12
15
  return {
13
16
  ...(selected === undefined || selected.length === 0 ? {} : { selectedServerId: selected }),
14
17
  directRecoverySeconds,
package/lib/config.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,MAAM,0BAA0B,CAAA;AAOxC,MAAM,CAAC,MAAM,MAAM,GAAc,CAAC,CAAC,MAAM,CAAC;IACxC,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE;IAC5B,qBAAqB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC;CAC3E,CAAC,CAAA;AAOF,MAAM,UAAU,aAAa,CAAC,MAAc;IAC1C,MAAM,qBAAqB,GAAG,MAAM,CAAC,qBAAqB,IAAI,GAAG,CAAA;IACjE,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,qBAAqB,CAAC,IAAI,qBAAqB,GAAG,EAAE,IAAI,qBAAqB,GAAG,MAAM,EAAE,CAAC;QACjH,MAAM,IAAI,SAAS,CAAC,oEAAoE,CAAC,CAAA;IAC3F,CAAC;IACD,MAAM,QAAQ,GAAG,MAAM,CAAC,gBAAgB,EAAE,IAAI,EAAE,CAAA;IAChD,OAAO;QACL,GAAG,CAAC,QAAQ,KAAK,SAAS,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,QAAQ,EAAE,CAAC;QAC1F,qBAAqB;KACtB,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AACA,OAAO,CAAC,MAAM,0BAA0B,CAAA;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAS5C,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7B,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,QAAQ,EAAE;IACxF,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,QAAQ,EAAE;IACzE,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACvC,qBAAqB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;CACtF,CAAC,CAAA;AAOF,MAAM,UAAU,aAAa,CAAC,MAAc;IAC1C,MAAM,qBAAqB,GAAG,MAAM,CAAC,qBAAqB,CAAC,GAAG,EAAE,CAAA;IAChE,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,qBAAqB,CAAC,IAAI,qBAAqB,GAAG,EAAE,IAAI,qBAAqB,GAAG,MAAM,EAAE,CAAC;QACjH,MAAM,IAAI,SAAS,CAAC,oEAAoE,CAAC,CAAA;IAC3F,CAAC;IACD,MAAM,QAAQ,GAAG,MAAM,CAAC,gBAAgB,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,CAAA;IACtD,OAAO;QACL,GAAG,CAAC,QAAQ,KAAK,SAAS,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,QAAQ,EAAE,CAAC;QAC1F,qBAAqB;KACtB,CAAA;AACH,CAAC"}
package/lib/constants.js CHANGED
@@ -2,10 +2,11 @@ export const PROVIDER = 'ustc';
2
2
  export const PROVIDER_NAME = 'USTC LLM';
3
3
  export const SETTINGS_NS = 'llm-ustc';
4
4
  export const API_KEY_REF = 'USTC_LLM_API_KEY';
5
+ export const TOKENWORKS_SESSION_REF = 'USTC_LLM_TOKENWORKS_SESSION';
5
6
  export const IWAN_CONFIG_REF = 'USTC_LLM_IWAN_CONFIG';
6
- export const DEFAULT_MODEL = 'deepseek-v4-flash-ascend';
7
+ export const DEFAULT_MODEL = 'deepseek-flash';
7
8
  export const DEFAULT_CONTEXT_WINDOW = 262_144;
8
9
  export const DEFAULT_MAX_TOKENS = 32_768;
9
10
  export const HELPER_PROTOCOL = 'v2';
10
- export const SETTINGS_ROUTE = '/_dsh/llm-ustc/settings';
11
+ export const SETTINGS_ROUTE = '/api/llm-ustc/settings';
11
12
  //# sourceMappingURL=constants.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,QAAQ,GAAG,MAAM,CAAA;AAC9B,MAAM,CAAC,MAAM,aAAa,GAAG,UAAU,CAAA;AACvC,MAAM,CAAC,MAAM,WAAW,GAAG,UAAU,CAAA;AACrC,MAAM,CAAC,MAAM,WAAW,GAAG,kBAAkB,CAAA;AAC7C,MAAM,CAAC,MAAM,eAAe,GAAG,sBAAsB,CAAA;AACrD,MAAM,CAAC,MAAM,aAAa,GAAG,0BAA0B,CAAA;AACvD,MAAM,CAAC,MAAM,sBAAsB,GAAG,OAAO,CAAA;AAC7C,MAAM,CAAC,MAAM,kBAAkB,GAAG,MAAM,CAAA;AACxC,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,CAAA;AACnC,MAAM,CAAC,MAAM,cAAc,GAAG,yBAAyB,CAAA"}
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,QAAQ,GAAG,MAAM,CAAA;AAC9B,MAAM,CAAC,MAAM,aAAa,GAAG,UAAU,CAAA;AACvC,MAAM,CAAC,MAAM,WAAW,GAAG,UAAU,CAAA;AACrC,MAAM,CAAC,MAAM,WAAW,GAAG,kBAAkB,CAAA;AAC7C,MAAM,CAAC,MAAM,sBAAsB,GAAG,6BAA6B,CAAA;AACnE,MAAM,CAAC,MAAM,eAAe,GAAG,sBAAsB,CAAA;AACrD,MAAM,CAAC,MAAM,aAAa,GAAG,gBAAgB,CAAA;AAC7C,MAAM,CAAC,MAAM,sBAAsB,GAAG,OAAO,CAAA;AAC7C,MAAM,CAAC,MAAM,kBAAkB,GAAG,MAAM,CAAA;AACxC,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,CAAA;AACnC,MAAM,CAAC,MAAM,cAAc,GAAG,wBAAwB,CAAA"}