@khorsheed/dsh-local-agent-codex 0.2.0-rc.1 → 0.2.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
@@ -100,23 +100,28 @@ window.__ModuleLoader__.load({
100
100
  const NS = "local-agent-codex";
101
101
  /** Simplified Chinese dictionary (the key-set source of truth). */
102
102
  const zh = {
103
- "card.title": "Local Agent · Codex",
104
- "card.description": "Codex 委派:登录授权与常驻模式(live)",
103
+ "card.title": "本机 Codex 委派",
104
+ "card.description": "Codex 委派:端点凭据与常驻模式(live)",
105
105
  "card.expand": "展开",
106
106
  "card.collapse": "收起",
107
107
  "auth.title": "认证",
108
- "model.title": "默认模型",
109
- "model.info.aria": "默认模型说明",
110
- "model.info": "留空 = 跟随默认(输入框里显示的就是当前跟随的模型)。从列表选择或手动输入一个模型名后保存,之后的每轮委派都以它起 CLI(一次性轮次走 `codex exec -m <模型>`,常驻模式走 app-server 的 `-c model=…`)。成员会话里还可用作曲器按会话切换,不影响这里的默认。改动在下一轮委派生效,进行中的轮次不受影响;评测 run 的条件在建立时冻结,中途换模型会被下一轮的模型回读判为 misattributed 而拦下。",
111
- "model.placeholder": "留空 = 不传模型,用 CLI 自己的默认",
112
- "model.menu": "选择模型",
113
- "model.menuDefault": "默认(跟随 CLI 内置默认)",
114
- "model.menuDefault.cli-config": "默认(跟随 CLI 配置:{model})",
115
- "model.menuDefault.cli-builtin": "默认(跟随 CLI 默认:{model})",
116
- "model.menuDefault.last-observed": "默认(跟随 CLI 内置默认(最近:{model}))",
117
- "model.save": "保存",
118
- "model.applied": "已保存,下一轮委派生效;进行中的轮次不受影响",
119
- "model.error": "保存失败,请重试",
108
+ "endpoint.info.aria": "端点凭据说明",
109
+ "endpoint.info": "这里保存的就是默认 scope 的 endpoint-auth.json(0600,只存本机),它优先于 profile 的 YAML 配置行;宿主 shell 的环境变量从不被读取。配置 API Key 后每轮委派以自定义 provider(wire_api=responses)路由到端点;端点地址留空 = OpenAI 官方端点。密钥绝不写进 profile 文件,也不进入评测条件指纹。没有网页登录:容器轮与宿主轮用同一套凭据,容器里不需要再授权一次。保存对下一轮委派生效,进行中的轮次不受影响。",
110
+ "endpoint.baseUrl": "端点地址(baseUrl)",
111
+ "endpoint.baseUrl.placeholder": "留空 = OpenAI 官方端点",
112
+ "endpoint.apiKey": "API Key",
113
+ "endpoint.secret.configured": "已配置 · 输入新值更换,留空保存 = 清除",
114
+ "endpoint.secret.placeholder": "未配置",
115
+ "endpoint.configured": "已配置",
116
+ "endpoint.save": "保存凭据",
117
+ "endpoint.applied": "已保存,下一轮委派生效;进行中的轮次不受影响",
118
+ "endpoint.error": "保存失败,请重试",
119
+ "endpoint.check": "验证端点并获取模型",
120
+ "endpoint.checking": "验证中…",
121
+ "endpoint.checkDirtyHint": "有未保存的修改,先保存凭据再验证",
122
+ "endpoint.checkUnavailable": "当前核心不支持端点验证(请升级 local-agent 核心包)",
123
+ "endpoint.unavailable": "当前核心不支持凭据配置(请升级 local-agent 核心包)",
124
+ "endpoint.checkFailed": "验证失败,请重试",
120
125
  "live.title": "常驻模式(live)",
121
126
  "live.info.aria": "常驻模式说明",
122
127
  "live.info": "常驻模式 = 成员进程常驻:输出实时流入成员会话、取消不杀进程、崩溃后自动续上原会话;关闭则每轮独立进程,跑完一次性出结果。设置为全局偏好,对所有后续委派生效。",
@@ -126,23 +131,28 @@ window.__ModuleLoader__.load({
126
131
  };
127
132
  /** English dictionary, checked complete against the zh key set. */
128
133
  const en = {
129
- "card.title": "Local Agent · Codex",
130
- "card.description": "Codex delegation: sign-in and resident (live) mode",
134
+ "card.title": "Local Codex Delegation",
135
+ "card.description": "Codex delegation: endpoint credential and resident (live) mode",
131
136
  "card.expand": "Expand",
132
137
  "card.collapse": "Collapse",
133
138
  "auth.title": "Authentication",
134
- "model.title": "Default model",
135
- "model.info.aria": "About the default model",
136
- "model.info": "Blank = follow the default (the input shows the model it currently follows). Pick from the list or type a model name by hand and save, and every later delegation round starts the CLI with it (`codex exec -m <model>` for one-shot rounds, `-c model=…` on the resident app-server). Inside a member session the composer can also switch the model per session, without touching this default. A change applies to the next round and never disturbs a round in flight; an evaluation run freezes its condition at setup, so switching mid-run is caught by the model read-back on the next round and fails the run as misattributed.",
137
- "model.placeholder": "Blank = pass no model, keep the CLI's own default",
138
- "model.menu": "Pick a model",
139
- "model.menuDefault": "Default (follow the CLI's built-in default)",
140
- "model.menuDefault.cli-config": "Default (follow the CLI config: {model})",
141
- "model.menuDefault.cli-builtin": "Default (follow the CLI default: {model})",
142
- "model.menuDefault.last-observed": "Default (follow the CLI's built-in default (last: {model}))",
143
- "model.save": "Save",
144
- "model.applied": "Saved — takes effect on the next delegation round; rounds in flight are unaffected",
145
- "model.error": "Save failed, please retry",
139
+ "endpoint.info.aria": "About the endpoint credential",
140
+ "endpoint.info": "What you save here is the default scope's endpoint-auth.json (mode 0600, local only), which outranks the profile's YAML row; the host shell's environment is never read. With an API Key configured, every delegation round routes at the endpoint through a custom provider (wire_api=responses); a blank endpoint means OpenAI's own. Keys never enter the profile document or an evaluation's condition fingerprint. There is no web login: container rounds share the host's credential and nothing is authorized inside a container. Saving applies to the next delegation round; rounds in flight are unaffected.",
141
+ "endpoint.baseUrl": "Endpoint (baseUrl)",
142
+ "endpoint.baseUrl.placeholder": "Blank = OpenAI's own endpoint",
143
+ "endpoint.apiKey": "API Key",
144
+ "endpoint.secret.configured": "Configured · type to replace, save blank to clear",
145
+ "endpoint.secret.placeholder": "Not configured",
146
+ "endpoint.configured": "Configured",
147
+ "endpoint.save": "Save credential",
148
+ "endpoint.applied": "Saved — takes effect on the next delegation round; rounds in flight are unaffected",
149
+ "endpoint.error": "Save failed, please retry",
150
+ "endpoint.check": "Verify endpoint & list models",
151
+ "endpoint.checking": "Verifying…",
152
+ "endpoint.checkDirtyHint": "Unsaved changes — save the credential before verifying",
153
+ "endpoint.checkUnavailable": "This core cannot verify endpoints (upgrade the local-agent core package)",
154
+ "endpoint.unavailable": "This core cannot configure credentials (upgrade the local-agent core package)",
155
+ "endpoint.checkFailed": "Verification failed, please retry",
146
156
  "live.title": "Resident mode (live)",
147
157
  "live.info.aria": "About resident mode",
148
158
  "live.info": "Resident mode keeps the member process alive: output streams into the member session in real time, cancelling does not kill the process, and a crash resumes the same session. Off means every round runs as an independent one-shot process. The setting is global and applies to every later delegation.",
@@ -207,6 +217,14 @@ window.__ModuleLoader__.load({
207
217
  };
208
218
  }
209
219
  /**
220
+ * Subscribe one harness's published status (no probe of its own): a sibling
221
+ * surface that WRITES the credential (the endpoint form's save) publishes
222
+ * through the same bus, and every block showing the status flips at once.
223
+ */
224
+ function usePublishedAuthStatus(harnessId) {
225
+ return (0, react.useSyncExternalStore)(subscribe, () => statuses.get(harnessId));
226
+ }
227
+ /**
210
228
  * Read one harness's latest known auth status. Every mount probes once and
211
229
  * publishes — a published value only snapshots the past (a credential expiring
212
230
  * mid-session must flip the dot at the next view, not stay green forever);
@@ -233,40 +251,40 @@ window.__ModuleLoader__.load({
233
251
  }
234
252
  //#endregion
235
253
  //#region \0dsh-css:../local-agent/src/client/ProviderAuthBlock.module.css.mjs
236
- const css$2 = ".On5p7W_section{max-width:720px;color:var(--dsw-alias-label-primary);flex-direction:column;gap:4px;display:flex}.On5p7W_title{color:var(--dsw-alias-label-primary);margin:0;font-size:16px;font-weight:500;line-height:24px}.On5p7W_intro{color:var(--dsw-alias-label-tertiary);margin:0;font-size:14px;line-height:22px}.On5p7W_rows{flex-direction:column;gap:8px;margin:12px 0 0;padding:0;list-style:none;display:flex}.On5p7W_rowCard{border:1px solid var(--dsw-alias-border-l2);border-radius:12px;flex-direction:column;gap:10px;padding:12px 14px;display:flex}.On5p7W_rowPending{opacity:.6}.On5p7W_rowHead{align-items:center;gap:10px;display:flex}.On5p7W_rowIdentity{align-items:center;gap:6px;min-width:0;display:inline-flex}.On5p7W_rowName{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}.On5p7W_rowTag{border:1px solid var(--dsw-alias-border-l3);color:var(--dsw-alias-label-secondary);border-radius:4px;flex:none;padding:1px 6px;font-size:11px;line-height:16px}.On5p7W_credentialDot{box-sizing:border-box;border-radius:50%;flex:none;width:8px;height:8px;display:inline-block}.On5p7W_dotOn{background:var(--dsw-alias-state-success-primary)}.On5p7W_dotOff{background:var(--dsw-alias-state-error-primary)}.On5p7W_dotPending{background:var(--dsw-alias-fill-l3)}.On5p7W_status{color:var(--dsw-alias-label-tertiary);flex:none;margin-left:auto;font-size:12px;line-height:18px}.On5p7W_rowActions{align-items:center;gap:4px;display:inline-flex}.On5p7W_loginButton{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);height:28px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border-radius:14px;justify-content:center;align-items:center;padding:0 12px;font-size:12px;line-height:18px;display:inline-flex}.On5p7W_loginButton:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l3)}.On5p7W_loginButton:disabled{color:var(--dsw-alias-label-tertiary);border-color:var(--dsw-alias-border-l2);cursor:not-allowed}.On5p7W_logoutButton{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);height:28px;color:var(--dsw-alias-state-error-primary);cursor:pointer;background:0 0;border-radius:14px;justify-content:center;align-items:center;padding:0 12px;font-size:12px;line-height:18px;display:inline-flex}.On5p7W_logoutButton:hover{border-color:var(--dsw-alias-state-error-primary)}.On5p7W_rosterError{color:var(--dsw-alias-state-error-primary);align-items:center;gap:10px;margin:8px 0 0;font-size:12px;line-height:18px;display:flex}.On5p7W_retryButton{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);height:24px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border-radius:12px;justify-content:center;align-items:center;padding:0 10px;font-size:12px;line-height:18px;display:inline-flex}.On5p7W_retryButton:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l3)}.On5p7W_loginPrompt{align-items:flex-start;gap:10px;display:flex}.On5p7W_promptText{min-width:0;color:var(--dsw-alias-label-tertiary);overflow-wrap:anywhere;flex:1;font-size:12px;line-height:18px}.On5p7W_codeInput{border:1px solid var(--dsw-alias-line-normal,currentColor);min-width:0;color:inherit;background:0 0;border-radius:6px;flex:1;padding:4px 8px;font-size:12px;line-height:18px}.On5p7W_openPage{color:var(--dsw-alias-accent-link);flex:none;font-size:12px;line-height:18px;text-decoration:none}.On5p7W_openPage:hover{text-decoration:underline}";
237
- const tagId$2 = "@khorsheed/dsh-local-agent-codex/ProviderAuthBlock.module.css";
238
- if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
254
+ const css$3 = ".On5p7W_section{max-width:720px;color:var(--dsw-alias-label-primary);flex-direction:column;gap:4px;display:flex}.On5p7W_title{color:var(--dsw-alias-label-primary);margin:0;font-size:16px;font-weight:500;line-height:24px}.On5p7W_intro{color:var(--dsw-alias-label-tertiary);margin:0;font-size:14px;line-height:22px}.On5p7W_rows{flex-direction:column;gap:8px;margin:12px 0 0;padding:0;list-style:none;display:flex}.On5p7W_rowCard{border:1px solid var(--dsw-alias-border-l2);border-radius:12px;flex-direction:column;gap:10px;padding:12px 14px;display:flex}.On5p7W_rowPending{opacity:.6}.On5p7W_rowHead{align-items:center;gap:10px;display:flex}.On5p7W_rowIdentity{align-items:center;gap:6px;min-width:0;display:inline-flex}.On5p7W_rowName{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}.On5p7W_rowTag{border:1px solid var(--dsw-alias-border-l3);color:var(--dsw-alias-label-secondary);border-radius:4px;flex:none;padding:1px 6px;font-size:11px;line-height:16px}.On5p7W_credentialDot{box-sizing:border-box;border-radius:50%;flex:none;width:8px;height:8px;display:inline-block}.On5p7W_dotOn{background:var(--dsw-alias-state-success-primary)}.On5p7W_dotOff{background:var(--dsw-alias-state-error-primary)}.On5p7W_dotPending{background:var(--dsw-alias-fill-l3)}.On5p7W_status{color:var(--dsw-alias-label-tertiary);flex:none;margin-left:auto;font-size:12px;line-height:18px}.On5p7W_rowActions{align-items:center;gap:4px;display:inline-flex}.On5p7W_loginButton{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);height:28px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border-radius:14px;justify-content:center;align-items:center;padding:0 12px;font-size:12px;line-height:18px;display:inline-flex}.On5p7W_loginButton:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l3)}.On5p7W_loginButton:disabled{color:var(--dsw-alias-label-tertiary);border-color:var(--dsw-alias-border-l2);cursor:not-allowed}.On5p7W_logoutButton{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);height:28px;color:var(--dsw-alias-state-error-primary);cursor:pointer;background:0 0;border-radius:14px;justify-content:center;align-items:center;padding:0 12px;font-size:12px;line-height:18px;display:inline-flex}.On5p7W_logoutButton:hover{border-color:var(--dsw-alias-state-error-primary)}.On5p7W_rosterError{color:var(--dsw-alias-state-error-primary);align-items:center;gap:10px;margin:8px 0 0;font-size:12px;line-height:18px;display:flex}.On5p7W_retryButton{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);height:24px;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border-radius:12px;justify-content:center;align-items:center;padding:0 10px;font-size:12px;line-height:18px;display:inline-flex}.On5p7W_retryButton:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l3)}.On5p7W_loginPrompt{align-items:flex-start;gap:10px;display:flex}.On5p7W_promptText{min-width:0;color:var(--dsw-alias-label-tertiary);overflow-wrap:anywhere;flex:1;font-size:12px;line-height:18px}.On5p7W_codeInput{border:1px solid var(--dsw-alias-line-normal,currentColor);min-width:0;color:inherit;background:0 0;border-radius:6px;flex:1;padding:4px 8px;font-size:12px;line-height:18px}.On5p7W_openPage{color:var(--dsw-alias-accent-link);flex:none;font-size:12px;line-height:18px;text-decoration:none}.On5p7W_openPage:hover{text-decoration:underline}";
255
+ const tagId$3 = "@khorsheed/dsh-local-agent-codex/ProviderAuthBlock.module.css";
256
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
239
257
  const tag = document.createElement("style");
240
258
  tag.dataset.plugin = "@khorsheed/dsh-local-agent-codex";
241
- tag.dataset.pluginCss = tagId$2;
242
- tag.textContent = css$2;
259
+ tag.dataset.pluginCss = tagId$3;
260
+ tag.textContent = css$3;
243
261
  document.head.appendChild(tag);
244
262
  }
245
263
  var ProviderAuthBlock_module_css_default = {
246
- "rowHead": "On5p7W_rowHead",
247
- "rowTag": "On5p7W_rowTag",
248
- "credentialDot": "On5p7W_credentialDot",
264
+ "status": "On5p7W_status",
265
+ "rowName": "On5p7W_rowName",
266
+ "rosterError": "On5p7W_rosterError",
267
+ "section": "On5p7W_section",
249
268
  "retryButton": "On5p7W_retryButton",
250
- "rows": "On5p7W_rows",
251
269
  "rowIdentity": "On5p7W_rowIdentity",
270
+ "rows": "On5p7W_rows",
271
+ "loginButton": "On5p7W_loginButton",
272
+ "rowCard": "On5p7W_rowCard",
273
+ "intro": "On5p7W_intro",
274
+ "rowTag": "On5p7W_rowTag",
252
275
  "loginPrompt": "On5p7W_loginPrompt",
253
276
  "openPage": "On5p7W_openPage",
254
- "intro": "On5p7W_intro",
255
277
  "title": "On5p7W_title",
256
- "rosterError": "On5p7W_rosterError",
257
- "rowCard": "On5p7W_rowCard",
258
- "loginButton": "On5p7W_loginButton",
278
+ "rowHead": "On5p7W_rowHead",
279
+ "dotOn": "On5p7W_dotOn",
280
+ "credentialDot": "On5p7W_credentialDot",
259
281
  "dotOff": "On5p7W_dotOff",
260
- "promptText": "On5p7W_promptText",
261
282
  "codeInput": "On5p7W_codeInput",
262
283
  "dotPending": "On5p7W_dotPending",
263
- "logoutButton": "On5p7W_logoutButton",
264
- "section": "On5p7W_section",
265
- "status": "On5p7W_status",
266
- "dotOn": "On5p7W_dotOn",
267
284
  "rowPending": "On5p7W_rowPending",
268
285
  "rowActions": "On5p7W_rowActions",
269
- "rowName": "On5p7W_rowName"
286
+ "promptText": "On5p7W_promptText",
287
+ "logoutButton": "On5p7W_logoutButton"
270
288
  };
271
289
  //#endregion
272
290
  //#region ../local-agent/src/client/ProviderAuthBlock.tsx
@@ -300,12 +318,13 @@ window.__ModuleLoader__.load({
300
318
  * for the retainedBy.mainView count on each summary (the label is ui-session's
301
319
  * declaration merge, read here as a plain duck-typed count so this package
302
320
  * needs no ui-session edge), with the legacy field as the older-host fallback.
321
+ * Exported for sibling cards that run slash commands from their own rows.
303
322
  */
304
323
  const mainSessionId = (list) => Object.values(list.byId ?? {}).find((session) => (session.retainedBy?.mainView ?? 0) > 0)?.id ?? list.current;
305
324
  /**
306
325
  * One harness's auth interactions, shared between the per-provider settings
307
- * cards (the alpha.2 `plugins.bundle.config` pages and the 0.1.5
308
- * `settings.plugin.item` tab): auth status through
326
+ * cards (each a `plugins.item` entry on the Plugins page, registered while
327
+ * the Host serves the provider's namespace): auth status through
309
328
  * the read-only Remote channel and a web-login action that runs
310
329
  * `/<harness> login`, surfaces the device-code prompt, and offers a link to
311
330
  * the authorization page. The login command returns at prompt time while the
@@ -338,7 +357,8 @@ window.__ModuleLoader__.load({
338
357
  const capabilities = probe === void 0 ? {} : {
339
358
  loginable: probe.loginable ?? true,
340
359
  logoutable: probe.logoutable ?? true,
341
- ...probe.loginAwaitingCode === true ? { loginAwaitingCode: true } : {}
360
+ ...probe.loginAwaitingCode === true ? { loginAwaitingCode: true } : {},
361
+ ...probe.effectiveSettings?.cliVersion === void 0 ? {} : { cliVersion: probe.effectiveSettings.cliVersion }
342
362
  };
343
363
  setView((prev) => statusKind === "authenticated" ? {
344
364
  status: statusKind,
@@ -414,7 +434,7 @@ window.__ModuleLoader__.load({
414
434
  setView({ status: "unavailable" });
415
435
  });
416
436
  };
417
- const statusKind = view.status;
437
+ const statusKind = usePublishedAuthStatus(harness.id) ?? view.status;
418
438
  const authenticated = statusKind === "authenticated";
419
439
  const loginPending = pendingSince !== void 0;
420
440
  const loginable = view.loginable ?? true;
@@ -438,6 +458,10 @@ window.__ModuleLoader__.load({
438
458
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
439
459
  className: ProviderAuthBlock_module_css_default.rowTag,
440
460
  children: harness.id
461
+ }),
462
+ view.cliVersion !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
463
+ className: ProviderAuthBlock_module_css_default.rowTag,
464
+ children: view.cliVersion
441
465
  })
442
466
  ]
443
467
  }),
@@ -529,20 +553,20 @@ window.__ModuleLoader__.load({
529
553
  }
530
554
  //#endregion
531
555
  //#region \0dsh-css:../local-agent/src/client/AuthStatusDot.module.css.mjs
532
- const css$1 = ".OFjNUG_credentialDot{box-sizing:border-box;border-radius:50%;flex:none;width:8px;height:8px;display:inline-block}.OFjNUG_dotOn{background:var(--dsw-alias-state-success-primary)}.OFjNUG_dotOff{background:var(--dsw-alias-state-error-primary)}.OFjNUG_dotPending{background:var(--dsw-alias-fill-l3)}";
533
- const tagId$1 = "@khorsheed/dsh-local-agent-codex/AuthStatusDot.module.css";
534
- if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
556
+ const css$2 = ".OFjNUG_credentialDot{box-sizing:border-box;border-radius:50%;flex:none;width:8px;height:8px;display:inline-block}.OFjNUG_dotOn{background:var(--dsw-alias-state-success-primary)}.OFjNUG_dotOff{background:var(--dsw-alias-state-error-primary)}.OFjNUG_dotPending{background:var(--dsw-alias-fill-l3)}";
557
+ const tagId$2 = "@khorsheed/dsh-local-agent-codex/AuthStatusDot.module.css";
558
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
535
559
  const tag = document.createElement("style");
536
560
  tag.dataset.plugin = "@khorsheed/dsh-local-agent-codex";
537
- tag.dataset.pluginCss = tagId$1;
538
- tag.textContent = css$1;
561
+ tag.dataset.pluginCss = tagId$2;
562
+ tag.textContent = css$2;
539
563
  document.head.appendChild(tag);
540
564
  }
541
565
  var AuthStatusDot_module_css_default = {
566
+ "credentialDot": "OFjNUG_credentialDot",
542
567
  "dotPending": "OFjNUG_dotPending",
543
- "dotOn": "OFjNUG_dotOn",
544
568
  "dotOff": "OFjNUG_dotOff",
545
- "credentialDot": "OFjNUG_credentialDot"
569
+ "dotOn": "OFjNUG_dotOn"
546
570
  };
547
571
  //#endregion
548
572
  //#region ../local-agent/src/client/AuthStatusDot.tsx
@@ -557,8 +581,412 @@ window.__ModuleLoader__.load({
557
581
  });
558
582
  }
559
583
  //#endregion
584
+ //#region \0dsh-css:../local-agent/src/client/ModelListSection.module.css.mjs
585
+ const css$1 = "._4eeJ7W_block{flex-direction:column;gap:10px;display:flex}._4eeJ7W_blockTitle{color:var(--dsw-alias-label-primary);align-items:center;gap:6px;margin:0;font-size:13px;font-weight:500;line-height:1.4;display:flex}._4eeJ7W_info{appearance:none;color:var(--dsw-alias-label-tertiary);cursor:help;background:0 0;border:0;border-radius:50%;padding:0 2px;font-size:12px;line-height:1}._4eeJ7W_row{align-items:center;gap:12px;display:flex}._4eeJ7W_saved{color:var(--dsw-alias-state-success-primary);font-size:12px}._4eeJ7W_errorText{color:var(--dsw-alias-state-error-primary);font-size:12px}._4eeJ7W_hint{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:1.5}._4eeJ7W_modelInput{min-width:0;font:inherit;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);border-radius:8px;flex:1;padding:5px 8px;font-size:13px;line-height:1.4}._4eeJ7W_modelSave{appearance:none;font:inherit;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:8px;flex:none;padding:5px 10px;font-size:13px;line-height:1.4}._4eeJ7W_modelList{flex-direction:column;gap:8px;display:flex}._4eeJ7W_modelRow{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);cursor:grab;border-radius:8px;align-items:center;gap:8px;padding:6px 8px;display:flex}._4eeJ7W_modelRowDragging{opacity:.6;cursor:grabbing}._4eeJ7W_modelDrag{color:var(--dsw-alias-label-tertiary);cursor:grab;user-select:none}._4eeJ7W_modelRowValue{font-size:13px;font-family:var(--dsw-alias-font-mono,monospace);color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;flex:1;overflow:hidden}._4eeJ7W_modelRowLabel{color:var(--dsw-alias-label-tertiary);text-overflow:ellipsis;white-space:nowrap;max-width:40%;font-size:12px;overflow:hidden}._4eeJ7W_modelDefaultChip{color:var(--dsw-alias-success,#1a9e54);background:color-mix(in srgb, var(--dsw-alias-success,#1a9e54) 12%, transparent);border-radius:999px;padding:1px 8px;font-size:11px;line-height:1.4}._4eeJ7W_modelDelete{appearance:none;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:0;border-radius:4px;padding:2px 4px;font-size:12px}._4eeJ7W_modelAdd{appearance:none;border:1px dashed var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border-radius:8px;padding:5px 12px;font-size:13px}._4eeJ7W_dialogBackdrop{z-index:100;background:#00000052;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}._4eeJ7W_dialog{background:var(--dsw-alias-bg-layer-3);border:1px solid var(--dsw-alias-border-l2);border-radius:12px;flex-direction:column;gap:10px;width:min(520px,100vw - 48px);max-height:min(560px,100vh - 96px);padding:16px;display:flex;box-shadow:0 12px 40px #00000059}._4eeJ7W_dialogHead{justify-content:space-between;align-items:center;display:flex}._4eeJ7W_dialogTitle{color:var(--dsw-alias-label-primary);margin:0;font-size:14px;font-weight:600}._4eeJ7W_dialogClose{appearance:none;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:0;border-radius:4px;padding:2px 4px;font-size:13px}._4eeJ7W_dialogHint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px}._4eeJ7W_dialogToolbar{align-items:center;gap:8px;display:flex}._4eeJ7W_dialogToolbar ._4eeJ7W_modelInput{flex:1}._4eeJ7W_dialogSelectAll{appearance:none;color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border:0;padding:4px 6px;font-size:13px}._4eeJ7W_dialogList{border:1px solid var(--dsw-alias-border-l2);border-radius:8px;flex-direction:column;flex:1;gap:2px;min-height:120px;max-height:320px;padding:6px;display:flex;overflow-y:auto}._4eeJ7W_dialogItem{color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:6px;align-items:center;gap:8px;padding:6px 8px;font-size:13px;display:flex}._4eeJ7W_dialogItemAdded{opacity:.55;cursor:default}._4eeJ7W_dialogItemValue{font-family:var(--dsw-alias-font-mono,monospace)}._4eeJ7W_dialogItemLabel{color:var(--dsw-alias-label-tertiary);text-overflow:ellipsis;white-space:nowrap;font-size:12px;overflow:hidden}._4eeJ7W_dialogActions{justify-content:flex-end;gap:8px;display:flex}._4eeJ7W_dialogConfirm{appearance:none;color:var(--dsw-alias-text-on-brand,#fff);background:var(--dsw-alias-brand-primary);cursor:pointer;border:0;border-radius:8px;padding:6px 14px;font-size:13px}";
586
+ const tagId$1 = "@khorsheed/dsh-local-agent-codex/ModelListSection.module.css";
587
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
588
+ const tag = document.createElement("style");
589
+ tag.dataset.plugin = "@khorsheed/dsh-local-agent-codex";
590
+ tag.dataset.pluginCss = tagId$1;
591
+ tag.textContent = css$1;
592
+ document.head.appendChild(tag);
593
+ }
594
+ var ModelListSection_module_css_default = {
595
+ "modelRow": "_4eeJ7W_modelRow",
596
+ "blockTitle": "_4eeJ7W_blockTitle",
597
+ "block": "_4eeJ7W_block",
598
+ "modelRowDragging": "_4eeJ7W_modelRowDragging",
599
+ "dialogSelectAll": "_4eeJ7W_dialogSelectAll",
600
+ "dialogTitle": "_4eeJ7W_dialogTitle",
601
+ "dialog": "_4eeJ7W_dialog",
602
+ "dialogList": "_4eeJ7W_dialogList",
603
+ "row": "_4eeJ7W_row",
604
+ "modelSave": "_4eeJ7W_modelSave",
605
+ "errorText": "_4eeJ7W_errorText",
606
+ "info": "_4eeJ7W_info",
607
+ "modelDelete": "_4eeJ7W_modelDelete",
608
+ "dialogHead": "_4eeJ7W_dialogHead",
609
+ "dialogConfirm": "_4eeJ7W_dialogConfirm",
610
+ "modelDrag": "_4eeJ7W_modelDrag",
611
+ "dialogItemValue": "_4eeJ7W_dialogItemValue",
612
+ "dialogBackdrop": "_4eeJ7W_dialogBackdrop",
613
+ "dialogActions": "_4eeJ7W_dialogActions",
614
+ "dialogItemLabel": "_4eeJ7W_dialogItemLabel",
615
+ "modelList": "_4eeJ7W_modelList",
616
+ "modelDefaultChip": "_4eeJ7W_modelDefaultChip",
617
+ "dialogToolbar": "_4eeJ7W_dialogToolbar",
618
+ "hint": "_4eeJ7W_hint",
619
+ "modelAdd": "_4eeJ7W_modelAdd",
620
+ "dialogClose": "_4eeJ7W_dialogClose",
621
+ "modelRowLabel": "_4eeJ7W_modelRowLabel",
622
+ "dialogItemAdded": "_4eeJ7W_dialogItemAdded",
623
+ "modelRowValue": "_4eeJ7W_modelRowValue",
624
+ "modelInput": "_4eeJ7W_modelInput",
625
+ "saved": "_4eeJ7W_saved",
626
+ "dialogItem": "_4eeJ7W_dialogItem",
627
+ "dialogHint": "_4eeJ7W_dialogHint"
628
+ };
629
+ //#endregion
630
+ //#region ../local-agent/src/client/ModelListSection.tsx
631
+ /**
632
+ * The family's shared「可用模型」section: an ORDERED shortlist of
633
+ * endpoint-served models — the first entry is the default model every
634
+ * delegation round starts with. Rows drag to reorder and delete per row;
635
+ * "add model" opens a dialog over the endpoint's served catalog (search +
636
+ * checkboxes), so the list can only ever contain what the endpoint actually
637
+ * serves. An empty list falls back to the pre-list chain (the settings
638
+ * model key, the scoped config file, then the CLI's own default). Every
639
+ * edit persists immediately through the harness's model-list face (the
640
+ * gateway's `modelList`/`configureModelList` Remotes).
641
+ *
642
+ * One implementation serves every companion card (claude-code, codex,
643
+ * kimi): the section owns its state (list read on activate, immediate
644
+ * writes, the catalog read behind the add dialog) so each card's
645
+ * integration is the two faces plus copy — the interaction cannot drift
646
+ * between harnesses. Copy lives in the family core's own dictionary.
647
+ * @module @khorsheed/dsh-local-agent/client — model list section
648
+ */
649
+ /** The section's staged state: the list and the catalog behind the add dialog. */
650
+ function useModelListState(active, modelList, modelDirectory, directoryRefresh) {
651
+ const unavailable = modelList === void 0;
652
+ const [models, setModels] = (0, react.useState)([]);
653
+ const [saved, setSaved] = (0, react.useState)(false);
654
+ const [error, setError] = (0, react.useState)(false);
655
+ (0, react.useEffect)(() => {
656
+ if (!active || modelList === void 0) return;
657
+ let stale = false;
658
+ modelList.read().then((list) => {
659
+ if (!stale) setModels(list ?? []);
660
+ }, () => {
661
+ if (!stale) setModels([]);
662
+ });
663
+ return () => {
664
+ stale = true;
665
+ };
666
+ }, [active, modelList]);
667
+ const [directory, setDirectory] = (0, react.useState)(null);
668
+ (0, react.useEffect)(() => {
669
+ if (!active || modelDirectory === void 0) return;
670
+ let stale = false;
671
+ modelDirectory(true).then((result) => {
672
+ if (!stale) setDirectory(result ?? null);
673
+ }, () => {
674
+ if (!stale) setDirectory(null);
675
+ });
676
+ return () => {
677
+ stale = true;
678
+ };
679
+ }, [
680
+ active,
681
+ modelDirectory,
682
+ directoryRefresh
683
+ ]);
684
+ const saveModels = (next) => {
685
+ if (modelList === void 0) return;
686
+ setSaved(false);
687
+ setError(false);
688
+ modelList.write(next).then((written) => {
689
+ setModels(written ?? next);
690
+ setSaved(true);
691
+ }, () => {
692
+ setError(true);
693
+ });
694
+ };
695
+ return {
696
+ models,
697
+ unavailable,
698
+ saved,
699
+ error,
700
+ directory,
701
+ saveModels
702
+ };
703
+ }
704
+ /** The add dialog: search + checkboxes over the endpoint's served catalog. */
705
+ function AddModelsDialog({ models, directory, onAdd, onClose, t }) {
706
+ const [query, setQuery] = (0, react.useState)("");
707
+ const [checked, setChecked] = (0, react.useState)(/* @__PURE__ */ new Set());
708
+ const candidates = (0, react.useMemo)(() => {
709
+ const entries = directory?.entries ?? [];
710
+ const needle = query.trim().toLowerCase();
711
+ return entries.filter((entry) => needle === "" || entry.value.toLowerCase().includes(needle) || entry.label.toLowerCase().includes(needle)).map((entry) => ({
712
+ ...entry,
713
+ added: models.includes(entry.value)
714
+ }));
715
+ }, [
716
+ directory,
717
+ query,
718
+ models
719
+ ]);
720
+ const selectable = candidates.filter((entry) => !entry.added);
721
+ const allChecked = selectable.length > 0 && selectable.every((entry) => checked.has(entry.value));
722
+ const toggleAll = () => {
723
+ setChecked(allChecked ? /* @__PURE__ */ new Set() : new Set(selectable.map((entry) => entry.value)));
724
+ };
725
+ const toggle = (value) => {
726
+ setChecked((prev) => {
727
+ const next = new Set(prev);
728
+ if (next.has(value)) next.delete(value);
729
+ else next.add(value);
730
+ return next;
731
+ });
732
+ };
733
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
734
+ className: ModelListSection_module_css_default.dialogBackdrop,
735
+ role: "presentation",
736
+ onClick: () => {
737
+ onClose();
738
+ },
739
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
740
+ className: ModelListSection_module_css_default.dialog,
741
+ role: "dialog",
742
+ "aria-label": t("models.dialog.title"),
743
+ onClick: (event) => {
744
+ event.stopPropagation();
745
+ },
746
+ children: [
747
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
748
+ className: ModelListSection_module_css_default.dialogHead,
749
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
750
+ className: ModelListSection_module_css_default.dialogTitle,
751
+ children: t("models.dialog.title")
752
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
753
+ type: "button",
754
+ className: ModelListSection_module_css_default.dialogClose,
755
+ "aria-label": t("models.dialog.cancel"),
756
+ onClick: () => {
757
+ onClose();
758
+ },
759
+ children: "✕"
760
+ })]
761
+ }),
762
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
763
+ className: ModelListSection_module_css_default.dialogHint,
764
+ children: t("models.dialog.hint")
765
+ }),
766
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
767
+ className: ModelListSection_module_css_default.dialogToolbar,
768
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
769
+ type: "text",
770
+ className: ModelListSection_module_css_default.modelInput,
771
+ value: query,
772
+ placeholder: t("models.dialog.search"),
773
+ "aria-label": t("models.dialog.search"),
774
+ onChange: (event) => {
775
+ setQuery(event.currentTarget.value);
776
+ }
777
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
778
+ type: "button",
779
+ className: ModelListSection_module_css_default.dialogSelectAll,
780
+ disabled: selectable.length === 0,
781
+ onClick: () => {
782
+ toggleAll();
783
+ },
784
+ children: t("models.dialog.selectAll")
785
+ })]
786
+ }),
787
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
788
+ className: ModelListSection_module_css_default.dialogList,
789
+ children: [
790
+ directory === null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
791
+ className: ModelListSection_module_css_default.hint,
792
+ children: t("models.dialog.loading")
793
+ }),
794
+ directory !== null && directory.entries.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
795
+ className: ModelListSection_module_css_default.hint,
796
+ children: directory.reason ?? t("models.dialog.empty")
797
+ }),
798
+ candidates.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
799
+ className: entry.added ? `${ModelListSection_module_css_default.dialogItem} ${ModelListSection_module_css_default.dialogItemAdded}` : ModelListSection_module_css_default.dialogItem,
800
+ children: [
801
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
802
+ type: "checkbox",
803
+ checked: entry.added || checked.has(entry.value),
804
+ disabled: entry.added,
805
+ onChange: () => {
806
+ toggle(entry.value);
807
+ }
808
+ }),
809
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
810
+ className: ModelListSection_module_css_default.dialogItemValue,
811
+ children: entry.value
812
+ }),
813
+ entry.label !== entry.value && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
814
+ className: ModelListSection_module_css_default.dialogItemLabel,
815
+ children: entry.label
816
+ })
817
+ ]
818
+ }, entry.value))
819
+ ]
820
+ }),
821
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
822
+ className: ModelListSection_module_css_default.dialogActions,
823
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
824
+ type: "button",
825
+ className: ModelListSection_module_css_default.modelSave,
826
+ onClick: () => {
827
+ onClose();
828
+ },
829
+ children: t("models.dialog.cancel")
830
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
831
+ type: "button",
832
+ className: ModelListSection_module_css_default.dialogConfirm,
833
+ disabled: checked.size === 0,
834
+ onClick: () => {
835
+ onAdd([...checked]);
836
+ },
837
+ children: t("models.dialog.confirm")
838
+ })]
839
+ })
840
+ ]
841
+ })
842
+ });
843
+ }
844
+ /**
845
+ * The editor body: the ordered rows, the per-row delete, and the add button.
846
+ * Reorders persist on drop — every gesture writes immediately.
847
+ */
848
+ function ModelListEditor({ models, directory, onChange, t }) {
849
+ const [dialogOpen, setDialogOpen] = (0, react.useState)(false);
850
+ const [dragIndex, setDragIndex] = (0, react.useState)(null);
851
+ const move = (from, to) => {
852
+ if (from === to) return;
853
+ const item = models[from];
854
+ if (item === void 0) return;
855
+ const next = [...models];
856
+ next.splice(from, 1);
857
+ next.splice(to, 0, item);
858
+ onChange(next);
859
+ };
860
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
861
+ className: ModelListSection_module_css_default.modelList,
862
+ children: [
863
+ models.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
864
+ className: ModelListSection_module_css_default.hint,
865
+ children: t("models.empty")
866
+ }),
867
+ models.map((model, index) => {
868
+ const entry = directory?.entries.find((candidate) => candidate.value === model);
869
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
870
+ className: dragIndex === index ? `${ModelListSection_module_css_default.modelRow} ${ModelListSection_module_css_default.modelRowDragging}` : ModelListSection_module_css_default.modelRow,
871
+ draggable: true,
872
+ onDragStart: () => {
873
+ setDragIndex(index);
874
+ },
875
+ onDragOver: (event) => {
876
+ event.preventDefault();
877
+ },
878
+ onDrop: () => {
879
+ if (dragIndex !== null) move(dragIndex, index);
880
+ setDragIndex(null);
881
+ },
882
+ onDragEnd: () => {
883
+ setDragIndex(null);
884
+ },
885
+ children: [
886
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
887
+ className: ModelListSection_module_css_default.modelDrag,
888
+ "aria-hidden": true,
889
+ children: "⠿"
890
+ }),
891
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
892
+ className: ModelListSection_module_css_default.modelRowValue,
893
+ children: model
894
+ }),
895
+ entry !== void 0 && entry.label !== model && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
896
+ className: ModelListSection_module_css_default.modelRowLabel,
897
+ children: entry.label
898
+ }),
899
+ index === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
900
+ className: ModelListSection_module_css_default.modelDefaultChip,
901
+ children: t("models.defaultChip")
902
+ }),
903
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
904
+ type: "button",
905
+ className: ModelListSection_module_css_default.modelDelete,
906
+ "aria-label": `${t("models.delete")}: ${model}`,
907
+ onClick: () => {
908
+ onChange(models.filter((_, other) => other !== index));
909
+ },
910
+ children: "✕"
911
+ })
912
+ ]
913
+ }, model);
914
+ }),
915
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
916
+ className: ModelListSection_module_css_default.row,
917
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
918
+ type: "button",
919
+ className: ModelListSection_module_css_default.modelAdd,
920
+ onClick: () => {
921
+ setDialogOpen(true);
922
+ },
923
+ children: t("models.add")
924
+ }), models.length > 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
925
+ className: ModelListSection_module_css_default.hint,
926
+ children: t("models.orderHint")
927
+ })]
928
+ }),
929
+ dialogOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AddModelsDialog, {
930
+ models,
931
+ directory,
932
+ onAdd: (added) => {
933
+ onChange([...models, ...added]);
934
+ setDialogOpen(false);
935
+ },
936
+ onClose: () => {
937
+ setDialogOpen(false);
938
+ },
939
+ t
940
+ })
941
+ ]
942
+ });
943
+ }
944
+ /**
945
+ * The「可用模型」section every companion's settings card renders: the block
946
+ * chrome (title + ⓘ bubble), the editor, and the save verdict. Reads fire
947
+ * only while `active`; a missing list face renders the header without the
948
+ * editor (declare-and-degrade).
949
+ * @param props - the faces, the activity gate, and copy.
950
+ * @returns the section.
951
+ */
952
+ function ModelListSection({ active, modelList, modelDirectory, directoryRefresh, t }) {
953
+ const { models, unavailable, saved, error, directory, saveModels } = useModelListState(active, modelList, modelDirectory, directoryRefresh);
954
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
955
+ className: ModelListSection_module_css_default.block,
956
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("h3", {
957
+ className: ModelListSection_module_css_default.blockTitle,
958
+ children: [t("models.title"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
959
+ label: t("models.info"),
960
+ side: "bottom",
961
+ maxWidth: 360,
962
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
963
+ type: "button",
964
+ className: ModelListSection_module_css_default.info,
965
+ "aria-label": t("models.info.aria"),
966
+ children: "ⓘ"
967
+ })
968
+ })]
969
+ }), !unavailable && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
970
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelListEditor, {
971
+ models,
972
+ directory,
973
+ onChange: saveModels,
974
+ t
975
+ }),
976
+ saved && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
977
+ className: ModelListSection_module_css_default.saved,
978
+ children: t("models.applied")
979
+ }),
980
+ error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
981
+ className: ModelListSection_module_css_default.errorText,
982
+ children: t("models.error")
983
+ })
984
+ ] })]
985
+ });
986
+ }
987
+ //#endregion
560
988
  //#region \0dsh-css:src/client/SettingsCard.module.css.mjs
561
- const css = ".oWITGW_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}.oWITGW_card:hover{border-color:var(--dsw-alias-label-dimmed)}.oWITGW_cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.oWITGW_header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.oWITGW_header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.oWITGW_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.oWITGW_nameRow{align-items:center;gap:6px;display:flex}.oWITGW_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.oWITGW_description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.oWITGW_chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.oWITGW_chevronOpen{transform:rotate(180deg)}.oWITGW_body{border-top:1px solid var(--dsw-alias-border-l2);flex-direction:column;gap:14px;margin:0 16px;padding:12px 0 8px;display:flex}.oWITGW_block{flex-direction:column;gap:10px;display:flex}.oWITGW_blockTitle{color:var(--dsw-alias-label-primary);align-items:center;gap:6px;margin:0;font-size:13px;font-weight:500;line-height:1.4;display:flex}.oWITGW_info{appearance:none;color:var(--dsw-alias-label-tertiary);cursor:help;background:0 0;border:0;border-radius:50%;padding:0 2px;font-size:12px;line-height:1}.oWITGW_info:hover,.oWITGW_info:focus-visible{color:var(--dsw-alias-label-primary)}.oWITGW_row{align-items:center;gap:12px;display:flex}.oWITGW_rowLabel{color:var(--dsw-alias-label-primary);align-items:center;gap:4px;font-size:13px;line-height:1.4;display:inline-flex}.oWITGW_switch{border:1px solid color-mix(in srgb, currentColor 30%, transparent);background:color-mix(in srgb, currentColor 12%, transparent);cursor:pointer;width:2.25rem;height:1.25rem;color:var(--dsw-alias-label-primary);border-radius:999px;flex:none;align-self:center;padding:.125rem;transition:background .12s}.oWITGW_switch:disabled{opacity:.5;cursor:default}.oWITGW_switchOn{background:color-mix(in srgb, var(--dsh-accent,#4f8cff) 70%, transparent)}.oWITGW_knob{background:var(--dsh-surface,#fff);border-radius:999px;width:.9375rem;height:.9375rem;transition:transform .12s;display:block}.oWITGW_switchOn .oWITGW_knob{transform:translate(.9375rem)}.oWITGW_saved{color:var(--dsw-alias-state-success-primary);font-size:12px}.oWITGW_errorText{color:var(--dsw-alias-state-error-primary);font-size:12px}.oWITGW_hint{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:1.5}.oWITGW_modelInput{min-width:0;font:inherit;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);border-radius:8px;flex:1;padding:5px 8px;font-size:13px;line-height:1.4}.oWITGW_modelInput::placeholder{color:var(--dsw-alias-label-caption);opacity:1}.oWITGW_modelInput:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.oWITGW_modelInput:disabled{opacity:.5}.oWITGW_modelField{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:8px;flex:1;align-items:center;min-width:0;display:flex;position:relative}.oWITGW_modelField:focus-within{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.oWITGW_modelField .oWITGW_modelInput{background:0 0;border:0;border-radius:0}.oWITGW_modelField .oWITGW_modelInput:focus-visible{outline:none}.oWITGW_modelMenuButton{width:24px;height:24px;color:var(--dsw-alias-label-caption);cursor:pointer;background:0 0;border:0;border-radius:6px;flex:none;justify-content:center;align-items:center;margin-right:4px;display:flex}.oWITGW_modelMenuButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.oWITGW_modelMenuButton:disabled{opacity:.5;cursor:default}.oWITGW_modelMenuChevron{transition:transform .16s}.oWITGW_modelMenuChevronOpen{transform:rotate(180deg)}.oWITGW_modelMenu{z-index:10;border:1px solid var(--dsw-alias-border-l2-darkmode-thin,var(--dsw-alias-border-l2));background:var(--dsw-specific-menu,var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-layer-1,#fff)));min-width:160px;max-height:240px;backdrop-filter:var(--dsw-menu-backdrop-filter,blur(20px));box-shadow:var(--dsw-elevation-prominent,var(--dsw-shadow-lv3,0 0 1px 0 #0003, 0 0 4px 0 #00000005, 0 12px 32px 0 #00000014));border-radius:12px;flex-direction:column;padding:4px;display:flex;position:absolute;top:calc(100% + 6px);left:0;right:0;overflow-y:auto}.oWITGW_modelMenuUp{top:auto;bottom:calc(100% + 6px)}.oWITGW_modelItem,.oWITGW_modelItemCurrent,.oWITGW_modelItemDefault{color:var(--dsw-alias-label-primary);text-align:left;white-space:nowrap;cursor:pointer;background:0 0;border:none;border-radius:8px;align-items:center;gap:8px;padding:6px 10px;font-size:12px;line-height:18px;display:flex;overflow:hidden}.oWITGW_modelItem:hover,.oWITGW_modelItemCurrent:hover,.oWITGW_modelItemDefault:hover{background:var(--dsw-alias-interactive-bg-hover)}.oWITGW_modelItemCurrent{font-weight:560}.oWITGW_modelItemLabel{text-overflow:ellipsis;flex:1;min-width:0;overflow:hidden}.oWITGW_modelItemCheck{text-align:center;width:14px;color:var(--dsw-alias-label-secondary);flex:none}.oWITGW_modelItemDefault{color:var(--dsw-alias-label-secondary);box-shadow:0 1px 0 var(--dsw-alias-border-l2);border-radius:8px 8px 0 0;margin-bottom:2px;padding-bottom:7px}.oWITGW_modelSave{appearance:none;font:inherit;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:8px;flex:none;padding:5px 10px;font-size:13px;line-height:1.4}.oWITGW_modelSave:hover:not(:disabled){border-color:var(--dsw-alias-label-dimmed)}.oWITGW_modelSave:disabled{opacity:.5;cursor:default}";
989
+ const css = ".oWITGW_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}.oWITGW_card:hover{border-color:var(--dsw-alias-label-dimmed)}.oWITGW_cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.oWITGW_header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.oWITGW_header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.oWITGW_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.oWITGW_nameRow{align-items:center;gap:6px;display:flex}.oWITGW_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.oWITGW_description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.oWITGW_chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.oWITGW_chevronOpen{transform:rotate(180deg)}.oWITGW_body{border-top:1px solid var(--dsw-alias-border-l2);flex-direction:column;gap:14px;margin:0 16px;padding:12px 0 8px;display:flex}.oWITGW_block{flex-direction:column;gap:10px;display:flex}.oWITGW_blockTitle{color:var(--dsw-alias-label-primary);align-items:center;gap:6px;margin:0;font-size:13px;font-weight:500;line-height:1.4;display:flex}.oWITGW_info{appearance:none;color:var(--dsw-alias-label-tertiary);cursor:help;background:0 0;border:0;border-radius:50%;padding:0 2px;font-size:12px;line-height:1}.oWITGW_info:hover,.oWITGW_info:focus-visible{color:var(--dsw-alias-label-primary)}.oWITGW_row{align-items:center;gap:12px;display:flex}.oWITGW_endpointFields{flex-direction:column;gap:8px;margin-top:4px;display:flex}.oWITGW_endpointField{flex-direction:column;gap:4px;display:flex}.oWITGW_endpointLabel{color:var(--dsw-alias-label-tertiary);align-items:center;gap:6px;font-size:12px;line-height:1.3;display:inline-flex}.oWITGW_configuredChip{color:var(--dsw-alias-success,#1a9e54);background:color-mix(in srgb, var(--dsw-alias-success,#1a9e54) 12%, transparent);border-radius:999px;padding:1px 8px;font-size:11px;line-height:1.4}.oWITGW_rowLabel{color:var(--dsw-alias-label-primary);align-items:center;gap:4px;font-size:13px;line-height:1.4;display:inline-flex}.oWITGW_switch{border:1px solid color-mix(in srgb, currentColor 30%, transparent);background:color-mix(in srgb, currentColor 12%, transparent);cursor:pointer;width:2.25rem;height:1.25rem;color:var(--dsw-alias-label-primary);border-radius:999px;flex:none;align-self:center;padding:.125rem;transition:background .12s}.oWITGW_switch:disabled{opacity:.5;cursor:default}.oWITGW_switchOn{background:color-mix(in srgb, var(--dsh-accent,#4f8cff) 70%, transparent)}.oWITGW_knob{background:var(--dsh-surface,#fff);border-radius:999px;width:.9375rem;height:.9375rem;transition:transform .12s;display:block}.oWITGW_switchOn .oWITGW_knob{transform:translate(.9375rem)}.oWITGW_saved{color:var(--dsw-alias-state-success-primary);font-size:12px}.oWITGW_errorText{color:var(--dsw-alias-state-error-primary);font-size:12px}.oWITGW_hint{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:1.5}.oWITGW_modelInput{min-width:0;font:inherit;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);border-radius:8px;flex:1;padding:5px 8px;font-size:13px;line-height:1.4}.oWITGW_modelInput::placeholder{color:var(--dsw-alias-label-caption);opacity:1}.oWITGW_modelInput:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.oWITGW_modelInput:disabled{opacity:.5}.oWITGW_modelSave{appearance:none;font:inherit;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:8px;flex:none;padding:5px 10px;font-size:13px;line-height:1.4}.oWITGW_modelSave:hover:not(:disabled){border-color:var(--dsw-alias-label-dimmed)}.oWITGW_modelSave:disabled{opacity:.5;cursor:default}";
562
990
  const tagId = "@khorsheed/dsh-local-agent-codex/SettingsCard.module.css";
563
991
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
564
992
  const tag = document.createElement("style");
@@ -568,100 +996,97 @@ window.__ModuleLoader__.load({
568
996
  document.head.appendChild(tag);
569
997
  }
570
998
  var SettingsCard_module_css_default = {
571
- "modelMenuUp": "oWITGW_modelMenuUp",
572
- "headText": "oWITGW_headText",
999
+ "row": "oWITGW_row",
1000
+ "knob": "oWITGW_knob",
1001
+ "hint": "oWITGW_hint",
1002
+ "modelInput": "oWITGW_modelInput",
573
1003
  "card": "oWITGW_card",
574
1004
  "cardOpen": "oWITGW_cardOpen",
575
- "hint": "oWITGW_hint",
576
1005
  "name": "oWITGW_name",
577
- "modelItem": "oWITGW_modelItem",
578
- "chevron": "oWITGW_chevron",
579
- "errorText": "oWITGW_errorText",
580
- "body": "oWITGW_body",
1006
+ "modelSave": "oWITGW_modelSave",
581
1007
  "chevronOpen": "oWITGW_chevronOpen",
582
- "modelField": "oWITGW_modelField",
583
- "modelMenuChevron": "oWITGW_modelMenuChevron",
584
- "description": "oWITGW_description",
585
- "modelItemLabel": "oWITGW_modelItemLabel",
586
1008
  "saved": "oWITGW_saved",
1009
+ "switch": "oWITGW_switch",
1010
+ "description": "oWITGW_description",
1011
+ "errorText": "oWITGW_errorText",
1012
+ "headText": "oWITGW_headText",
1013
+ "endpointFields": "oWITGW_endpointFields",
1014
+ "body": "oWITGW_body",
1015
+ "rowLabel": "oWITGW_rowLabel",
1016
+ "blockTitle": "oWITGW_blockTitle",
1017
+ "chevron": "oWITGW_chevron",
587
1018
  "info": "oWITGW_info",
588
- "modelMenu": "oWITGW_modelMenu",
589
- "block": "oWITGW_block",
590
- "modelSave": "oWITGW_modelSave",
1019
+ "header": "oWITGW_header",
591
1020
  "nameRow": "oWITGW_nameRow",
1021
+ "endpointField": "oWITGW_endpointField",
1022
+ "endpointLabel": "oWITGW_endpointLabel",
1023
+ "block": "oWITGW_block",
592
1024
  "switchOn": "oWITGW_switchOn",
593
- "modelItemCurrent": "oWITGW_modelItemCurrent",
594
- "blockTitle": "oWITGW_blockTitle",
595
- "rowLabel": "oWITGW_rowLabel",
596
- "modelItemCheck": "oWITGW_modelItemCheck",
597
- "modelItemDefault": "oWITGW_modelItemDefault",
598
- "knob": "oWITGW_knob",
599
- "header": "oWITGW_header",
600
- "switch": "oWITGW_switch",
601
- "modelInput": "oWITGW_modelInput",
602
- "modelMenuChevronOpen": "oWITGW_modelMenuChevronOpen",
603
- "row": "oWITGW_row",
604
- "modelMenuButton": "oWITGW_modelMenuButton"
1025
+ "configuredChip": "oWITGW_configuredChip"
605
1026
  };
606
1027
  //#endregion
607
1028
  //#region src/client/SettingsCard.tsx
608
- /** How many previously-saved model identifiers the input suggests. */
609
- const RECENT_MODEL_LIMIT = 5;
610
1029
  /**
611
- * The staged card state both settings surfaces share: the resident-mode and
612
- * model edits write through the bound settingsScope immediately
1030
+ * The staged card state both settings surfaces share: the resident-mode
1031
+ * switch writes through the bound settingsScope immediately
613
1032
  * (revision-fenced), so a flip takes effect on the next delegation round
614
- * without a reload; the harness's model surface is fetched while the surface
615
- * is active (the 0.1.5 card opened, the alpha.2 page mounted) and re-fetched
616
- * after every save.
617
- * @param active - whether the surface currently shows the body (gates the model-surface fetch).
1033
+ * without a reload; the endpoint credential form talks to the harness's own
1034
+ * store through the endpointAuth face. The「可用模型」section is the family
1035
+ * core's shared ModelListSection, which owns its own state.
1036
+ * @param active - whether the surface currently shows the body (gates the credential-form fetch).
618
1037
  * @param useSettings - the bound settings hook from the inject hooks compartment.
619
1038
  * @param scope - the bound settings scope the writes go through.
620
- * @param harnessModel - the harness's memberless model surface read.
621
- * @param t - copy.
1039
+ * @param endpointAuth - the harness's endpoint credential face; absent hides the credential form.
622
1040
  */
623
- function useCardState(active, useSettings, scope, harnessModel, t) {
1041
+ function useCardState(active, useSettings, scope, endpointAuth) {
624
1042
  const snapshot = useSettings((value) => value);
625
1043
  const [saved, setSaved] = (0, react.useState)(false);
626
1044
  const [error, setError] = (0, react.useState)(false);
627
1045
  const ready = snapshot.status === "ready" && snapshot.writable;
628
1046
  const live = snapshot.value?.live ?? false;
629
- const storedModel = snapshot.value?.model ?? "";
630
- const recentModels = snapshot.value?.recentModels ?? [];
631
- const [modelDraft, setModelDraft] = (0, react.useState)(null);
632
- const [modelSaved, setModelSaved] = (0, react.useState)(false);
633
- const [modelError, setModelError] = (0, react.useState)(false);
634
- const modelValue = modelDraft ?? storedModel;
635
- const [modelInfo, setModelInfo] = (0, react.useState)(void 0);
1047
+ const [endpointState, setEndpointState] = (0, react.useState)(null);
1048
+ const [baseUrlDraft, setBaseUrlDraft] = (0, react.useState)(null);
1049
+ const [apiKeyDraft, setApiKeyDraft] = (0, react.useState)(null);
1050
+ const [endpointSaved, setEndpointSaved] = (0, react.useState)(false);
1051
+ const [endpointError, setEndpointError] = (0, react.useState)(false);
1052
+ const endpointDirty = baseUrlDraft !== null || apiKeyDraft !== null;
1053
+ const endpointUnavailable = endpointAuth === void 0 || endpointState === "unavailable";
1054
+ const endpointLoading = !endpointUnavailable && endpointState === null;
1055
+ const apiKeyConfigured = endpointState !== null && endpointState !== "unavailable" && endpointState.apiKeySet;
636
1056
  (0, react.useEffect)(() => {
637
- if (!active) return;
1057
+ if (!active || endpointAuth === void 0) return;
638
1058
  let stale = false;
639
- harnessModel().then((info) => {
640
- if (!stale) setModelInfo(info ?? null);
1059
+ endpointAuth.read().then((state) => {
1060
+ if (!stale) setEndpointState(state ?? "unavailable");
1061
+ }, () => {
1062
+ if (!stale) setEndpointState("unavailable");
641
1063
  });
642
1064
  return () => {
643
1065
  stale = true;
644
1066
  };
645
1067
  }, [
646
1068
  active,
647
- harnessModel,
648
- modelSaved
1069
+ endpointAuth,
1070
+ endpointSaved
649
1071
  ]);
650
- /**
651
- * Commit the model field. A blank value UNSETS the key rather than storing
652
- * an empty string, so the field re-inherits the YAML composition base — and
653
- * with no base, the harness is back to passing no model at all.
654
- */
655
- const saveModel = () => {
656
- const next = modelValue.trim();
657
- setModelSaved(false);
658
- setModelError(false);
659
- (next === "" ? scope.unset("model") : scope.set("model", next)).then(async () => {
660
- if (next !== "") await scope.set("recentModels", [next, ...recentModels.filter((value) => value !== next)].slice(0, RECENT_MODEL_LIMIT));
661
- setModelDraft(null);
662
- setModelSaved(true);
1072
+ const saveEndpoint = () => {
1073
+ if (endpointAuth === void 0) return;
1074
+ setEndpointSaved(false);
1075
+ setEndpointError(false);
1076
+ const fields = {
1077
+ ...baseUrlDraft !== null ? { baseUrl: baseUrlDraft.trim() === "" ? null : baseUrlDraft.trim() } : {},
1078
+ ...apiKeyDraft !== null ? { apiKey: apiKeyDraft.trim() === "" ? null : apiKeyDraft.trim() } : {}
1079
+ };
1080
+ endpointAuth.write(fields).then((state) => {
1081
+ if (state !== void 0 && state !== null) {
1082
+ setEndpointState(state);
1083
+ if (state.apiKeySet) publishAuthStatus("codex", "authenticated");
1084
+ }
1085
+ setBaseUrlDraft(null);
1086
+ setApiKeyDraft(null);
1087
+ setEndpointSaved(true);
663
1088
  }, () => {
664
- setModelError(true);
1089
+ setEndpointError(true);
665
1090
  });
666
1091
  };
667
1092
  const write = (field, value) => {
@@ -673,194 +1098,166 @@ window.__ModuleLoader__.load({
673
1098
  setError(true);
674
1099
  });
675
1100
  };
676
- const choices = modelInfo?.choices ?? recentModels;
677
- const [modelMenuOpen, setModelMenuOpen] = (0, react.useState)(false);
678
- const [modelMenuUp, setModelMenuUp] = (0, react.useState)(false);
679
- const modelFieldRef = (0, react.useRef)(null);
680
- (0, react.useEffect)(() => {
681
- if (!modelMenuOpen) return;
682
- const onPointerDown = (event) => {
683
- if (modelFieldRef.current?.contains(event.target) === true) return;
684
- setModelMenuOpen(false);
685
- };
686
- const onKeyDown = (event) => {
687
- if (event.key === "Escape") setModelMenuOpen(false);
688
- };
689
- document.addEventListener("mousedown", onPointerDown);
690
- document.addEventListener("keydown", onKeyDown);
691
- return () => {
692
- document.removeEventListener("mousedown", onPointerDown);
693
- document.removeEventListener("keydown", onKeyDown);
694
- };
695
- }, [modelMenuOpen]);
696
- const toggleModelMenu = () => {
697
- if (!modelMenuOpen) {
698
- const rect = modelFieldRef.current?.getBoundingClientRect();
699
- if (rect !== void 0) {
700
- const spaceBelow = window.innerHeight - rect.bottom;
701
- setModelMenuUp(spaceBelow < 260 && rect.top > spaceBelow);
702
- }
703
- }
704
- setModelMenuOpen((open) => !open);
705
- };
706
1101
  return {
707
1102
  snapshot,
708
1103
  saved,
709
1104
  error,
710
1105
  ready,
711
1106
  live,
712
- storedModel,
713
- modelValue,
714
- modelSaved,
715
- modelError,
716
- choices,
717
- modelMenuOpen,
718
- modelMenuUp,
719
- modelFieldRef,
720
- modelPlaceholder: (modelInfo != null && modelInfo.cliDefault !== void 0 && modelInfo.cliDefault !== "" ? modelInfo.cliDefault : modelInfo != null && modelInfo.source === "cli-builtin" && modelInfo.effective !== void 0 && modelInfo.effective !== "" ? modelInfo.effective : modelInfo != null && modelInfo.lastObserved !== void 0 && modelInfo.lastObserved !== "" ? modelInfo.lastObserved : void 0) ?? t("model.placeholder"),
721
- modelDefaultItem: modelInfo != null && modelInfo.cliDefault !== void 0 && modelInfo.cliDefault !== "" ? t("model.menuDefault.cli-config", { model: modelInfo.cliDefault }) : modelInfo != null && modelInfo.source === "cli-builtin" && modelInfo.effective !== void 0 && modelInfo.effective !== "" ? t("model.menuDefault.cli-builtin", { model: modelInfo.effective }) : modelInfo != null && modelInfo.lastObserved !== void 0 && modelInfo.lastObserved !== "" ? t("model.menuDefault.last-observed", { model: modelInfo.lastObserved }) : t("model.menuDefault"),
722
- saveModel,
723
1107
  write,
724
- toggleModelMenu,
725
- setModelDraft,
726
- setModelMenuOpen
1108
+ endpointState,
1109
+ endpointUnavailable,
1110
+ endpointLoading,
1111
+ baseUrlValue: baseUrlDraft ?? (endpointState !== null && endpointState !== "unavailable" ? endpointState.baseUrl ?? "" : ""),
1112
+ apiKeyConfigured,
1113
+ apiKeyDraft,
1114
+ endpointSaved,
1115
+ endpointError,
1116
+ endpointDirty,
1117
+ saveEndpoint,
1118
+ setBaseUrlDraft,
1119
+ setApiKeyDraft
727
1120
  };
728
1121
  }
729
- /** The card body both settings surfaces share: the auth block, the default-model block, and the resident-mode block. */
730
- function CardBody({ state, auth, authT, renderModelPicker, useSessions, t }) {
731
- const { snapshot, saved, error, ready, live, storedModel, modelValue, modelSaved, modelError, choices, modelMenuOpen, modelMenuUp, modelFieldRef, modelPlaceholder, modelDefaultItem, saveModel, write, toggleModelMenu, setModelDraft, setModelMenuOpen } = state;
732
- const modelInputElement = /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
733
- type: "text",
734
- className: SettingsCard_module_css_default.modelInput,
735
- value: modelValue,
736
- placeholder: modelPlaceholder,
737
- "aria-label": t("model.title"),
738
- disabled: !ready,
739
- onChange: (event) => {
740
- setModelDraft(event.target.value);
741
- }
742
- });
1122
+ /** The card body both settings surfaces share: the auth block, the model-list section, and the resident-mode block. */
1123
+ function CardBody({ state, active, auth, authT, modelList, modelDirectory, checkEndpoint, useSessions, t }) {
1124
+ const { snapshot, saved, error, ready, live, write, endpointUnavailable, endpointLoading, baseUrlValue, apiKeyConfigured, apiKeyDraft, endpointSaved, endpointError, endpointDirty, saveEndpoint, setBaseUrlDraft, setApiKeyDraft } = state;
1125
+ const [checking, setChecking] = (0, react.useState)(false);
1126
+ const [checkText, setCheckText] = (0, react.useState)(null);
1127
+ const runCheck = () => {
1128
+ if (checkEndpoint === void 0) return;
1129
+ setChecking(true);
1130
+ setCheckText(null);
1131
+ checkEndpoint().then((text) => {
1132
+ setCheckText(text ?? t("endpoint.checkUnavailable"));
1133
+ setChecking(false);
1134
+ }, () => {
1135
+ setCheckText(t("endpoint.checkFailed"));
1136
+ setChecking(false);
1137
+ });
1138
+ };
743
1139
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
744
1140
  className: SettingsCard_module_css_default.body,
745
1141
  children: [
746
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
747
- className: SettingsCard_module_css_default.block,
748
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
749
- className: SettingsCard_module_css_default.blockTitle,
750
- children: t("auth.title")
751
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProviderAuthBlock, {
752
- harness: {
753
- id: "codex",
754
- label: "Codex"
755
- },
756
- useSessions,
757
- status: auth.status,
758
- runCommand: auth.runCommand,
759
- t: authT
760
- })]
761
- }),
762
1142
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
763
1143
  className: SettingsCard_module_css_default.block,
764
1144
  children: [
765
1145
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("h3", {
766
1146
  className: SettingsCard_module_css_default.blockTitle,
767
- children: [t("model.title"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
768
- label: t("model.info"),
1147
+ children: [t("auth.title"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
1148
+ label: t("endpoint.info"),
769
1149
  side: "bottom",
770
1150
  maxWidth: 360,
771
1151
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
772
1152
  type: "button",
773
1153
  className: SettingsCard_module_css_default.info,
774
- "aria-label": t("model.info.aria"),
1154
+ "aria-label": t("endpoint.info.aria"),
775
1155
  children: "ⓘ"
776
1156
  })
777
1157
  })]
778
1158
  }),
779
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
780
- className: SettingsCard_module_css_default.row,
781
- children: [renderModelPicker?.({
782
- value: modelValue,
783
- onChange: setModelDraft,
784
- disabled: !ready,
785
- defaultLabel: modelDefaultItem
786
- }) ?? (choices.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
787
- className: SettingsCard_module_css_default.modelField,
788
- ref: modelFieldRef,
789
- children: [
790
- modelInputElement,
791
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
792
- type: "button",
793
- className: SettingsCard_module_css_default.modelMenuButton,
794
- "aria-label": t("model.menu"),
795
- "aria-haspopup": "menu",
796
- "aria-expanded": modelMenuOpen,
797
- disabled: !ready,
798
- onClick: () => {
799
- toggleModelMenu();
800
- },
801
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconChevronDownOutlineMedium, { className: modelMenuOpen ? `${SettingsCard_module_css_default.modelMenuChevron} ${SettingsCard_module_css_default.modelMenuChevronOpen}` : SettingsCard_module_css_default.modelMenuChevron })
802
- }),
803
- modelMenuOpen && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
804
- className: modelMenuUp ? `${SettingsCard_module_css_default.modelMenu} ${SettingsCard_module_css_default.modelMenuUp}` : SettingsCard_module_css_default.modelMenu,
805
- role: "menu",
806
- "aria-label": t("model.menu"),
807
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1159
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProviderAuthBlock, {
1160
+ harness: {
1161
+ id: "codex",
1162
+ label: "Codex"
1163
+ },
1164
+ useSessions,
1165
+ status: auth.status,
1166
+ runCommand: auth.runCommand,
1167
+ t: authT
1168
+ }),
1169
+ endpointUnavailable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1170
+ className: SettingsCard_module_css_default.hint,
1171
+ children: t("endpoint.unavailable")
1172
+ }),
1173
+ !endpointUnavailable && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1174
+ className: SettingsCard_module_css_default.endpointFields,
1175
+ children: [
1176
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1177
+ className: SettingsCard_module_css_default.endpointField,
1178
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1179
+ className: SettingsCard_module_css_default.endpointLabel,
1180
+ children: t("endpoint.baseUrl")
1181
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1182
+ type: "text",
1183
+ className: SettingsCard_module_css_default.modelInput,
1184
+ value: baseUrlValue,
1185
+ placeholder: t("endpoint.baseUrl.placeholder"),
1186
+ "aria-label": t("endpoint.baseUrl"),
1187
+ disabled: endpointLoading,
1188
+ onChange: (event) => {
1189
+ setBaseUrlDraft(event.currentTarget.value);
1190
+ }
1191
+ })]
1192
+ }),
1193
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1194
+ className: SettingsCard_module_css_default.endpointField,
1195
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1196
+ className: SettingsCard_module_css_default.endpointLabel,
1197
+ children: [t("endpoint.apiKey"), apiKeyConfigured && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1198
+ className: SettingsCard_module_css_default.configuredChip,
1199
+ children: t("endpoint.configured")
1200
+ })]
1201
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1202
+ type: "password",
1203
+ className: SettingsCard_module_css_default.modelInput,
1204
+ value: apiKeyDraft ?? "",
1205
+ placeholder: apiKeyConfigured ? t("endpoint.secret.configured") : t("endpoint.secret.placeholder"),
1206
+ "aria-label": t("endpoint.apiKey"),
1207
+ autoComplete: "off",
1208
+ disabled: endpointLoading,
1209
+ onChange: (event) => {
1210
+ setApiKeyDraft(event.currentTarget.value);
1211
+ }
1212
+ })]
1213
+ }),
1214
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1215
+ className: SettingsCard_module_css_default.row,
1216
+ children: [
1217
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
808
1218
  type: "button",
809
- role: "menuitemradio",
810
- "aria-checked": modelValue.trim() === "",
811
- className: modelValue.trim() === "" ? `${SettingsCard_module_css_default.modelItemDefault} ${SettingsCard_module_css_default.modelItemCurrent}` : SettingsCard_module_css_default.modelItemDefault,
1219
+ className: SettingsCard_module_css_default.modelSave,
1220
+ disabled: endpointLoading || !endpointDirty,
812
1221
  onClick: () => {
813
- setModelDraft("");
814
- setModelMenuOpen(false);
1222
+ saveEndpoint();
815
1223
  },
816
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
817
- className: SettingsCard_module_css_default.modelItemLabel,
818
- children: modelDefaultItem
819
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
820
- className: SettingsCard_module_css_default.modelItemCheck,
821
- "aria-hidden": true,
822
- children: modelValue.trim() === "" ? "✓" : ""
823
- })]
824
- }), choices.map((choice) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1224
+ children: t("endpoint.save")
1225
+ }),
1226
+ checkEndpoint !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
825
1227
  type: "button",
826
- role: "menuitemradio",
827
- "aria-checked": choice === modelValue,
828
- className: choice === modelValue ? SettingsCard_module_css_default.modelItemCurrent : SettingsCard_module_css_default.modelItem,
1228
+ className: SettingsCard_module_css_default.modelSave,
1229
+ disabled: endpointLoading || endpointDirty || checking,
1230
+ title: endpointDirty ? t("endpoint.checkDirtyHint") : void 0,
829
1231
  onClick: () => {
830
- setModelDraft(choice);
831
- setModelMenuOpen(false);
1232
+ runCheck();
832
1233
  },
833
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
834
- className: SettingsCard_module_css_default.modelItemLabel,
835
- children: choice
836
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
837
- className: SettingsCard_module_css_default.modelItemCheck,
838
- "aria-hidden": true,
839
- children: choice === modelValue ? "✓" : ""
840
- })]
841
- }, choice))]
842
- })
843
- ]
844
- }) : modelInputElement), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
845
- type: "button",
846
- className: SettingsCard_module_css_default.modelSave,
847
- disabled: !ready || modelValue.trim() === storedModel,
848
- onClick: () => {
849
- saveModel();
850
- },
851
- children: t("model.save")
852
- })]
853
- }),
854
- modelSaved && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
855
- className: SettingsCard_module_css_default.saved,
856
- children: t("model.applied")
857
- }),
858
- modelError && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
859
- className: SettingsCard_module_css_default.errorText,
860
- children: t("model.error")
1234
+ children: checking ? t("endpoint.checking") : t("endpoint.check")
1235
+ }),
1236
+ endpointSaved && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1237
+ className: SettingsCard_module_css_default.saved,
1238
+ children: t("endpoint.applied")
1239
+ }),
1240
+ endpointError && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1241
+ className: SettingsCard_module_css_default.errorText,
1242
+ children: t("endpoint.error")
1243
+ })
1244
+ ]
1245
+ }),
1246
+ checkText !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1247
+ className: SettingsCard_module_css_default.hint,
1248
+ children: checkText
1249
+ })
1250
+ ]
861
1251
  })
862
1252
  ]
863
1253
  }),
1254
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelListSection, {
1255
+ active,
1256
+ modelList,
1257
+ modelDirectory,
1258
+ directoryRefresh: endpointSaved,
1259
+ t: authT
1260
+ }),
864
1261
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
865
1262
  className: SettingsCard_module_css_default.block,
866
1263
  children: [
@@ -910,8 +1307,9 @@ window.__ModuleLoader__.load({
910
1307
  });
911
1308
  }
912
1309
  /**
913
- * The codex harness settings card in the 0.1.5 plugin configuration tab: the
914
- * same collapsible chrome the official plugin cards use, re-implemented
1310
+ * The codex harness settings card of the retired 0.1.5 plugin configuration
1311
+ * tab (no longer registered; the live surface is the plugins.item entry
1312
+ * below): the same collapsible chrome the official plugin cards use, re-implemented
915
1313
  * locally (the bundle-purity gate forbids value-importing the official chrome
916
1314
  * — the context-guard pattern), with the at-a-glance credential dot in the
917
1315
  * header. Explanation copy lives in hover/focus bubbles behind ⓘ anchors —
@@ -920,9 +1318,9 @@ window.__ModuleLoader__.load({
920
1318
  * @returns the card.
921
1319
  */
922
1320
  function CodexSettingsCard(props) {
923
- const { useSettings, scope, auth, authT, renderModelPicker, harnessModel, useSessions, t } = props;
1321
+ const { useSettings, scope, auth, authT, modelList, modelDirectory, checkEndpoint, endpointAuth, useSessions, t } = props;
924
1322
  const [open, setOpen] = (0, react.useState)(false);
925
- const state = useCardState(open, useSettings, scope, harnessModel, t);
1323
+ const state = useCardState(open, useSettings, scope, endpointAuth);
926
1324
  const authStatus = useHarnessAuthStatus("codex", auth.status);
927
1325
  const dotLabel = authT(authStatus === "authenticated" ? "settings.authenticated" : authStatus === "anonymous" ? "settings.notAuthenticated" : authStatus === "checking" ? "loading" : "error");
928
1326
  const title = t("card.title");
@@ -954,33 +1352,61 @@ window.__ModuleLoader__.load({
954
1352
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconChevronDownOutlineMedium, { className: open ? `${SettingsCard_module_css_default.chevron} ${SettingsCard_module_css_default.chevronOpen}` : SettingsCard_module_css_default.chevron })]
955
1353
  }), open && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CardBody, {
956
1354
  state,
1355
+ active: open,
957
1356
  auth,
958
1357
  authT,
959
- renderModelPicker,
1358
+ modelList,
1359
+ modelDirectory,
1360
+ checkEndpoint,
960
1361
  useSessions,
961
1362
  t
962
1363
  })]
963
1364
  });
964
1365
  }
965
1366
  /**
966
- * The bundle's configuration on the alpha.2 Plugins page
967
- * (`plugins.bundle.config`, keyed by package name): the page draws the title,
968
- * the icon, and the crumb itself, so the entry renders the one-liner for the
969
- * `summary` view and the bare form — with its own save control — for `page`.
1367
+ * The provider's card on the Plugins page (the `plugins.item` entry
1368
+ * client/index.ts registers while the Host serves the namespace AND the core
1369
+ * predates the family card): the page draws the title, the icon, and the
1370
+ * crumb itself, so the entry renders the one-liner for the `summary` view and
1371
+ * the bare form — with its own save control — for `page`.
970
1372
  * @param props - the owner view, the injected scope/auth faces, and copy.
971
1373
  * @returns the entry.
972
1374
  */
973
- function CodexBundleConfig({ view, useSettings, scope, auth, authT, renderModelPicker, harnessModel, useSessions, t }) {
974
- const state = useCardState(view === "page", useSettings, scope, harnessModel, t);
1375
+ function CodexBundleConfig({ view, useSettings, scope, auth, authT, modelList, modelDirectory, checkEndpoint, endpointAuth, useSessions, t }) {
1376
+ const state = useCardState(view === "page", useSettings, scope, endpointAuth);
975
1377
  if (view === "summary") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
976
1378
  className: SettingsCard_module_css_default.description,
977
1379
  children: t("card.description")
978
1380
  });
979
1381
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CardBody, {
980
1382
  state,
1383
+ active: view === "page",
1384
+ auth,
1385
+ authT,
1386
+ modelList,
1387
+ modelDirectory,
1388
+ checkEndpoint,
1389
+ useSessions,
1390
+ t
1391
+ });
1392
+ }
1393
+ /**
1394
+ * The harness's section inside the core's「本机 Agent 委派」family card: the
1395
+ * same useCardState + CardBody the plugins.item page view renders — the
1396
+ * collapsible chrome belongs to the family card now.
1397
+ * @param props - the injected scope/auth faces, the forwarded session seat, and copy.
1398
+ * @returns the section body.
1399
+ */
1400
+ function CodexCardSection({ active, useSettings, scope, auth, authT, modelList, modelDirectory, checkEndpoint, endpointAuth, useSessions, t }) {
1401
+ const state = useCardState(active, useSettings, scope, endpointAuth);
1402
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CardBody, {
1403
+ state,
1404
+ active,
981
1405
  auth,
982
1406
  authT,
983
- renderModelPicker,
1407
+ modelList,
1408
+ modelDirectory,
1409
+ checkEndpoint,
984
1410
  useSessions,
985
1411
  t
986
1412
  });
@@ -988,18 +1414,37 @@ window.__ModuleLoader__.load({
988
1414
  //#endregion
989
1415
  //#region src/client/index.ts
990
1416
  /**
991
- * This bundle's package name — the key the Plugins page dispatches
992
- * `plugins.bundle.config` entries on (identity triangle: cordis.patch.yml,
993
- * tsdown.config.ts, invariant.ts).
1417
+ * Local-agent-codex plugin, browser half: the codex settings card — a section
1418
+ * of the family core's「本机 Agent 委派」family card on the Plugins page
1419
+ * (registered through the core's `localAgentUi.registerHarnessCard` while the
1420
+ * Host serves the `local-agent-codex` namespace), or this package's own
1421
+ * `plugins.item` entry when the core predates the family card (the
1422
+ * declare-and-degrade fallback). Every install shape serves the row's
1423
+ * volatile Config under the row id —
1424
+ * standalone install, family bundle, or mode pack — so the card follows the
1425
+ * served namespace rather than any carrier bundle, and a composition without
1426
+ * the row shows no trace of it. The card carries the family core's shared
1427
+ * ProviderAuthBlock (login/logout/device-code, backed by the core's
1428
+ * localAgentGateway Remote and the commands channel — endpoint-key only, no
1429
+ * login flow), the endpoint credential form, the family core's shared
1430
+ * ModelListSection (the curated ordered shortlist — first entry is the
1431
+ * default model — edited through the gateway's modelList/configureModelList
1432
+ * Remotes, the add dialog fed by modelDirectory), and the resident-mode
1433
+ * block (live switch), whose writes ride
1434
+ * the bound settingsScope — the host watcher hot-applies them, no reload.
1435
+ * Every read degrades: an absent gateway renders the auth block's
1436
+ * 'unavailable' state and drops the model surfaces, an
1437
+ * unregistered namespace disables the live controls.
1438
+ * @module @khorsheed/dsh-local-agent-codex/client
994
1439
  */
995
- const PACKAGE_NAME = "@khorsheed/dsh-local-agent-codex";
996
1440
  /**
997
1441
  * Required services: slot registry, command Remote, and locale registry. The
998
1442
  * settings scope is NOT a static inject — its service name differs per host
999
1443
  * line (`settingsScope` on 0.1.5, `configForms` on rc.1), so a static entry
1000
1444
  * would pend the whole client plugin on the other line; bindSettingsScope
1001
1445
  * probes the serving one instead (the settings client package is a declared
1002
- * client inject, so one of them is always already up).
1446
+ * client inject, so one of them is always already up). The card track probes
1447
+ * `configForms` through the same kind of deferred inject (see apply).
1003
1448
  */
1004
1449
  const inject = [
1005
1450
  "slots",
@@ -1008,8 +1453,8 @@ window.__ModuleLoader__.load({
1008
1453
  "locale"
1009
1454
  ];
1010
1455
  /**
1011
- * Client plugin body: register the dictionaries and the codex settings
1012
- * surfaces bound to the local-agent-codex namespace.
1456
+ * Client plugin body: register the dictionaries and the codex settings card
1457
+ * bound to the local-agent-codex namespace.
1013
1458
  * @param ctx - client root context.
1014
1459
  */
1015
1460
  function apply(ctx) {
@@ -1017,42 +1462,91 @@ window.__ModuleLoader__.load({
1017
1462
  zh,
1018
1463
  en
1019
1464
  }), "local-agent-codex: dictionaries");
1465
+ const t = ctx.locale.bind(NS);
1020
1466
  const scope = bindSettingsScope(ctx, "local-agent-codex");
1021
1467
  const authT = ctx.locale.bind("local-agent");
1022
1468
  const gateway = () => ctx.get("remote.localAgentGateway");
1023
- const cardInject = () => ({
1469
+ const cardFace = () => ({
1024
1470
  scope,
1025
- hooks: { settings: scope },
1026
1471
  authT,
1027
- renderModelPicker: (props) => ctx.get("localAgentUi")?.renderHarnessModelPicker("codex", props),
1028
- harnessModel: () => gateway()?.harnessModel("codex").then((result) => result.ok ? result.value : void 0) ?? Promise.resolve(void 0),
1472
+ modelList: {
1473
+ read: () => {
1474
+ const gw = gateway();
1475
+ if (gw === void 0 || typeof gw.modelList !== "function") return Promise.resolve(void 0);
1476
+ return gw.modelList("codex").then((result) => result.ok ? result.value : void 0);
1477
+ },
1478
+ write: (models) => {
1479
+ const gw = gateway();
1480
+ if (gw === void 0 || typeof gw.configureModelList !== "function") return Promise.resolve(void 0);
1481
+ return gw.configureModelList("codex", models).then((result) => result.ok ? result.value : void 0);
1482
+ }
1483
+ },
1484
+ modelDirectory: (refresh) => {
1485
+ const gw = gateway();
1486
+ if (gw === void 0 || typeof gw.modelDirectory !== "function") return Promise.resolve(void 0);
1487
+ return gw.modelDirectory("codex", void 0, refresh ?? false).then((result) => result.ok ? result.value : void 0);
1488
+ },
1489
+ checkEndpoint: () => {
1490
+ const gw = gateway();
1491
+ if (gw === void 0 || typeof gw.checkEndpoint !== "function") return Promise.resolve(void 0);
1492
+ return gw.checkEndpoint("codex").then((result) => result.ok ? result.value : void 0);
1493
+ },
1494
+ endpointAuth: {
1495
+ read: () => {
1496
+ const gw = gateway();
1497
+ if (gw === void 0 || typeof gw.endpointAuth !== "function") return Promise.resolve(void 0);
1498
+ return gw.endpointAuth("codex").then((result) => result.ok ? result.value : void 0);
1499
+ },
1500
+ write: (fields) => {
1501
+ const gw = gateway();
1502
+ if (gw === void 0 || typeof gw.configureEndpoint !== "function") return Promise.resolve(void 0);
1503
+ return gw.configureEndpoint("codex", fields).then((result) => result.ok ? result.value : void 0);
1504
+ }
1505
+ },
1029
1506
  auth: {
1030
1507
  status: (name) => gateway()?.status(name, void 0).then((result) => result.ok ? result.value : void 0) ?? Promise.resolve(void 0),
1031
1508
  runCommand: (sessionId, line) => ctx.remote.commands.execute(sessionId, line, []).then((result) => result.ok ? result.value?.result.text : void 0)
1032
1509
  }
1033
1510
  });
1034
- ctx.slots.inject("plugins.bundle.config", () => ctx.slots.register({
1035
- name: "plugins.bundle.config",
1036
- key: PACKAGE_NAME,
1037
- locale: NS,
1038
- inject: cardInject
1039
- }, CodexBundleConfig));
1040
- ctx.slots.inject("plugins.row.config", () => ctx.slots.register({
1041
- name: "plugins.row.config",
1042
- key: "@khorsheed/dsh-bundle-local-agent#local-agent-codex",
1043
- locale: NS,
1044
- inject: cardInject
1045
- }, CodexBundleConfig));
1046
- const legacy = ctx.slots;
1047
- legacy.inject("settings.plugin.item", () => legacy.register({
1048
- name: "settings.plugin.item",
1049
- key: "local-agent-codex",
1050
- locale: NS,
1051
- inject: cardInject
1052
- }, CodexSettingsCard));
1511
+ const cardInject = () => ({
1512
+ ...cardFace(),
1513
+ hooks: { settings: scope }
1514
+ });
1515
+ const subscribeScope = (listener) => scope.subscribe(listener);
1516
+ const readScope = () => scope.getSnapshot();
1517
+ const useSettings = (select) => select((0, react.useSyncExternalStore)(subscribeScope, readScope));
1518
+ ctx.inject(["configForms"], (formsCtx) => {
1519
+ const forms = formsCtx.configForms;
1520
+ if (typeof forms.whileServed !== "function") return;
1521
+ return forms.whileServed(["local-agent-codex"], () => {
1522
+ const ui = ctx.get("localAgentUi");
1523
+ if (typeof ui?.registerHarnessCard === "function") return ui.registerHarnessCard({
1524
+ name: "codex",
1525
+ order: 120,
1526
+ locale: NS,
1527
+ label: () => t("card.title"),
1528
+ render: ({ active, useSessions }) => (0, react.createElement)(CodexCardSection, {
1529
+ active,
1530
+ useSessions,
1531
+ ...cardFace(),
1532
+ useSettings,
1533
+ t
1534
+ })
1535
+ });
1536
+ return ctx.slots.inject("plugins.item", () => ctx.slots.register({
1537
+ name: "plugins.item",
1538
+ id: "local-agent-codex",
1539
+ order: 120,
1540
+ label: () => t("card.title"),
1541
+ locale: NS,
1542
+ inject: cardInject
1543
+ }, CodexBundleConfig));
1544
+ });
1545
+ });
1053
1546
  }
1054
1547
  //#endregion
1055
1548
  exports.CodexBundleConfig = CodexBundleConfig;
1549
+ exports.CodexCardSection = CodexCardSection;
1056
1550
  exports.CodexSettingsCard = CodexSettingsCard;
1057
1551
  exports.NS = NS;
1058
1552
  exports.apply = apply;