@tnnevol/dsh-codex-auth 0.1.0-rc.8 → 0.1.0-rc.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,5 +8,7 @@ declare const CODEX_AUTH_LOGOUT_PATH = "/plugins/dsh-codex-auth-plugin/auth/logo
8
8
  declare const CODEX_USAGE_PATH = "/plugins/dsh-codex-auth-plugin/auth/usage";
9
9
  /** Same-origin settings endpoint used when DSH marks a NAS browser as remote. */
10
10
  declare const CODEX_AUTH_SETTINGS_PATH = "/plugins/dsh-codex-auth-plugin/auth/settings";
11
+ /** Same-origin endpoint for the DSH-wide Codex default model selection. */
12
+ declare const CODEX_GLOBAL_MODEL_PATH = "/plugins/dsh-codex-auth-plugin/auth/global-model";
11
13
  //#endregion
12
- export { CODEX_AUTH_LOGIN_PATH, CODEX_AUTH_LOGOUT_PATH, CODEX_AUTH_SETTINGS_NAMESPACE, CODEX_AUTH_SETTINGS_PATH, CODEX_AUTH_STATUS_PATH, CODEX_USAGE_PATH };
14
+ export { CODEX_AUTH_LOGIN_PATH, CODEX_AUTH_LOGOUT_PATH, CODEX_AUTH_SETTINGS_NAMESPACE, CODEX_AUTH_SETTINGS_PATH, CODEX_AUTH_STATUS_PATH, CODEX_GLOBAL_MODEL_PATH, CODEX_USAGE_PATH };
package/lib/auth-paths.js CHANGED
@@ -8,5 +8,7 @@ const CODEX_AUTH_LOGOUT_PATH = "/plugins/dsh-codex-auth-plugin/auth/logout";
8
8
  const CODEX_USAGE_PATH = "/plugins/dsh-codex-auth-plugin/auth/usage";
9
9
  /** Same-origin settings endpoint used when DSH marks a NAS browser as remote. */
10
10
  const CODEX_AUTH_SETTINGS_PATH = "/plugins/dsh-codex-auth-plugin/auth/settings";
11
+ /** Same-origin endpoint for the DSH-wide Codex default model selection. */
12
+ const CODEX_GLOBAL_MODEL_PATH = "/plugins/dsh-codex-auth-plugin/auth/global-model";
11
13
  //#endregion
12
- export { CODEX_AUTH_LOGIN_PATH, CODEX_AUTH_LOGOUT_PATH, CODEX_AUTH_SETTINGS_NAMESPACE, CODEX_AUTH_SETTINGS_PATH, CODEX_AUTH_STATUS_PATH, CODEX_USAGE_PATH };
14
+ export { CODEX_AUTH_LOGIN_PATH, CODEX_AUTH_LOGOUT_PATH, CODEX_AUTH_SETTINGS_NAMESPACE, CODEX_AUTH_SETTINGS_PATH, CODEX_AUTH_STATUS_PATH, CODEX_GLOBAL_MODEL_PATH, CODEX_USAGE_PATH };
package/lib/client.js CHANGED
@@ -6,23 +6,24 @@ window.__ModuleLoader__.load({
6
6
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
7
  let react = require("react");
8
8
  let react_jsx_runtime = require("react/jsx-runtime");
9
+ let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
9
10
  //#region src/client/CodexCapabilities.tsx
10
11
  /** Live optional-capability settings for the Codex Auth plugin. */
11
- const sectionStyle = {
12
+ const sectionStyle$1 = {
12
13
  display: "flex",
13
14
  flexDirection: "column",
14
15
  gap: 12,
15
16
  borderTop: "1px solid var(--dsw-alias-border-l2)",
16
17
  paddingTop: 14
17
18
  };
18
- const headingStyle = {
19
+ const headingStyle$1 = {
19
20
  margin: 0,
20
21
  fontSize: 14,
21
22
  lineHeight: "20px",
22
23
  fontWeight: 600,
23
24
  color: "var(--dsw-alias-label-primary)"
24
25
  };
25
- const bodyStyle$1 = {
26
+ const bodyStyle$2 = {
26
27
  margin: 0,
27
28
  fontSize: 12,
28
29
  lineHeight: "18px",
@@ -47,6 +48,7 @@ window.__ModuleLoader__.load({
47
48
  cursor: "not-allowed",
48
49
  opacity: .62
49
50
  };
51
+ const checkboxStyle = { accentColor: "var(--dsw-alias-button-primary-fill)" };
50
52
  const copyStyle = {
51
53
  display: "flex",
52
54
  flexDirection: "column",
@@ -58,7 +60,7 @@ window.__ModuleLoader__.load({
58
60
  fontWeight: 500,
59
61
  color: "var(--dsw-alias-label-primary)"
60
62
  };
61
- const actionsStyle = {
63
+ const actionsStyle$1 = {
62
64
  display: "flex",
63
65
  alignItems: "center",
64
66
  justifyContent: "space-between",
@@ -69,7 +71,7 @@ window.__ModuleLoader__.load({
69
71
  display: "flex",
70
72
  gap: 8
71
73
  };
72
- const buttonStyle$1 = {
74
+ const buttonStyle$2 = {
73
75
  boxSizing: "border-box",
74
76
  minHeight: 30,
75
77
  padding: "4px 12px",
@@ -81,18 +83,18 @@ window.__ModuleLoader__.load({
81
83
  fontSize: 12,
82
84
  cursor: "pointer"
83
85
  };
84
- const primaryButtonStyle$1 = {
85
- ...buttonStyle$1,
86
+ const primaryButtonStyle$2 = {
87
+ ...buttonStyle$2,
86
88
  border: 0,
87
89
  background: "var(--dsw-alias-button-primary-fill)",
88
90
  color: "var(--dsw-alias-label-primary-foreground)"
89
91
  };
90
- const errorStyle$1 = {
91
- ...bodyStyle$1,
92
+ const errorStyle$2 = {
93
+ ...bodyStyle$2,
92
94
  color: "var(--dsw-alias-state-error-primary, #d92d20)"
93
95
  };
94
- const successStyle = {
95
- ...bodyStyle$1,
96
+ const successStyle$1 = {
97
+ ...bodyStyle$2,
96
98
  color: "var(--dsw-alias-state-success-primary, #16825d)"
97
99
  };
98
100
  const UNAVAILABLE_SNAPSHOT = {
@@ -165,32 +167,32 @@ window.__ModuleLoader__.load({
165
167
  const loading = snapshot.status === "loading";
166
168
  const editable = snapshot.status === "ready" && snapshot.writable && !busy;
167
169
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
168
- style: sectionStyle,
170
+ style: sectionStyle$1,
169
171
  "aria-labelledby": "dsh-codex-capabilities-title",
170
172
  children: [
171
173
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
172
174
  id: "dsh-codex-capabilities-title",
173
- style: headingStyle,
175
+ style: headingStyle$1,
174
176
  children: t("capabilitiesTitle")
175
177
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
176
178
  style: {
177
- ...bodyStyle$1,
179
+ ...bodyStyle$2,
178
180
  marginTop: 3
179
181
  },
180
182
  children: t("capabilitiesIntro")
181
183
  })] }),
182
184
  loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
183
- style: bodyStyle$1,
185
+ style: bodyStyle$2,
184
186
  role: "status",
185
187
  children: t("settingsLoading")
186
188
  }) : null,
187
189
  snapshot.status === "unavailable" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
188
- style: errorStyle$1,
190
+ style: errorStyle$2,
189
191
  role: "alert",
190
192
  children: t("settingsUnavailable")
191
193
  }) : null,
192
194
  snapshot.status === "ready" && !snapshot.writable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
193
- style: errorStyle$1,
195
+ style: errorStyle$2,
194
196
  role: "alert",
195
197
  children: t("settingsReadOnly")
196
198
  }) : null,
@@ -201,6 +203,7 @@ window.__ModuleLoader__.load({
201
203
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
202
204
  style: rowStyle$1,
203
205
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
206
+ style: checkboxStyle,
204
207
  type: "checkbox",
205
208
  checked: draft.enableImageTool,
206
209
  onChange: (event) => {
@@ -212,7 +215,7 @@ window.__ModuleLoader__.load({
212
215
  style: labelStyle,
213
216
  children: t("enableImageRecognition")
214
217
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
215
- style: bodyStyle$1,
218
+ style: bodyStyle$2,
216
219
  children: t("enableImageRecognitionHelp")
217
220
  })]
218
221
  })]
@@ -220,6 +223,7 @@ window.__ModuleLoader__.load({
220
223
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
221
224
  style: rowStyle$1,
222
225
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
226
+ style: checkboxStyle,
223
227
  type: "checkbox",
224
228
  checked: draft.enableImageUpload,
225
229
  onChange: (event) => {
@@ -231,7 +235,7 @@ window.__ModuleLoader__.load({
231
235
  style: labelStyle,
232
236
  children: t("enableImageUpload")
233
237
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
234
- style: bodyStyle$1,
238
+ style: bodyStyle$2,
235
239
  children: t("enableImageUploadHelp")
236
240
  })]
237
241
  })]
@@ -240,6 +244,7 @@ window.__ModuleLoader__.load({
240
244
  style: disabledRowStyle,
241
245
  title: t("imageGenerationUnavailableHelp"),
242
246
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
247
+ style: checkboxStyle,
243
248
  type: "checkbox",
244
249
  checked: false,
245
250
  disabled: true,
@@ -251,7 +256,7 @@ window.__ModuleLoader__.load({
251
256
  style: labelStyle,
252
257
  children: t("enableImageGeneration")
253
258
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
254
- style: bodyStyle$1,
259
+ style: bodyStyle$2,
255
260
  children: t("imageGenerationUnavailableHelp")
256
261
  })]
257
262
  })]
@@ -259,27 +264,27 @@ window.__ModuleLoader__.load({
259
264
  ]
260
265
  }),
261
266
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
262
- style: actionsStyle,
267
+ style: actionsStyle$1,
263
268
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
264
269
  "aria-live": "polite",
265
270
  children: [feedback === "saved" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
266
- style: successStyle,
271
+ style: successStyle$1,
267
272
  children: t("settingsSaved")
268
273
  }) : null, feedback === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
269
- style: errorStyle$1,
274
+ style: errorStyle$2,
270
275
  children: t("settingsSaveFailed")
271
276
  }) : null]
272
277
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
273
278
  style: buttonsStyle,
274
279
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
275
280
  type: "button",
276
- style: buttonStyle$1,
281
+ style: buttonStyle$2,
277
282
  disabled: !dirty || busy,
278
283
  onClick: discard,
279
284
  children: t("discard")
280
285
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
281
286
  type: "button",
282
- style: primaryButtonStyle$1,
287
+ style: primaryButtonStyle$2,
283
288
  disabled: !dirty || !snapshot.writable || busy,
284
289
  onClick: () => {
285
290
  save();
@@ -302,6 +307,566 @@ window.__ModuleLoader__.load({
302
307
  const CODEX_USAGE_PATH = "/plugins/dsh-codex-auth-plugin/auth/usage";
303
308
  /** Same-origin settings endpoint used when DSH marks a NAS browser as remote. */
304
309
  const CODEX_AUTH_SETTINGS_PATH = "/plugins/dsh-codex-auth-plugin/auth/settings";
310
+ /** Same-origin endpoint for the DSH-wide Codex default model selection. */
311
+ const CODEX_GLOBAL_MODEL_PATH = "/plugins/dsh-codex-auth-plugin/auth/global-model";
312
+ //#endregion
313
+ //#region src/client/CodexGlobalModel.tsx
314
+ /** DSH-wide Codex default model controls. */
315
+ const sectionStyle = {
316
+ display: "flex",
317
+ flexDirection: "column",
318
+ gap: 12,
319
+ borderTop: "1px solid var(--dsw-alias-border-l2)",
320
+ paddingTop: 14
321
+ };
322
+ const headingStyle = {
323
+ margin: 0,
324
+ fontSize: 14,
325
+ lineHeight: "20px",
326
+ fontWeight: 600,
327
+ color: "var(--dsw-alias-label-primary)"
328
+ };
329
+ const bodyStyle$1 = {
330
+ margin: 0,
331
+ fontSize: 12,
332
+ lineHeight: "18px",
333
+ color: "var(--dsw-alias-label-secondary)"
334
+ };
335
+ const fieldsStyle = {
336
+ display: "flex",
337
+ alignItems: "center",
338
+ minWidth: 0
339
+ };
340
+ const actionsStyle = {
341
+ display: "flex",
342
+ alignItems: "center",
343
+ justifyContent: "space-between",
344
+ gap: 10,
345
+ flexWrap: "wrap"
346
+ };
347
+ const buttonStyle$1 = {
348
+ boxSizing: "border-box",
349
+ minHeight: 30,
350
+ padding: "4px 12px",
351
+ border: "1px solid var(--dsw-alias-border-l2)",
352
+ borderRadius: 16,
353
+ background: "var(--dsw-alias-bg-layer-1)",
354
+ color: "var(--dsw-alias-label-primary)",
355
+ font: "inherit",
356
+ fontSize: 12,
357
+ cursor: "pointer"
358
+ };
359
+ const primaryButtonStyle$1 = {
360
+ ...buttonStyle$1,
361
+ border: 0,
362
+ background: "var(--dsw-alias-button-primary-fill)",
363
+ color: "var(--dsw-alias-label-primary-foreground)"
364
+ };
365
+ const errorStyle$1 = {
366
+ ...bodyStyle$1,
367
+ color: "var(--dsw-alias-state-error-primary, #d92d20)"
368
+ };
369
+ const successStyle = {
370
+ ...bodyStyle$1,
371
+ color: "var(--dsw-alias-state-success-primary, #16825d)"
372
+ };
373
+ async function request(method, body) {
374
+ const response = await fetch(CODEX_GLOBAL_MODEL_PATH, {
375
+ method,
376
+ headers: {
377
+ accept: "application/json",
378
+ ...body === void 0 ? {} : { "content-type": "application/json" }
379
+ },
380
+ ...body === void 0 ? {} : { body: JSON.stringify(body) },
381
+ credentials: "same-origin"
382
+ });
383
+ const payload = await response.json().catch(() => void 0);
384
+ if (!response.ok) throw new Error(typeof payload === "object" && payload !== null && "error" in payload && typeof payload.error === "string" ? payload.error : `HTTP ${response.status}`);
385
+ return payload;
386
+ }
387
+ function codexModels(groups) {
388
+ return groups.find((group) => group.id === "openai-codex")?.models ?? [];
389
+ }
390
+ const choiceButtonStyle = {
391
+ boxSizing: "border-box",
392
+ display: "flex",
393
+ alignItems: "center",
394
+ justifyContent: "space-between",
395
+ gap: 8,
396
+ width: "100%",
397
+ minHeight: 34,
398
+ padding: "5px 9px",
399
+ border: "1px solid var(--dsw-alias-border-l2)",
400
+ borderRadius: 8,
401
+ background: "var(--dsw-alias-bg-layer-1)",
402
+ color: "var(--dsw-alias-label-primary)",
403
+ font: "inherit",
404
+ fontSize: 13,
405
+ textAlign: "left",
406
+ cursor: "pointer"
407
+ };
408
+ const choicePanelStyle = {
409
+ position: "absolute",
410
+ zIndex: 20,
411
+ top: "calc(100% + 6px)",
412
+ left: 0,
413
+ width: "max(100%, 220px)",
414
+ maxHeight: 260,
415
+ overflowY: "auto",
416
+ boxSizing: "border-box",
417
+ padding: 6,
418
+ border: "1px solid var(--dsw-alias-border-l1, var(--dsw-alias-border-l2))",
419
+ borderRadius: 10,
420
+ background: "var(--dsw-alias-bg-layer-3, var(--dsw-alias-bg-layer-2))",
421
+ boxShadow: "var(--dsw-alias-shadow-popover, 0 12px 30px rgb(0 0 0 / 24%))"
422
+ };
423
+ const choiceItemStyle = {
424
+ display: "flex",
425
+ alignItems: "center",
426
+ justifyContent: "space-between",
427
+ gap: 10,
428
+ width: "100%",
429
+ minHeight: 34,
430
+ padding: "6px 9px",
431
+ border: 0,
432
+ borderRadius: 7,
433
+ background: "transparent",
434
+ color: "var(--dsw-alias-label-primary)",
435
+ font: "inherit",
436
+ fontSize: 13,
437
+ textAlign: "left",
438
+ cursor: "pointer"
439
+ };
440
+ function ChevronDown() {
441
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
442
+ "aria-hidden": "true",
443
+ style: {
444
+ display: "inline-flex",
445
+ alignItems: "center",
446
+ color: "var(--dsw-alias-label-tertiary)",
447
+ lineHeight: 1
448
+ },
449
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 14 })
450
+ });
451
+ }
452
+ const pickerButtonStyle = {
453
+ ...choiceButtonStyle,
454
+ width: "fit-content",
455
+ maxWidth: "100%",
456
+ minHeight: 30,
457
+ padding: "3px 4px",
458
+ border: 0,
459
+ borderRadius: 7,
460
+ background: "transparent",
461
+ fontSize: 14
462
+ };
463
+ const pickerPanelStyle = {
464
+ ...choicePanelStyle,
465
+ display: "flex",
466
+ position: "absolute",
467
+ gap: 0,
468
+ width: "min(210px, calc(100vw - 32px))",
469
+ maxHeight: 260,
470
+ padding: 5,
471
+ overflow: "visible"
472
+ };
473
+ const pickerColumnStyle = {
474
+ display: "flex",
475
+ width: "100%",
476
+ flexDirection: "column",
477
+ gap: 2
478
+ };
479
+ const pickerSubmenuStyle = {
480
+ position: "absolute",
481
+ zIndex: 1,
482
+ top: 5,
483
+ left: "calc(100% + 4px)",
484
+ width: "210px",
485
+ maxHeight: 250,
486
+ overflowY: "auto",
487
+ boxSizing: "border-box",
488
+ display: "flex",
489
+ flexDirection: "column",
490
+ gap: 2,
491
+ padding: 5,
492
+ border: "1px solid var(--dsw-alias-border-l1, var(--dsw-alias-border-l2))",
493
+ borderRadius: 10,
494
+ background: "var(--dsw-alias-bg-layer-3, var(--dsw-alias-bg-layer-2))",
495
+ boxShadow: "var(--dsw-alias-shadow-popover, 0 12px 30px rgb(0 0 0 / 24%))"
496
+ };
497
+ function PickerRow({ label, value, disabled, active, onClick }) {
498
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
499
+ type: "button",
500
+ disabled,
501
+ style: {
502
+ ...choiceItemStyle,
503
+ minHeight: 34,
504
+ padding: "4px 8px",
505
+ background: active ? "var(--dsw-alias-interactive-bg-active, var(--dsw-alias-interactive-bg-hover))" : "transparent",
506
+ opacity: disabled ? .5 : 1,
507
+ cursor: disabled ? "not-allowed" : "pointer"
508
+ },
509
+ onMouseEnter: (event) => {
510
+ if (!disabled) event.currentTarget.style.background = "var(--dsw-alias-interactive-bg-hover, var(--dsw-alias-bg-layer-1))";
511
+ },
512
+ onMouseLeave: (event) => {
513
+ event.currentTarget.style.background = active ? "var(--dsw-alias-interactive-bg-active, var(--dsw-alias-interactive-bg-hover))" : "transparent";
514
+ },
515
+ onClick,
516
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
517
+ style: {
518
+ minWidth: 0,
519
+ overflow: "hidden",
520
+ textOverflow: "ellipsis",
521
+ whiteSpace: "nowrap",
522
+ fontSize: 13
523
+ },
524
+ children: label
525
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
526
+ style: {
527
+ display: "inline-flex",
528
+ alignItems: "center",
529
+ gap: 6,
530
+ minWidth: 0,
531
+ color: "var(--dsw-alias-label-secondary)",
532
+ fontSize: 13
533
+ },
534
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
535
+ style: {
536
+ maxWidth: 140,
537
+ overflow: "hidden",
538
+ textOverflow: "ellipsis",
539
+ whiteSpace: "nowrap"
540
+ },
541
+ children: value
542
+ }), !disabled ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
543
+ "aria-hidden": "true",
544
+ style: {
545
+ fontSize: 18,
546
+ lineHeight: 1
547
+ },
548
+ children: "›"
549
+ }) : null]
550
+ })]
551
+ });
552
+ }
553
+ function GlobalModelPicker({ modelMenuLabel, effortMenuLabel, modelLabel, modelPlaceholder, effortLabel, selectedModelId, selectedEffortId, modelOptions, effortOptions, activeMenu, onToggle, onOpenSubmenu, onChooseModel, onChooseEffort }) {
554
+ const open = activeMenu !== null;
555
+ const modelValue = modelLabel === "" ? modelPlaceholder : modelLabel;
556
+ const buttonValue = effortLabel === "" ? modelValue : `${modelValue} · ${effortLabel}`;
557
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
558
+ style: {
559
+ position: "relative",
560
+ width: "fit-content",
561
+ maxWidth: "100%"
562
+ },
563
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
564
+ type: "button",
565
+ "aria-haspopup": "menu",
566
+ "aria-expanded": open,
567
+ style: pickerButtonStyle,
568
+ onClick: onToggle,
569
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
570
+ style: {
571
+ minWidth: 0,
572
+ overflow: "hidden",
573
+ textOverflow: "ellipsis",
574
+ whiteSpace: "nowrap",
575
+ color: modelLabel === "" ? "var(--dsw-alias-label-dimmed)" : "var(--dsw-alias-label-primary)"
576
+ },
577
+ children: buttonValue
578
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChevronDown, {})]
579
+ }), open ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
580
+ role: "menu",
581
+ style: pickerPanelStyle,
582
+ children: [
583
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
584
+ style: pickerColumnStyle,
585
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PickerRow, {
586
+ label: modelMenuLabel,
587
+ value: modelValue,
588
+ active: activeMenu === "model",
589
+ onClick: () => {
590
+ onOpenSubmenu("model");
591
+ }
592
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PickerRow, {
593
+ label: effortMenuLabel,
594
+ value: effortLabel,
595
+ active: activeMenu === "effort",
596
+ onClick: () => {
597
+ onOpenSubmenu("effort");
598
+ }
599
+ })]
600
+ }),
601
+ activeMenu === "model" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
602
+ role: "menu",
603
+ "aria-label": modelMenuLabel,
604
+ style: pickerSubmenuStyle,
605
+ children: modelOptions.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
606
+ type: "button",
607
+ role: "menuitemradio",
608
+ "aria-checked": option.id === selectedModelId,
609
+ style: option.id === selectedModelId ? {
610
+ ...choiceItemStyle,
611
+ background: "var(--dsw-alias-interactive-bg-active, var(--dsw-alias-interactive-bg-hover))"
612
+ } : choiceItemStyle,
613
+ onClick: () => {
614
+ onChooseModel(option.id);
615
+ },
616
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
617
+ style: {
618
+ minWidth: 0,
619
+ overflow: "hidden",
620
+ textOverflow: "ellipsis",
621
+ whiteSpace: "nowrap"
622
+ },
623
+ children: option.label
624
+ }), option.id === selectedModelId ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
625
+ "aria-hidden": "true",
626
+ children: "✓"
627
+ }) : null]
628
+ }, option.id))
629
+ }) : null,
630
+ activeMenu === "effort" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
631
+ role: "menu",
632
+ "aria-label": effortMenuLabel,
633
+ style: pickerSubmenuStyle,
634
+ children: effortOptions.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
635
+ type: "button",
636
+ role: "menuitemradio",
637
+ "aria-checked": option.id === selectedEffortId,
638
+ style: option.id === selectedEffortId ? {
639
+ ...choiceItemStyle,
640
+ background: "var(--dsw-alias-interactive-bg-active, var(--dsw-alias-interactive-bg-hover))"
641
+ } : choiceItemStyle,
642
+ onClick: () => {
643
+ onChooseEffort(option.id);
644
+ },
645
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: option.label }), option.id === selectedEffortId ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
646
+ "aria-hidden": "true",
647
+ children: "✓"
648
+ }) : null]
649
+ }, option.id))
650
+ }) : null
651
+ ]
652
+ }) : null]
653
+ });
654
+ }
655
+ /** Set the model used by new DSH sessions and the host-backed agent entry point. */
656
+ function CodexGlobalModel({ connection, t }) {
657
+ const [models, setModels] = (0, react.useState)([]);
658
+ const [current, setCurrent] = (0, react.useState)();
659
+ const [draftModel, setDraftModel] = (0, react.useState)("");
660
+ const [draftEffort, setDraftEffort] = (0, react.useState)("");
661
+ const [status, setStatus] = (0, react.useState)("loading");
662
+ const [busy, setBusy] = (0, react.useState)(false);
663
+ const [feedback, setFeedback] = (0, react.useState)("idle");
664
+ const [openMenu, setOpenMenu] = (0, react.useState)(null);
665
+ const fieldsRef = (0, react.useRef)(null);
666
+ const load = (0, react.useCallback)(async () => {
667
+ setStatus("loading");
668
+ try {
669
+ const [global, catalog] = await Promise.all([request("GET"), connection.api.llm.models({})]);
670
+ if (!catalog.result.ok) throw new Error(catalog.result.error.message);
671
+ const available = codexModels(catalog.result.value.groups);
672
+ setModels(available);
673
+ setCurrent(global.globalModel);
674
+ setDraftModel(global.globalModel?.model ?? "");
675
+ setDraftEffort(global.globalModel?.reasoningEffort ?? "");
676
+ setStatus("ready");
677
+ } catch {
678
+ setStatus("error");
679
+ }
680
+ }, [connection]);
681
+ (0, react.useEffect)(() => {
682
+ load();
683
+ }, [load]);
684
+ (0, react.useEffect)(() => {
685
+ if (openMenu === null) return;
686
+ const onPointerDown = (event) => {
687
+ if (event.target instanceof Node && fieldsRef.current?.contains(event.target)) return;
688
+ setOpenMenu(null);
689
+ };
690
+ const onKeyDown = (event) => {
691
+ if (event.key === "Escape") setOpenMenu(null);
692
+ };
693
+ document.addEventListener("pointerdown", onPointerDown);
694
+ document.addEventListener("keydown", onKeyDown);
695
+ return () => {
696
+ document.removeEventListener("pointerdown", onPointerDown);
697
+ document.removeEventListener("keydown", onKeyDown);
698
+ };
699
+ }, [openMenu]);
700
+ const selected = (0, react.useMemo)(() => models.find((model) => model.id === draftModel), [draftModel, models]);
701
+ const efforts = selected?.reasoning?.efforts ?? [];
702
+ const modelLabel = selected?.name ?? draftModel;
703
+ const modelOptions = [
704
+ {
705
+ id: "",
706
+ label: t("globalModelUnset")
707
+ },
708
+ ...draftModel !== "" && !models.some((model) => model.id === draftModel) ? [{
709
+ id: draftModel,
710
+ label: draftModel
711
+ }] : [],
712
+ ...models.map((model) => ({
713
+ id: model.id,
714
+ label: model.name
715
+ }))
716
+ ];
717
+ const effortOptions = [{
718
+ id: "",
719
+ label: t("globalModelProviderDefault")
720
+ }, ...efforts.map((effort) => ({
721
+ id: effort.id,
722
+ label: effort.name
723
+ }))];
724
+ const dirty = draftModel.length > 0 && (current?.model !== draftModel || (current?.reasoningEffort ?? "") !== draftEffort);
725
+ const save = async () => {
726
+ if (!dirty || busy) return;
727
+ setBusy(true);
728
+ setFeedback("idle");
729
+ try {
730
+ const result = await request("PUT", {
731
+ model: draftModel,
732
+ ...draftEffort.length === 0 ? {} : { reasoningEffort: draftEffort }
733
+ });
734
+ setCurrent(result.globalModel);
735
+ setDraftModel(result.globalModel?.model ?? draftModel);
736
+ setDraftEffort(result.globalModel?.reasoningEffort ?? "");
737
+ setFeedback("saved");
738
+ } catch {
739
+ setFeedback("error");
740
+ } finally {
741
+ setBusy(false);
742
+ }
743
+ };
744
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
745
+ style: sectionStyle,
746
+ "aria-labelledby": "dsh-codex-global-model-title",
747
+ children: [
748
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
749
+ id: "dsh-codex-global-model-title",
750
+ style: headingStyle,
751
+ children: t("globalModelTitle")
752
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
753
+ style: {
754
+ ...bodyStyle$1,
755
+ marginTop: 3
756
+ },
757
+ children: t("globalModelIntro")
758
+ })] }),
759
+ status === "loading" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
760
+ style: bodyStyle$1,
761
+ children: t("settingsLoading")
762
+ }) : null,
763
+ status === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
764
+ style: errorStyle$1,
765
+ role: "alert",
766
+ children: t("globalModelLoadFailed")
767
+ }) : null,
768
+ status === "ready" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
769
+ ref: fieldsRef,
770
+ style: fieldsStyle,
771
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(GlobalModelPicker, {
772
+ modelMenuLabel: t("globalModelModel"),
773
+ effortMenuLabel: t("globalModelThinking"),
774
+ modelLabel,
775
+ modelPlaceholder: t("globalModelUnset"),
776
+ effortLabel: effortOptions.find((option) => option.id === draftEffort)?.label ?? t("globalModelProviderDefault"),
777
+ selectedModelId: draftModel,
778
+ selectedEffortId: draftEffort,
779
+ modelOptions,
780
+ effortOptions,
781
+ activeMenu: openMenu,
782
+ onToggle: () => {
783
+ setOpenMenu((current) => current === null ? "root" : null);
784
+ },
785
+ onOpenSubmenu: (menu) => {
786
+ setOpenMenu(menu);
787
+ },
788
+ onChooseModel: (id) => {
789
+ setDraftModel(id);
790
+ setDraftEffort("");
791
+ setFeedback("idle");
792
+ setOpenMenu(null);
793
+ },
794
+ onChooseEffort: (id) => {
795
+ setDraftEffort(id);
796
+ setFeedback("idle");
797
+ setOpenMenu(null);
798
+ }
799
+ }), selected === void 0 && modelLabel !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
800
+ style: bodyStyle$1,
801
+ children: t("globalModelUnavailable")
802
+ }) : null]
803
+ }) : null,
804
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
805
+ style: actionsStyle,
806
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
807
+ "aria-live": "polite",
808
+ children: [feedback === "saved" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
809
+ style: successStyle,
810
+ children: t("settingsSaved")
811
+ }) : null, feedback === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
812
+ style: errorStyle$1,
813
+ children: t("settingsSaveFailed")
814
+ }) : null]
815
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
816
+ style: {
817
+ display: "flex",
818
+ gap: 8
819
+ },
820
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
821
+ type: "button",
822
+ style: buttonStyle$1,
823
+ disabled: status === "loading" || busy,
824
+ onClick: () => {
825
+ load();
826
+ },
827
+ children: t("refreshUsage")
828
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
829
+ type: "button",
830
+ style: primaryButtonStyle$1,
831
+ disabled: !dirty || busy,
832
+ onClick: () => {
833
+ save();
834
+ },
835
+ children: busy ? t("saving") : t("setGlobalModel")
836
+ })]
837
+ })]
838
+ })
839
+ ]
840
+ });
841
+ }
842
+ //#endregion
843
+ //#region src/client/copy-to-clipboard.ts
844
+ async function copyTextToClipboard(text, environment = {}) {
845
+ const navigatorRef = environment.navigator ?? globalThis.navigator;
846
+ const documentRef = environment.document ?? globalThis.document;
847
+ try {
848
+ if (typeof navigatorRef?.clipboard?.writeText === "function") {
849
+ await navigatorRef.clipboard.writeText(text);
850
+ return;
851
+ }
852
+ } catch {}
853
+ if (documentRef === void 0) throw new Error("Clipboard is unavailable");
854
+ const textarea = documentRef.createElement("textarea");
855
+ textarea.value = text;
856
+ textarea.setAttribute("readonly", "");
857
+ textarea.style.position = "fixed";
858
+ textarea.style.top = "0";
859
+ textarea.style.left = "-9999px";
860
+ textarea.style.opacity = "0";
861
+ documentRef.body.appendChild(textarea);
862
+ textarea.focus();
863
+ textarea.select();
864
+ try {
865
+ if (!documentRef.execCommand("copy")) throw new Error("Copy command was rejected");
866
+ } finally {
867
+ documentRef.body.removeChild(textarea);
868
+ }
869
+ }
305
870
  //#endregion
306
871
  //#region src/client/CodexAuthCard.tsx
307
872
  /** Expandable account card for the DSH Plugins settings section. */
@@ -485,7 +1050,7 @@ window.__ModuleLoader__.load({
485
1050
  maxWidth: 192,
486
1051
  height: 8,
487
1052
  borderRadius: 999,
488
- background: "var(--dsw-alias-bg-layer-3, rgba(127, 127, 127, 0.28))"
1053
+ background: "rgba(127, 127, 127, 0.28)"
489
1054
  };
490
1055
  const usageFillStyle = {
491
1056
  height: "100%",
@@ -617,13 +1182,15 @@ window.__ModuleLoader__.load({
617
1182
  })
618
1183
  });
619
1184
  }
620
- /** Render a standalone login/logout card in the rc.7 keyed Plugin slot. */
621
- function CodexAuthCard({ t, configScope }) {
1185
+ /** Render a standalone login/logout card in the rc.8 keyed Plugin slot. */
1186
+ function CodexAuthCard({ t, configScope, connection }) {
622
1187
  if (t === void 0) throw new Error("Codex auth card requires its translation function");
1188
+ if (connection === void 0) throw new Error("Codex auth card requires the DSH connection");
623
1189
  const [open, setOpen] = (0, react.useState)(false);
624
1190
  const [status, setStatus] = (0, react.useState)({ status: "loading" });
625
1191
  const [busy, setBusy] = (0, react.useState)(false);
626
1192
  const [challenge, setChallenge] = (0, react.useState)();
1193
+ const [copyStatus, setCopyStatus] = (0, react.useState)("idle");
627
1194
  const [usage, setUsage] = (0, react.useState)({ status: "hidden" });
628
1195
  const refreshUsage = (0, react.useCallback)(async () => {
629
1196
  if (status.status !== "signed-in") return;
@@ -641,7 +1208,10 @@ window.__ModuleLoader__.load({
641
1208
  try {
642
1209
  const next = await jsonRequest(CODEX_AUTH_STATUS_PATH);
643
1210
  setStatus(next);
644
- if (next.status !== "signing-in") setChallenge(void 0);
1211
+ if (next.status !== "signing-in") {
1212
+ setChallenge(void 0);
1213
+ setCopyStatus("idle");
1214
+ }
645
1215
  } catch (error) {
646
1216
  setStatus(error instanceof AccountRequestError && error.code === "remote-web-origin-not-trusted" ? { status: "remote-web-origin-not-trusted" } : {
647
1217
  status: "error",
@@ -687,6 +1257,7 @@ window.__ModuleLoader__.load({
687
1257
  setBusy(true);
688
1258
  setStatus({ status: "signing-in" });
689
1259
  setChallenge(void 0);
1260
+ setCopyStatus("idle");
690
1261
  try {
691
1262
  const next = await jsonRequest(CODEX_AUTH_LOGIN_PATH, "POST");
692
1263
  setChallenge(next);
@@ -694,6 +1265,7 @@ window.__ModuleLoader__.load({
694
1265
  } catch (error) {
695
1266
  popup.close();
696
1267
  setChallenge(void 0);
1268
+ setCopyStatus("idle");
697
1269
  setStatus(error instanceof AccountRequestError && error.code === "remote-web-origin-not-trusted" ? { status: "remote-web-origin-not-trusted" } : {
698
1270
  status: "error",
699
1271
  message: error instanceof Error ? error.message : t("requestFailed")
@@ -709,6 +1281,7 @@ window.__ModuleLoader__.load({
709
1281
  setStatus({ status: "signed-out" });
710
1282
  setUsage({ status: "hidden" });
711
1283
  setChallenge(void 0);
1284
+ setCopyStatus("idle");
712
1285
  } catch (error) {
713
1286
  setStatus({
714
1287
  status: "error",
@@ -718,6 +1291,15 @@ window.__ModuleLoader__.load({
718
1291
  setBusy(false);
719
1292
  }
720
1293
  };
1294
+ const copyAuthorizationCode = async () => {
1295
+ if (challenge === void 0) return;
1296
+ try {
1297
+ await copyTextToClipboard(challenge.userCode);
1298
+ setCopyStatus("copied");
1299
+ } catch {
1300
+ setCopyStatus("failed");
1301
+ }
1302
+ };
721
1303
  const label = status.status === "signed-in" ? t("signedIn") : status.status === "loading" ? t("loading") : status.status === "signing-in" ? t("signingIn") : status.status === "remote-web-origin-not-trusted" ? t("remoteOrigin") : status.status === "error" ? t("requestFailed") : t("signedOut");
722
1304
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
723
1305
  style: {
@@ -827,34 +1409,56 @@ window.__ModuleLoader__.load({
827
1409
  flexDirection: "column",
828
1410
  gap: 8
829
1411
  },
830
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
831
- style: bodyStyle,
832
- children: t("authorizationCodeHelp")
833
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
834
- style: {
835
- display: "flex",
836
- alignItems: "center",
837
- flexWrap: "wrap",
838
- gap: 8
839
- },
840
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
841
- "aria-label": t("authorizationCodeLabel"),
842
- style: codeStyle,
843
- children: challenge.userCode
844
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
845
- href: challenge.verificationUri,
846
- target: "_blank",
847
- rel: "noreferrer",
1412
+ children: [
1413
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1414
+ style: bodyStyle,
1415
+ children: t("authorizationCodeHelp")
1416
+ }),
1417
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
848
1418
  style: {
849
- ...buttonStyle,
850
- display: "inline-flex",
1419
+ display: "flex",
851
1420
  alignItems: "center",
852
- textDecoration: "none"
1421
+ flexWrap: "wrap",
1422
+ gap: 8
853
1423
  },
854
- children: t("openAuthorization")
855
- })]
856
- })]
1424
+ children: [
1425
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
1426
+ "aria-label": t("authorizationCodeLabel"),
1427
+ style: codeStyle,
1428
+ children: challenge.userCode
1429
+ }),
1430
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1431
+ type: "button",
1432
+ style: buttonStyle,
1433
+ onClick: () => {
1434
+ copyAuthorizationCode();
1435
+ },
1436
+ children: copyStatus === "copied" ? t("authorizationCodeCopied") : t("copyAuthorizationCode")
1437
+ }),
1438
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
1439
+ href: challenge.verificationUri,
1440
+ target: "_blank",
1441
+ rel: "noreferrer",
1442
+ style: {
1443
+ ...buttonStyle,
1444
+ display: "inline-flex",
1445
+ alignItems: "center",
1446
+ textDecoration: "none"
1447
+ },
1448
+ children: t("openAuthorization")
1449
+ })
1450
+ ]
1451
+ }),
1452
+ copyStatus === "failed" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1453
+ style: errorStyle,
1454
+ children: t("authorizationCodeCopyFailed")
1455
+ }) : null
1456
+ ]
857
1457
  }) : null,
1458
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CodexGlobalModel, {
1459
+ connection,
1460
+ t
1461
+ }),
858
1462
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CodexCapabilities, {
859
1463
  scope: configScope,
860
1464
  t
@@ -1160,7 +1764,7 @@ window.__ModuleLoader__.load({
1160
1764
  };
1161
1765
  const markModelNameInputsReadonly = () => {
1162
1766
  const current = /* @__PURE__ */ new Set();
1163
- for (const input of document.querySelectorAll("input")) {
1767
+ for (const editor of document.querySelectorAll(`[${CODEX_EDITOR_ATTRIBUTE}="true"]`)) for (const input of editor.querySelectorAll("input")) {
1164
1768
  if (!startsWithAny(labelOf(input), MODEL_READONLY_LABELS)) continue;
1165
1769
  current.add(input);
1166
1770
  if (!originalModelNameAttributes.has(input)) originalModelNameAttributes.set(input, {
@@ -1423,8 +2027,8 @@ window.__ModuleLoader__.load({
1423
2027
  }
1424
2028
  };
1425
2029
  const updatePickers = () => {
1426
- markModelNameInputsReadonly();
1427
2030
  markEditors();
2031
+ markModelNameInputsReadonly();
1428
2032
  for (const [dialog] of originalPickerCopy) if (!document.contains(dialog)) originalPickerCopy.delete(dialog);
1429
2033
  const dialogs = [...document.querySelectorAll("[role=\"dialog\"]")];
1430
2034
  const activePicker = dialogs.find(isModelPicker);
@@ -1646,6 +2250,9 @@ window.__ModuleLoader__.load({
1646
2250
  remoteOrigin: "This browser origin is not trusted by the DSH Web server.",
1647
2251
  authorizationCodeHelp: "The plugin generated a one-time authorization code. Enter it on the opened Codex authorization page; no workspace selection is required.",
1648
2252
  authorizationCodeLabel: "Codex authorization code",
2253
+ copyAuthorizationCode: "Copy",
2254
+ authorizationCodeCopied: "Copied",
2255
+ authorizationCodeCopyFailed: "Unable to copy the authorization code. Please select and copy it manually.",
1649
2256
  openAuthorization: "Open authorization page",
1650
2257
  usageTitle: "General usage limits",
1651
2258
  refreshUsage: "Refresh",
@@ -1671,7 +2278,7 @@ window.__ModuleLoader__.load({
1671
2278
  enableImageUpload: "Enable image upload",
1672
2279
  enableImageUploadHelp: "Allow image-capable Codex models to receive images pasted or dropped into the conversation.",
1673
2280
  enableImageGeneration: "Enable image generation",
1674
- imageGenerationUnavailableHelp: "Not supported by the Codex provider and DSH rc.7 model adapter.",
2281
+ imageGenerationUnavailableHelp: "Not supported by the Codex provider and DSH rc.8 model adapter.",
1675
2282
  settingsLoading: "Loading plugin settings…",
1676
2283
  settingsUnavailable: "Plugin settings are unavailable in this dsh profile.",
1677
2284
  settingsReadOnly: "This profile exposes plugin settings as read-only.",
@@ -1679,7 +2286,16 @@ window.__ModuleLoader__.load({
1679
2286
  settingsSaveFailed: "Unable to save settings.",
1680
2287
  discard: "Discard",
1681
2288
  save: "Save",
1682
- saving: "Saving…"
2289
+ saving: "Saving…",
2290
+ globalModelTitle: "Global model",
2291
+ globalModelIntro: "Choose the default Codex model for new DSH sessions.",
2292
+ globalModelModel: "Model",
2293
+ globalModelThinking: "Reasoning effort",
2294
+ globalModelProviderDefault: "Provider default",
2295
+ globalModelUnset: "Select a model",
2296
+ globalModelLoadFailed: "Unable to load the Codex model catalog.",
2297
+ globalModelUnavailable: "This model is no longer in the current catalog.",
2298
+ setGlobalModel: "Set as global model"
1683
2299
  };
1684
2300
  const zh = {
1685
2301
  title: "Codex Auth",
@@ -1698,6 +2314,9 @@ window.__ModuleLoader__.load({
1698
2314
  remoteOrigin: "当前浏览器来源未被 DSH Web 服务信任。",
1699
2315
  authorizationCodeHelp: "插件已生成一次性授权码。请在打开的 Codex 授权页面中输入此代码,无需选择工作空间。",
1700
2316
  authorizationCodeLabel: "Codex 授权码",
2317
+ copyAuthorizationCode: "复制",
2318
+ authorizationCodeCopied: "已复制",
2319
+ authorizationCodeCopyFailed: "授权码复制失败,请手动选中并复制。",
1701
2320
  openAuthorization: "打开授权页面",
1702
2321
  usageTitle: "通用使用限额",
1703
2322
  refreshUsage: "刷新",
@@ -1723,7 +2342,7 @@ window.__ModuleLoader__.load({
1723
2342
  enableImageUpload: "启用图片上传",
1724
2343
  enableImageUploadHelp: "允许具备图片输入能力的 Codex 模型接收粘贴或拖入对话的图片。",
1725
2344
  enableImageGeneration: "启用图像生成",
1726
- imageGenerationUnavailableHelp: "当前 Codex 提供方和 DSH rc.7 模型适配器暂不支持图像输出。",
2345
+ imageGenerationUnavailableHelp: "当前 Codex 提供方和 DSH rc.8 模型适配器暂不支持图像输出。",
1727
2346
  settingsLoading: "正在加载插件设置…",
1728
2347
  settingsUnavailable: "此 dsh profile 无法使用插件设置。",
1729
2348
  settingsReadOnly: "此 profile 的插件设置为只读。",
@@ -1731,7 +2350,16 @@ window.__ModuleLoader__.load({
1731
2350
  settingsSaveFailed: "设置保存失败。",
1732
2351
  discard: "放弃",
1733
2352
  save: "保存",
1734
- saving: "保存中…"
2353
+ saving: "保存中…",
2354
+ globalModelTitle: "全局模型",
2355
+ globalModelIntro: "设置新 DSH 会话使用的 Codex 默认模型。",
2356
+ globalModelModel: "模型",
2357
+ globalModelThinking: "思考强度",
2358
+ globalModelProviderDefault: "提供方默认",
2359
+ globalModelUnset: "请选择模型",
2360
+ globalModelLoadFailed: "加载 Codex 模型目录失败。",
2361
+ globalModelUnavailable: "当前模型已不在可用目录中。",
2362
+ setGlobalModel: "设置为全局模型"
1735
2363
  };
1736
2364
  //#endregion
1737
2365
  //#region src/client/index.tsx
@@ -1751,7 +2379,8 @@ window.__ModuleLoader__.load({
1751
2379
  en
1752
2380
  }), "dsh-codex-auth-plugin: locale");
1753
2381
  const t = ctx.locale.bind(namespace);
1754
- const remoteScope = ctx.get("connection").isLoopback ? void 0 : new CodexAuthRemoteSettingsScope();
2382
+ const connection = ctx.get("connection");
2383
+ const remoteScope = connection.isLoopback ? void 0 : new CodexAuthRemoteSettingsScope();
1755
2384
  const configScope = remoteScope ?? ctx.settingsScope.bind({
1756
2385
  namespace: "dsh-codex-auth",
1757
2386
  decode: decodeCodexAuthSettings
@@ -1767,7 +2396,8 @@ window.__ModuleLoader__.load({
1767
2396
  key: CODEX_AUTH_SETTINGS_NAMESPACE,
1768
2397
  inject: () => ({
1769
2398
  t,
1770
- configScope
2399
+ configScope,
2400
+ connection
1771
2401
  })
1772
2402
  }, CodexAuthCard));
1773
2403
  }
package/lib/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { CODEX_AUTH_LOGIN_PATH, CODEX_AUTH_LOGOUT_PATH, CODEX_AUTH_SETTINGS_NAMESPACE, CODEX_AUTH_STATUS_PATH } from "./auth-paths.js";
1
+ import { CODEX_AUTH_LOGIN_PATH, CODEX_AUTH_LOGOUT_PATH, CODEX_AUTH_SETTINGS_NAMESPACE, CODEX_AUTH_STATUS_PATH, CODEX_GLOBAL_MODEL_PATH } from "./auth-paths.js";
2
2
  import { a as CODEX_AUTH_FILENAME, c as codexAuthPath, i as logoutCodex, n as codexAuthStatus, o as CODEX_PROVIDER, r as loginCodex, s as CodexCredentialStore, t as CodexAuthStatus } from "./auth-BBBeI24-.js";
3
3
  import { CredentialProvider } from "@deepseek-ai/dsh-credentials";
4
4
  import "@deepseek-ai/dsh-settings";
@@ -124,4 +124,4 @@ declare const name = "dsh-codex-auth-plugin";
124
124
  declare const inject: string[];
125
125
  declare function apply(ctx: Context): void;
126
126
  //#endregion
127
- export { CODEX_API_KEY_ENV, CODEX_API_KEY_REF, CODEX_AUTH_FILENAME, CODEX_AUTH_LOGIN_PATH, CODEX_AUTH_LOGOUT_PATH, CODEX_AUTH_SETTINGS_NAMESPACE, CODEX_AUTH_STATUS_PATH, CODEX_PROVIDER, CODEX_STREAM_IDLE_TIMEOUT_MS, type CodexAuthStatus, CodexCredentialMirror, CodexCredentialStore, type CodexLoginChallenge, CodexUsageService, CodexWebAuth, type CodexWebAuthStatus, apply, codexAuthPath, codexAuthStatus, createCodexAdapter, inject, loginCodex, logoutCodex, name, normalizeCodexUsagePayload, registerCodexAuthRoutes, trustedRequest };
127
+ export { CODEX_API_KEY_ENV, CODEX_API_KEY_REF, CODEX_AUTH_FILENAME, CODEX_AUTH_LOGIN_PATH, CODEX_AUTH_LOGOUT_PATH, CODEX_AUTH_SETTINGS_NAMESPACE, CODEX_AUTH_STATUS_PATH, CODEX_GLOBAL_MODEL_PATH, CODEX_PROVIDER, CODEX_STREAM_IDLE_TIMEOUT_MS, type CodexAuthStatus, CodexCredentialMirror, CodexCredentialStore, type CodexLoginChallenge, CodexUsageService, CodexWebAuth, type CodexWebAuthStatus, apply, codexAuthPath, codexAuthStatus, createCodexAdapter, inject, loginCodex, logoutCodex, name, normalizeCodexUsagePayload, registerCodexAuthRoutes, trustedRequest };
package/lib/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { a as CODEX_PROVIDER, i as CODEX_AUTH_FILENAME, n as loginCodex, o as CodexCredentialStore, r as logoutCodex, s as codexAuthPath, t as codexAuthStatus } from "./auth-DQ2F4vc6.js";
2
- import { CODEX_AUTH_LOGIN_PATH, CODEX_AUTH_LOGOUT_PATH, CODEX_AUTH_SETTINGS_NAMESPACE, CODEX_AUTH_SETTINGS_PATH, CODEX_AUTH_STATUS_PATH, CODEX_USAGE_PATH } from "./auth-paths.js";
2
+ import { CODEX_AUTH_LOGIN_PATH, CODEX_AUTH_LOGOUT_PATH, CODEX_AUTH_SETTINGS_NAMESPACE, CODEX_AUTH_SETTINGS_PATH, CODEX_AUTH_STATUS_PATH, CODEX_GLOBAL_MODEL_PATH, CODEX_USAGE_PATH } from "./auth-paths.js";
3
3
  import { createModels } from "@earendil-works/pi-ai";
4
4
  import { openaiCodexProvider } from "@earendil-works/pi-ai/providers/openai-codex";
5
5
  import { basename } from "node:path";
@@ -158,6 +158,7 @@ const CODEX_AUTH_URL_TIMEOUT_MS = 3e4;
158
158
  const REMOTE_WEB_ORIGIN_NOT_TRUSTED = "remote-web-origin-not-trusted";
159
159
  const CODEX_USAGE_UNAVAILABLE = "codex-usage-unavailable";
160
160
  const CODEX_SETTINGS_INVALID = "codex-settings-invalid";
161
+ const CODEX_GLOBAL_MODEL_INVALID = "codex-global-model-invalid";
161
162
  const CODEX_SETTINGS_BODY_LIMIT = 8192;
162
163
  function signedInStatus(expiresAt) {
163
164
  return expiresAt === void 0 ? { status: "signed-in" } : {
@@ -393,6 +394,56 @@ function decodeSettingsWrite(value) {
393
394
  if (typeof record["enableImageTool"] !== "boolean" || typeof record["enableImageUpload"] !== "boolean") return;
394
395
  return decodeCodexAuthSettings(record);
395
396
  }
397
+ function codexGlobalModelOf(selection) {
398
+ if (selection.provider !== "openai-codex") return void 0;
399
+ return {
400
+ model: selection.model,
401
+ ...selection.reasoningEffort === void 0 ? {} : { reasoningEffort: selection.reasoningEffort }
402
+ };
403
+ }
404
+ function decodeGlobalModelWrite(value) {
405
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
406
+ const record = value;
407
+ if (typeof record.model !== "string" || record.model.trim().length === 0) return void 0;
408
+ if (record.reasoningEffort !== void 0 && (typeof record.reasoningEffort !== "string" || record.reasoningEffort.trim().length === 0)) return void 0;
409
+ return {
410
+ model: record.model.trim(),
411
+ ...typeof record.reasoningEffort === "string" ? { reasoningEffort: record.reasoningEffort } : {}
412
+ };
413
+ }
414
+ /** Register the plugin-owned bridge to DSH rc.8's shared Agent default model. */
415
+ function registerCodexGlobalModelRoute(ctx, defaultModel, llm) {
416
+ ctx.effect(() => {
417
+ const authorize = (req, res) => {
418
+ if (trustedRequest(req)) return true;
419
+ json(res, 403, { error: REMOTE_WEB_ORIGIN_NOT_TRUSTED });
420
+ return false;
421
+ };
422
+ return ctx.webServer.register({
423
+ kind: "exact",
424
+ path: CODEX_GLOBAL_MODEL_PATH,
425
+ handler: async (req, res) => {
426
+ if (req.method !== "GET" && req.method !== "PUT") return json(res, 405, { error: "method not allowed" });
427
+ if (!authorize(req, res)) return;
428
+ if (req.method === "GET") return json(res, 200, { globalModel: codexGlobalModelOf(defaultModel.currentSelection()) });
429
+ try {
430
+ const next = decodeGlobalModelWrite(await readJsonBody(req));
431
+ if (next === void 0) return json(res, 400, { error: CODEX_GLOBAL_MODEL_INVALID });
432
+ const resolved = await llm.resolveCallConfig({
433
+ provider: CODEX_PROVIDER,
434
+ model: next.model,
435
+ ...next.reasoningEffort === void 0 ? {} : { reasoningEffort: next.reasoningEffort }
436
+ });
437
+ if (resolved.provider !== "openai-codex") return json(res, 400, { error: CODEX_GLOBAL_MODEL_INVALID });
438
+ await defaultModel.saveSelection(resolved);
439
+ return json(res, 200, { globalModel: codexGlobalModelOf(defaultModel.currentSelection()) });
440
+ } catch (error) {
441
+ return json(res, 400, { error: safeMessage(error) });
442
+ }
443
+ }
444
+ });
445
+ }, "dsh-codex-auth-plugin: global Codex model route");
446
+ }
396
447
  /** Register the plugin-owned settings endpoint for browsers opened through a NAS authority. */
397
448
  function registerCodexSettingsRoute(ctx, settings) {
398
449
  ctx.effect(() => {
@@ -768,7 +819,9 @@ const name = "dsh-codex-auth-plugin";
768
819
  const inject = [
769
820
  "webServer",
770
821
  "settings",
771
- "credentials"
822
+ "credentials",
823
+ "agentDefaultModel",
824
+ "llm"
772
825
  ];
773
826
  function apply(ctx) {
774
827
  const settings = ctx.settings.register(CODEX_AUTH_SETTINGS_NS, CodexAuthSettingsSchema);
@@ -788,6 +841,7 @@ function apply(ctx) {
788
841
  }, "dsh-codex-auth: credential mirror");
789
842
  registerCodexAuthRoutes(ctx, store, mirror);
790
843
  registerCodexSettingsRoute(ctx, settings);
844
+ registerCodexGlobalModelRoute(ctx, ctx.agentDefaultModel, ctx.llm);
791
845
  let stopped = false;
792
846
  let imageFiber;
793
847
  let imageTail = Promise.resolve();
@@ -830,4 +884,4 @@ function apply(ctx) {
830
884
  scheduleImageTool();
831
885
  }
832
886
  //#endregion
833
- export { CODEX_API_KEY_ENV, CODEX_API_KEY_REF, CODEX_AUTH_FILENAME, CODEX_AUTH_LOGIN_PATH, CODEX_AUTH_LOGOUT_PATH, CODEX_AUTH_SETTINGS_NAMESPACE, CODEX_AUTH_STATUS_PATH, CODEX_PROVIDER, CODEX_STREAM_IDLE_TIMEOUT_MS, CodexCredentialMirror, CodexCredentialStore, CodexUsageService, CodexWebAuth, apply, codexAuthPath, codexAuthStatus, createCodexAdapter, inject, loginCodex, logoutCodex, name, normalizeCodexUsagePayload, registerCodexAuthRoutes, trustedRequest };
887
+ export { CODEX_API_KEY_ENV, CODEX_API_KEY_REF, CODEX_AUTH_FILENAME, CODEX_AUTH_LOGIN_PATH, CODEX_AUTH_LOGOUT_PATH, CODEX_AUTH_SETTINGS_NAMESPACE, CODEX_AUTH_STATUS_PATH, CODEX_GLOBAL_MODEL_PATH, CODEX_PROVIDER, CODEX_STREAM_IDLE_TIMEOUT_MS, CodexCredentialMirror, CodexCredentialStore, CodexUsageService, CodexWebAuth, apply, codexAuthPath, codexAuthStatus, createCodexAdapter, inject, loginCodex, logoutCodex, name, normalizeCodexUsagePayload, registerCodexAuthRoutes, trustedRequest };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@tnnevol/dsh-codex-auth",
3
3
  "displayName": "Codex Auth",
4
4
  "description": "Standalone ChatGPT OAuth account management for DeepSeek Harness.",
5
- "version": "0.1.0-rc.8",
5
+ "version": "0.1.0-rc.8.2",
6
6
  "author": "tnnevol",
7
7
  "repository": {
8
8
  "type": "git",
@@ -64,6 +64,7 @@
64
64
  "@deepseek-ai/dsh-client-runtime",
65
65
  "@deepseek-ai/dsh-client-ui-conversation",
66
66
  "@deepseek-ai/dsh-client-ui-model-selection",
67
+ "@deepseek-ai/dsh-client-ui-primitives",
67
68
  "@deepseek-ai/dsh-client-ui-settings",
68
69
  "@deepseek-ai/dsh-client-ui-settings-plugins",
69
70
  "@deepseek-ai/dsh-client-locale"
@@ -75,6 +76,7 @@
75
76
  "@earendil-works/pi-ai": "0.82.1",
76
77
  "@deepseek-ai/cordis": "^4.0.1-rc.1",
77
78
  "@deepseek-ai/dsh-attachment": "0.1.0-rc.8",
79
+ "@deepseek-ai/dsh-agent-default-model": "0.1.0-rc.8",
78
80
  "@deepseek-ai/dsh-atomic-write": "0.1.0-rc.8",
79
81
  "@deepseek-ai/dsh-home-paths": "0.1.0-rc.8",
80
82
  "@deepseek-ai/dsh-host-webserver": "0.1.0-rc.8",
@@ -85,6 +87,7 @@
85
87
  "@deepseek-ai/dsh-tools": "0.1.0-rc.8",
86
88
  "@deepseek-ai/dsh-client-locale": "0.1.0-rc.8",
87
89
  "@deepseek-ai/dsh-client-runtime": "0.1.0-rc.8",
90
+ "@deepseek-ai/dsh-client-ui-primitives": "0.1.0-rc.8",
88
91
  "@deepseek-ai/dsh-client-ui-settings": "0.1.0-rc.8",
89
92
  "@deepseek-ai/dsh-client-ui-settings-plugins": "0.1.0-rc.8",
90
93
  "@deepseek-ai/dsh-client-ui-slots": "0.1.0-rc.8",
@@ -99,6 +102,7 @@
99
102
  "@earendil-works/pi-ai": "0.82.1",
100
103
  "@deepseek-ai/cordis": "^4.0.1",
101
104
  "@deepseek-ai/dsh-api-remotes": "0.1.0-rc.8",
105
+ "@deepseek-ai/dsh-agent-default-model": "0.1.0-rc.8",
102
106
  "@deepseek-ai/dsh-attachment": "0.1.0-rc.8",
103
107
  "@deepseek-ai/dsh-atomic-write": "0.1.0-rc.8",
104
108
  "@deepseek-ai/dsh-client-connection": "0.1.0-rc.8",
@@ -106,6 +110,7 @@
106
110
  "@deepseek-ai/dsh-client-runtime": "0.1.0-rc.8",
107
111
  "@deepseek-ai/dsh-client-ui-conversation": "0.1.0-rc.8",
108
112
  "@deepseek-ai/dsh-client-ui-model-selection": "0.1.0-rc.8",
113
+ "@deepseek-ai/dsh-client-ui-primitives": "0.1.0-rc.8",
109
114
  "@deepseek-ai/dsh-client-ui-settings": "0.1.0-rc.8",
110
115
  "@deepseek-ai/dsh-client-ui-settings-plugins": "0.1.0-rc.8",
111
116
  "@deepseek-ai/dsh-client-ui-slots": "0.1.0-rc.8",
@@ -128,4 +133,4 @@
128
133
  "typescript": "^5.9.3",
129
134
  "vitest": "^4.1.8"
130
135
  }
131
- }
136
+ }