hiwork-core 0.0.8 → 0.0.10

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.
@@ -15,6 +15,8 @@ export const DEFAULT_LOGIN_MODE = 'both';
15
15
  export const CLIENT_POLICY_PATH = '/api/client/policy';
16
16
  /** 策略拉取默认超时:免登录只读接口,超过 5s 视同不可达(fail-open)。 */
17
17
  export const CLIENT_POLICY_TIMEOUT_MS = 5_000;
18
+ /** fail-open 缺省:旧网关不返回 passwordLogin 时,密码表单仍视作可用。 */
19
+ export const DEFAULT_PASSWORD_LOGIN = true;
18
20
  /**
19
21
  * 拼策略 URL:`{baseURL}/api/client/policy?platform=&arch=&version=&device_id=`。
20
22
  * 参数固定顺序、逐个 `encodeURIComponent`(device_id 可能是任意 ASCII,含 `+/=` 也必须安全);
@@ -47,6 +49,16 @@ function parseLoginMode(payload) {
47
49
  const value = payload.loginMode;
48
50
  return value === 'browser' || value === 'form' || value === 'both' ? value : undefined;
49
51
  }
52
+ /**
53
+ * 解析 `passwordLogin`:只认布尔值;缺失 / 非法值一律 {@link DEFAULT_PASSWORD_LOGIN}
54
+ * (fail-open:旧网关不返回该字段,不能因此收起密码入口)。
55
+ */
56
+ function parsePasswordLogin(payload) {
57
+ if (typeof payload !== 'object' || payload === null)
58
+ return DEFAULT_PASSWORD_LOGIN;
59
+ const value = payload.passwordLogin;
60
+ return typeof value === 'boolean' ? value : DEFAULT_PASSWORD_LOGIN;
61
+ }
50
62
  /**
51
63
  * 拉取并解析客户端策略。**永不抛异常**:任何失败都返回 undefined(fail-open)。
52
64
  *
@@ -73,7 +85,7 @@ export async function fetchClientPolicy(opts) {
73
85
  return undefined;
74
86
  }
75
87
  const loginMode = parseLoginMode(payload);
76
- return loginMode === undefined ? undefined : { loginMode };
88
+ return loginMode === undefined ? undefined : { loginMode, passwordLogin: parsePasswordLogin(payload) };
77
89
  }
78
90
  catch {
79
91
  // 网络异常 / 超时(AbortSignal.timeout 抛 TimeoutError)/ 非法 URL:一律 fail-open。
package/lib/client.js CHANGED
@@ -78,7 +78,11 @@ var textInput = {
78
78
  var frameSurfaceSelector = '[class*="_frame"]:has(> [class*="_sidebarCol"])';
79
79
  var frameCss = [
80
80
  `${frameSurfaceSelector} { background: var(--dsw-specific-sidebar-fill, #f9fafb); }`,
81
+ // 原生会话区根节点:两代链路并存,见上方注释。
81
82
  `html[data-dsh-shell="win32"] [class*="_centerCol"] > div > [class*="_root"] { border-top-left-radius: 16px; }`,
83
+ `html[data-dsh-shell="win32"] [class*="_centerCol"] [data-slot="main.conversation"] > [class*="_root"] { border-top-left-radius: 16px; }`,
84
+ // HiWork 扩展页 takeover 根节点(中心列直接子元素)。
85
+ `html[data-dsh-shell="win32"] [class*="_centerCol"] > [data-hiwork-feature-root] { border-top-left-radius: 16px; }`,
82
86
  `[class*="_sidebarCol"] { border-right: 0 !important; }`
83
87
  ].join("\n");
84
88
  var hostHideCss = [
@@ -125,6 +129,9 @@ function authorizeUrlOf(value) {
125
129
  function browserLoginPendingOf(value) {
126
130
  return authorizeUrlOf(value) !== void 0 || value?.browserLoginRequest !== void 0;
127
131
  }
132
+ function passwordLoginOf(value) {
133
+ return value?.passwordLogin !== false;
134
+ }
128
135
  function browserLoginViewOf(value) {
129
136
  const loginMode = normalizeLoginMode(value?.loginMode);
130
137
  if (loginMode === "form") return { loginMode, browserLoginUrl: void 0, browserLoginPending: false };
@@ -162,7 +169,8 @@ function project(value) {
162
169
  budgets: value?.budgets,
163
170
  keys: value?.keys,
164
171
  activeKeyId: value?.keyId ?? value?.keys?.[0]?.id,
165
- ...browserLoginViewOf(value)
172
+ ...browserLoginViewOf(value),
173
+ passwordLogin: passwordLoginOf(value)
166
174
  };
167
175
  if (value?.pendingLogin !== void 0) return { ...base, status: "logging-in" };
168
176
  if (value?.lastLogin !== void 0) {
@@ -189,6 +197,7 @@ var AiRouterCardController = class {
189
197
  keys: void 0,
190
198
  activeKeyId: void 0,
191
199
  loginMode: "both",
200
+ passwordLogin: true,
192
201
  browserLoginUrl: void 0,
193
202
  browserLoginPending: false
194
203
  });
@@ -1016,12 +1025,15 @@ function removeStaleHeroGreeting(doc) {
1016
1025
 
1017
1026
  // src/client/daily-card.ts
1018
1027
  var DAILY_CARD_STYLE_ID = "dcu-daily-card-style";
1028
+ var DAILY_MAIN_FONT_SIZE_MAX = 64;
1029
+ var DAILY_MAIN_FONT_SIZE_MIN = 24;
1019
1030
  var DAILY_CARD_STYLE = `
1020
1031
  [data-dcu-daily-card]{box-sizing:border-box;display:flex;flex-direction:column;align-items:center;width:100%;max-width:min(880px,100%);margin:6px auto 44px;cursor:pointer;user-select:none;text-align:center}
1021
1032
  [data-dcu-daily-prefix]{display:inline-flex;align-items:center;gap:14px;font-size:20px;line-height:1.4;font-weight:500;letter-spacing:.22em;text-indent:.22em;color:rgba(24,24,27,.35);white-space:nowrap}
1022
1033
  [data-dcu-daily-prefix]::before,[data-dcu-daily-prefix]::after{content:"";display:inline-block;width:44px;height:2px;border-radius:1px;background:currentColor;opacity:.5}
1023
1034
  [data-dcu-daily-prefix][hidden]{display:none}
1024
- [data-dcu-daily-main]{margin-top:6px;font-size:64px;line-height:1.25;font-weight:650;letter-spacing:.005em;background:linear-gradient(100deg,#2f6bf0,#1d4ed8 45%,#0e7fc2);-webkit-background-clip:text;background-clip:text;color:transparent;transition:filter .18s ease}
1035
+ [data-dcu-daily-main]{margin-top:6px;max-width:100%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;font-size:${DAILY_MAIN_FONT_SIZE_MAX}px;line-height:1.25;font-weight:650;letter-spacing:.005em;background:linear-gradient(100deg,#2f6bf0,#1d4ed8 45%,#0e7fc2);-webkit-background-clip:text;background-clip:text;color:transparent;transition:filter .18s ease}
1036
+ [data-dcu-daily-main][data-dcu-daily-truncated]{text-align:left}
1025
1037
  [data-dcu-daily-card]:hover [data-dcu-daily-main]{filter:brightness(1.12)}
1026
1038
  [data-dcu-daily-card]:focus-visible{outline:2px solid var(--dsw-alias-button-info-fill,#4c8dff);outline-offset:4px;border-radius:6px}
1027
1039
  [data-dcu-daily-shuffle]{flex:none;display:inline-flex;align-items:center;justify-content:center;gap:5px;margin-top:10px;padding:3px 10px;border-radius:8px;color:var(--dsw-alias-label-tertiary,#71717a);font-size:13px;line-height:20px;letter-spacing:.08em;text-indent:.08em;cursor:pointer;transition:color .18s ease}
@@ -1060,6 +1072,31 @@ function ensureDailyCardStyle(doc) {
1060
1072
  style.textContent = DAILY_CARD_STYLE;
1061
1073
  doc.head.append(style);
1062
1074
  }
1075
+ function solveDailyMainFontSize(availableWidth, measure) {
1076
+ if (!(availableWidth > 0)) return null;
1077
+ if (measure(DAILY_MAIN_FONT_SIZE_MAX) <= availableWidth) return null;
1078
+ if (measure(DAILY_MAIN_FONT_SIZE_MIN) > availableWidth) return DAILY_MAIN_FONT_SIZE_MIN;
1079
+ let low = DAILY_MAIN_FONT_SIZE_MIN;
1080
+ let high = DAILY_MAIN_FONT_SIZE_MAX;
1081
+ while (high - low > 1) {
1082
+ const mid = Math.floor((low + high) / 2);
1083
+ if (measure(mid) <= availableWidth) low = mid;
1084
+ else high = mid;
1085
+ }
1086
+ return low;
1087
+ }
1088
+ function fitDailyMainText(card) {
1089
+ const main = card.querySelector("[data-dcu-daily-main]");
1090
+ if (main === null) return null;
1091
+ const availableWidth = card.clientWidth;
1092
+ const size = solveDailyMainFontSize(availableWidth, (fontSize) => {
1093
+ main.style.fontSize = `${fontSize}px`;
1094
+ return main.scrollWidth;
1095
+ });
1096
+ main.style.fontSize = size === null ? "" : `${size}px`;
1097
+ main.toggleAttribute("data-dcu-daily-truncated", size === DAILY_MAIN_FONT_SIZE_MIN && main.scrollWidth > availableWidth);
1098
+ return size;
1099
+ }
1063
1100
  function findDailyCardMount(doc) {
1064
1101
  const hero = doc.querySelector('[data-phase="hero"]');
1065
1102
  if (hero === null) return null;
@@ -1068,6 +1105,7 @@ function findDailyCardMount(doc) {
1068
1105
  return headline;
1069
1106
  }
1070
1107
  var modalKeyHandlers = /* @__PURE__ */ new WeakMap();
1108
+ var cardHandles = /* @__PURE__ */ new WeakMap();
1071
1109
  function modalHeadline(item) {
1072
1110
  return item.title !== "" ? item.title : item.content;
1073
1111
  }
@@ -1107,6 +1145,36 @@ function buildCard(doc, initial, date, quotes) {
1107
1145
  shuffle.setAttribute("aria-label", "\u6362\u4E00\u53E5");
1108
1146
  shuffle.append(createRefreshIcon(doc), doc.createTextNode("\u6362\u4E00\u53E5"));
1109
1147
  root.append(prefix, main, shuffle);
1148
+ let lastFitWidth = -1;
1149
+ let fitFrame;
1150
+ const fit = (reflow = false) => {
1151
+ const width = root.clientWidth;
1152
+ if (!reflow && width === lastFitWidth) return;
1153
+ lastFitWidth = width;
1154
+ fitDailyMainText(root);
1155
+ };
1156
+ const resizeObserver = typeof ResizeObserver === "function" ? new ResizeObserver(() => {
1157
+ if (fitFrame !== void 0) return;
1158
+ fitFrame = window.requestAnimationFrame(() => {
1159
+ fitFrame = void 0;
1160
+ fit();
1161
+ });
1162
+ }) : null;
1163
+ if (resizeObserver !== null) resizeObserver.observe(root);
1164
+ void doc.fonts?.ready.then(() => {
1165
+ fit(true);
1166
+ }).catch(() => {
1167
+ });
1168
+ cardHandles.set(root, {
1169
+ fit,
1170
+ dispose: () => {
1171
+ if (resizeObserver !== null) resizeObserver.disconnect();
1172
+ if (fitFrame !== void 0) {
1173
+ window.cancelAnimationFrame(fitFrame);
1174
+ fitFrame = void 0;
1175
+ }
1176
+ }
1177
+ });
1110
1178
  let current = initial;
1111
1179
  const applyItem = (next) => {
1112
1180
  current = next;
@@ -1114,6 +1182,7 @@ function buildCard(doc, initial, date, quotes) {
1114
1182
  prefix.hidden = next.subtitle === "";
1115
1183
  main.textContent = next.content;
1116
1184
  root.setAttribute("aria-label", `\u6BCF\u65E5\u4E00\u53E5\uFF1A${modalHeadline(next)}`);
1185
+ fit(true);
1117
1186
  };
1118
1187
  applyItem(initial);
1119
1188
  root.addEventListener("click", (e) => {
@@ -1228,12 +1297,16 @@ function decorateDailyCard(doc, quotes) {
1228
1297
  removeStaleDailyCard(doc);
1229
1298
  const card = buildCard(doc, view, view.date, quotes);
1230
1299
  parent.insertBefore(card, anchor);
1300
+ cardHandles.get(card)?.fit(true);
1231
1301
  }).catch(() => {
1232
1302
  });
1233
1303
  }
1234
1304
  function removeStaleDailyCard(doc) {
1235
1305
  closeDailyModal(doc);
1236
- for (const stale of Array.from(doc.querySelectorAll("[data-dcu-daily-card]"))) stale.remove();
1306
+ for (const stale of Array.from(doc.querySelectorAll("[data-dcu-daily-card]"))) {
1307
+ cardHandles.get(stale)?.dispose();
1308
+ stale.remove();
1309
+ }
1237
1310
  }
1238
1311
  function observeHeroDecorations(doc, quotes) {
1239
1312
  if (doc.head === null) return () => {
@@ -1450,9 +1523,10 @@ function LoginGateModal(props) {
1450
1523
  }, [props.browserLoginUrl]);
1451
1524
  const showBrowserLogin = props.loginMode !== "form";
1452
1525
  const waiting = showBrowserLogin && props.browserLoginPending;
1453
- const formVisible = props.loginMode === "form" || formOpen;
1526
+ const passwordFormOffered = props.passwordLogin || props.loginMode === "form";
1527
+ const formVisible = props.loginMode === "form" || passwordFormOffered && formOpen;
1454
1528
  const noKey = props.lastError?.code === "NO_KEY_ASSIGNED";
1455
- const error = describeLoginError(props.lastError) ?? submitError;
1529
+ const error = (props.lastError?.code === "AUTH_FAILED" && !passwordFormOffered ? "\u5F53\u524D\u7F51\u5173\u5DF2\u542F\u7528\u7EDF\u4E00\u8BA4\u8BC1\uFF0C\u8BF7\u70B9\u51FB\u300C\u4F7F\u7528\u6D4F\u89C8\u5668\u767B\u5F55\u300D\u5B8C\u6210\u767B\u5F55" : describeLoginError(props.lastError)) ?? submitError;
1456
1530
  const loginDisabled = props.loggingIn || username === "" || password === "";
1457
1531
  const submit = () => {
1458
1532
  if (loginDisabled) return;
@@ -1486,7 +1560,7 @@ function LoginGateModal(props) {
1486
1560
  ] }),
1487
1561
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { "data-hiwork-core-brand": true, type: "submit", style: loginDisabled ? loginButtonDisabled : loginButton, disabled: loginDisabled, children: props.loggingIn ? "\u767B\u5F55\u4E2D\u2026" : "\u767B \u5F55" }),
1488
1562
  noKey ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { "data-hiwork-core-secondary": true, type: "button", style: linkButton, onClick: () => void props.scope.set("refreshRequest", { at: (/* @__PURE__ */ new Date()).toISOString() }), children: "\u6211\u5DF2\u5206\u914D KEY\uFF0C\u5237\u65B0" }) : null
1489
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { "data-hiwork-core-secondary": true, "data-hiwork-core-form-toggle": true, type: "button", style: toggleStyle, onClick: () => setFormOpen(true), children: props.loginMode === "browser" ? "\u6D4F\u89C8\u5668\u767B\u5F55\u4E0D\u53EF\u7528\uFF1F\u4F7F\u7528\u8D26\u53F7\u5BC6\u7801\u767B\u5F55" : "\u4F7F\u7528\u8D26\u53F7\u5BC6\u7801\u767B\u5F55" });
1563
+ ] }) : !passwordFormOffered ? null : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { "data-hiwork-core-secondary": true, "data-hiwork-core-form-toggle": true, type: "button", style: toggleStyle, onClick: () => setFormOpen(true), children: props.loginMode === "browser" ? "\u6D4F\u89C8\u5668\u767B\u5F55\u4E0D\u53EF\u7528\uFF1F\u4F7F\u7528\u8D26\u53F7\u5BC6\u7801\u767B\u5F55" : "\u4F7F\u7528\u8D26\u53F7\u5BC6\u7801\u767B\u5F55" });
1490
1564
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: overlayStyle, children: [
1491
1565
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(OceanWaves, {}),
1492
1566
  /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("main", { id: "hiwork-core-frame", "data-hiwork-core-frame": true, style: contentFrameStyle, children: [
@@ -1553,7 +1627,7 @@ function mountLoginGate(scope, storeCredential) {
1553
1627
  root = (0, import_client.createRoot)(gateContainer);
1554
1628
  if (appRoot !== null) appRoot.inert = true;
1555
1629
  }
1556
- root.render(/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(LoginGateModal, { scope, storeCredential, lastError: value?.lastError, loggingIn: value?.pendingLogin !== void 0, initialUsername: value?.username, initialRememberPassword: value?.rememberPassword === true, ...browserLoginViewOf(value) }));
1630
+ root.render(/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(LoginGateModal, { scope, storeCredential, lastError: value?.lastError, loggingIn: value?.pendingLogin !== void 0, initialUsername: value?.username, initialRememberPassword: value?.rememberPassword === true, passwordLogin: passwordLoginOf(value), ...browserLoginViewOf(value) }));
1557
1631
  };
1558
1632
  render();
1559
1633
  const dispose = scope.subscribe(() => render());
@@ -2120,8 +2194,15 @@ function rfc3339Of(time) {
2120
2194
  const date = new Date(time);
2121
2195
  return Number.isNaN(date.getTime()) ? void 0 : date.toISOString();
2122
2196
  }
2123
- function selectSessionCost(owner) {
2124
- return sessionCostMatchOf(owner.turn.data.get("turn-tail"));
2197
+ function closingTurnTailData(snapshot, messageId) {
2198
+ for (const key of snapshot.order) {
2199
+ const node = snapshot.nodes.get(key);
2200
+ if (node === void 0 || node.kind !== "turn-tail") continue;
2201
+ const data = node.data;
2202
+ if (data === void 0) continue;
2203
+ if (data.closing?.finalNode?.messageId === messageId) return data;
2204
+ }
2205
+ return void 0;
2125
2206
  }
2126
2207
  function sessionCostKey(sessionId, match) {
2127
2208
  return `${sessionId}\0${match.turn}\0${JSON.stringify(match.items)}`;
@@ -2178,30 +2259,35 @@ var SessionCostStore = class {
2178
2259
  }
2179
2260
  };
2180
2261
  var costStyle = {
2262
+ // 字号/颜色与同一行的「用量」「用时」胶囊同源变量,保证在动作行里不显得突兀。
2181
2263
  color: textTertiary,
2182
- fontSize: 12,
2264
+ fontSize: "var(--dsh-content-font-size-secondary, 13px)",
2183
2265
  lineHeight: "18px",
2184
2266
  whiteSpace: "nowrap",
2185
2267
  cursor: "default"
2186
2268
  };
2187
- function SessionCost({ matched, sessionId, store }) {
2188
- const key = sessionCostKey(sessionId, matched);
2189
- const [result, setResult] = (0, import_react6.useState)(void 0);
2269
+ function SessionCost({ messageId, sessionId, useChat, store }) {
2270
+ const data = useChat((snapshot) => closingTurnTailData(snapshot, messageId));
2271
+ const match = sessionCostMatchOf(data);
2272
+ const key = match === null ? null : sessionCostKey(sessionId, match);
2273
+ const [shown, setShown] = (0, import_react6.useState)(void 0);
2190
2274
  (0, import_react6.useEffect)(() => {
2275
+ if (match === null || key === null) return;
2191
2276
  let disposed = false;
2192
2277
  const controller = new AbortController();
2193
- void store.estimate({ sessionId, turn: matched.turn, items: matched.items, signal: controller.signal }).then((value) => {
2194
- if (!disposed) setResult(value);
2278
+ void store.estimate({ sessionId, turn: match.turn, items: match.items, signal: controller.signal }).then((value) => {
2279
+ if (!disposed) setShown({ key, result: value });
2195
2280
  }).catch(() => {
2196
- if (!disposed) setResult(void 0);
2281
+ if (!disposed) setShown({ key, result: void 0 });
2197
2282
  });
2198
2283
  return () => {
2199
2284
  disposed = true;
2200
2285
  controller.abort();
2201
2286
  };
2202
2287
  }, [store, key]);
2203
- if (result === void 0) return null;
2204
- const estimated = !result.channelResolved || matched.ambiguous;
2288
+ if (match === null || key === null || shown === void 0 || shown.key !== key || shown.result === void 0) return null;
2289
+ const result = shown.result;
2290
+ const estimated = !result.channelResolved || match.ambiguous;
2205
2291
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: costStyle, "data-hiwork-session-cost": estimated ? "estimated" : "exact", title: SESSION_COST_HINT, children: `\u2248 ${formatYuan(result.cost)}${estimated ? SESSION_COST_ESTIMATED_SUFFIX : ""}` });
2206
2292
  }
2207
2293
 
@@ -2771,9 +2857,10 @@ function apply(ctx) {
2771
2857
  label: () => "\u7528\u91CF\u4E0E\u6210\u672C",
2772
2858
  inject: () => ({ hooks: controller.inject().hooks, usage })
2773
2859
  }, UsageSection));
2774
- ctx.slots.inject("conversation.chat.turnTail", () => ctx.slots.register({
2775
- name: "conversation.chat.turnTail",
2776
- select: selectSessionCost,
2860
+ ctx.slots.inject("conversation.chat.assistant-actions", () => ctx.slots.register({
2861
+ name: "conversation.chat.assistant-actions",
2862
+ id: "hiwork-session-cost",
2863
+ order: 20,
2777
2864
  inject: () => ({ store: sessionCost })
2778
2865
  }, SessionCost));
2779
2866
  ctx.effect(() => observeAccountChange(controller.inject().hooks.aiRouterCard, () => {
package/lib/index.js CHANGED
@@ -3137,7 +3137,7 @@ function isUnloading(ctx) {
3137
3137
  }
3138
3138
 
3139
3139
  // src/version.ts
3140
- var PKG_VERSION = false ? "dev" : "0.0.8";
3140
+ var PKG_VERSION = false ? "dev" : "0.0.10";
3141
3141
 
3142
3142
  // src/ai-router-client.ts
3143
3143
  var AUTH_AUTHORIZE_PATH = "/api/auth/authorize";
@@ -3766,6 +3766,7 @@ async function completeBrowserLogin(input, opts) {
3766
3766
  var DEFAULT_LOGIN_MODE = "both";
3767
3767
  var CLIENT_POLICY_PATH = "/api/client/policy";
3768
3768
  var CLIENT_POLICY_TIMEOUT_MS = 5e3;
3769
+ var DEFAULT_PASSWORD_LOGIN = true;
3769
3770
  function buildClientPolicyURL(opts) {
3770
3771
  const baseURL = typeof opts.baseURL === "string" ? opts.baseURL.trim().replace(/\/+$/, "") : "";
3771
3772
  if (baseURL === "") return void 0;
@@ -3783,6 +3784,11 @@ function parseLoginMode(payload) {
3783
3784
  const value = payload.loginMode;
3784
3785
  return value === "browser" || value === "form" || value === "both" ? value : void 0;
3785
3786
  }
3787
+ function parsePasswordLogin(payload) {
3788
+ if (typeof payload !== "object" || payload === null) return DEFAULT_PASSWORD_LOGIN;
3789
+ const value = payload.passwordLogin;
3790
+ return typeof value === "boolean" ? value : DEFAULT_PASSWORD_LOGIN;
3791
+ }
3786
3792
  async function fetchClientPolicy(opts) {
3787
3793
  const url = buildClientPolicyURL(opts);
3788
3794
  if (url === void 0) return void 0;
@@ -3800,7 +3806,7 @@ async function fetchClientPolicy(opts) {
3800
3806
  return void 0;
3801
3807
  }
3802
3808
  const loginMode = parseLoginMode(payload);
3803
- return loginMode === void 0 ? void 0 : { loginMode };
3809
+ return loginMode === void 0 ? void 0 : { loginMode, passwordLogin: parsePasswordLogin(payload) };
3804
3810
  } catch {
3805
3811
  return void 0;
3806
3812
  }
@@ -4422,6 +4428,7 @@ var Config = Schema.object({
4422
4428
  browserLogin: Schema.object({ authorizeUrl: Schema.string().required(), startedAt: Schema.string().required() }).default(void 0),
4423
4429
  // 三值枚举:非法值写入直接被 schema 拒绝(客户端还会再归一一次,双保险)。
4424
4430
  loginMode: Schema.union([Schema.const("browser"), Schema.const("form"), Schema.const("both")]),
4431
+ passwordLogin: Schema.boolean(),
4425
4432
  budgets: Schema.array(Schema.object({
4426
4433
  model_name: Schema.string().required(),
4427
4434
  total_budget: Schema.number(),
@@ -4593,6 +4600,7 @@ function registerIpcMessageHandler(ports) {
4593
4600
  }
4594
4601
  async function projectLoginMode(opts) {
4595
4602
  let loginMode = DEFAULT_LOGIN_MODE;
4603
+ let passwordLogin = DEFAULT_PASSWORD_LOGIN;
4596
4604
  try {
4597
4605
  const baseURL = opts.baseURL;
4598
4606
  if (baseURL !== void 0 && baseURL !== "") {
@@ -4605,13 +4613,16 @@ async function projectLoginMode(opts) {
4605
4613
  deviceId: device.deviceId,
4606
4614
  ...opts.timeoutMs === void 0 ? {} : { timeoutMs: opts.timeoutMs }
4607
4615
  });
4608
- if (policy !== void 0) loginMode = policy.loginMode;
4616
+ if (policy !== void 0) {
4617
+ loginMode = policy.loginMode;
4618
+ passwordLogin = policy.passwordLogin;
4619
+ }
4609
4620
  }
4610
4621
  } catch (error) {
4611
4622
  opts.log?.(`hiwork-core: \u5BA2\u6237\u7AEF\u7B56\u7565\u62C9\u53D6\u5931\u8D25\uFF0C\u767B\u5F55\u65B9\u5F0F\u56DE\u9000 both\uFF08${errorName(error)}\uFF09`);
4612
4623
  }
4613
4624
  try {
4614
- await opts.state.patch({ set: { loginMode } });
4625
+ await opts.state.patch({ set: { loginMode, passwordLogin } });
4615
4626
  } catch (error) {
4616
4627
  opts.log?.(`hiwork-core: \u5BA2\u6237\u7AEF\u7B56\u7565\u5199\u5165\u5931\u8D25\uFF08${errorName(error)}\uFF09`);
4617
4628
  }
@@ -49,6 +49,11 @@ export interface AiRouterCardSettings {
49
49
  keys?: GatewayKeyView[];
50
50
  /** 网关策略下发的登录方式;缺省或非法值一律按 `both`。 */
51
51
  loginMode?: LoginMode;
52
+ /**
53
+ * 网关策略下发:账号密码直连表单是否仍是受支持的登录方式。统一认证生效后网关报
54
+ * false(密码已由 IAM 持有,直连表单必然失败);缺省/非法一律按 true 展示入口。
55
+ */
56
+ passwordLogin?: boolean;
52
57
  /** 客户端写:请求 Host 发起浏览器授权登录(Host 完成后清掉本字段)。 */
53
58
  browserLoginRequest?: {
54
59
  at: string;
@@ -91,6 +96,8 @@ export interface AiRouterCardState {
91
96
  activeKeyId: string | undefined;
92
97
  /** 登录方式(投影后永不为 undefined;缺省/非法值归一为 both)。 */
93
98
  loginMode: LoginMode;
99
+ /** 账号密码表单是否仍是受支持的入口(缺省 true:绝不收起可用入口)。 */
100
+ passwordLogin: boolean;
94
101
  /** 授权页 URL:有值即等待浏览器授权中。 */
95
102
  browserLoginUrl: string | undefined;
96
103
  /** 是否等待浏览器授权(含请求已发出、URL 未回写的窗口期)。 */
@@ -135,6 +142,12 @@ export declare function authorizeUrlOf(value: AiRouterCardSettings | undefined):
135
142
  * 后一种窗口期也算等待,避免用户重复点击发起第二次授权。
136
143
  */
137
144
  export declare function browserLoginPendingOf(value: AiRouterCardSettings | undefined): boolean;
145
+ /**
146
+ * 账号密码直连表单是否仍是受支持的登录方式(网关策略下发)。
147
+ * 只有显式 false 才收起入口——缺省 / 非法值一律按 true(fail-open:旧网关不返回该
148
+ * 字段时不能把用户挡在密码入口之外)。
149
+ */
150
+ export declare function passwordLoginOf(value: AiRouterCardSettings | undefined): boolean;
138
151
  /**
139
152
  * 浏览器登录的投影视图(登录门禁与卡片共用):`form` 模式下一律不暴露浏览器登录
140
153
  * 入口——连残留的授权 URL 也一并抹掉,避免渲染层对旧 URL 再 `window.open` 一次。
@@ -10,6 +10,11 @@
10
10
  * 任何失败都不出错误 UI、不打断主流程。问候语轮换(decorateHeroGreeting)不受影响、
11
11
  * 照常每帧执行。
12
12
  *
13
+ * 主行恒为单行:CSS 侧 nowrap 打底,字号按卡片可用宽度二分收敛(见
14
+ * solveDailyMainFontSize)——条目越长字越小,绝不折行、也不撑破卡片;下限按「池内
15
+ * 最长条目(24 字)在常规窗口宽度下不被截断」取值,低到下限仍放不下的极端条目才截断
16
+ * 显示,不会把两端一起裁掉。
17
+ *
13
18
  * dcu 标记与 hiwork-dsh-ui 保持同名:两插件同环境共存时互认对方已挂的卡片,
14
19
  * decorateDailyCard 的幂等检查只会让先挂者生效,不会双挂载。
15
20
  */
@@ -20,8 +25,34 @@ export interface DailyQuoteSource {
20
25
  getRandom(excludeId?: number): Promise<DailyQuoteView | null>;
21
26
  }
22
27
  export declare const DAILY_CARD_STYLE_ID = "dcu-daily-card-style";
23
- export declare const DAILY_CARD_STYLE = "\n[data-dcu-daily-card]{box-sizing:border-box;display:flex;flex-direction:column;align-items:center;width:100%;max-width:min(880px,100%);margin:6px auto 44px;cursor:pointer;user-select:none;text-align:center}\n[data-dcu-daily-prefix]{display:inline-flex;align-items:center;gap:14px;font-size:20px;line-height:1.4;font-weight:500;letter-spacing:.22em;text-indent:.22em;color:rgba(24,24,27,.35);white-space:nowrap}\n[data-dcu-daily-prefix]::before,[data-dcu-daily-prefix]::after{content:\"\";display:inline-block;width:44px;height:2px;border-radius:1px;background:currentColor;opacity:.5}\n[data-dcu-daily-prefix][hidden]{display:none}\n[data-dcu-daily-main]{margin-top:6px;font-size:64px;line-height:1.25;font-weight:650;letter-spacing:.005em;background:linear-gradient(100deg,#2f6bf0,#1d4ed8 45%,#0e7fc2);-webkit-background-clip:text;background-clip:text;color:transparent;transition:filter .18s ease}\n[data-dcu-daily-card]:hover [data-dcu-daily-main]{filter:brightness(1.12)}\n[data-dcu-daily-card]:focus-visible{outline:2px solid var(--dsw-alias-button-info-fill,#4c8dff);outline-offset:4px;border-radius:6px}\n[data-dcu-daily-shuffle]{flex:none;display:inline-flex;align-items:center;justify-content:center;gap:5px;margin-top:10px;padding:3px 10px;border-radius:8px;color:var(--dsw-alias-label-tertiary,#71717a);font-size:13px;line-height:20px;letter-spacing:.08em;text-indent:.08em;cursor:pointer;transition:color .18s ease}\n[data-dcu-daily-shuffle] svg{flex:none;width:13px;height:13px;transition:transform .45s ease}\n[data-dcu-daily-shuffle]:hover svg{transform:rotate(360deg)}\n[data-dcu-daily-shuffle]:hover{color:var(--dsw-alias-label-secondary,#3f3f46)}\n[data-dcu-daily-shuffle]:focus-visible{outline:2px solid var(--dsw-alias-button-info-fill,#4c8dff);outline-offset:2px}\nbody[data-ds-dark-theme] [data-dcu-daily-prefix]{color:rgba(242,244,248,.38)}\nbody[data-ds-dark-theme] [data-dcu-daily-main]{background:linear-gradient(100deg,#6b9bff,#3d7bff 45%,#4cc3ff);-webkit-background-clip:text;background-clip:text;color:transparent}\nbody[data-ds-dark-theme] [data-dcu-daily-shuffle]{color:var(--dsw-alias-label-tertiary,#9ba3b4)}\nbody[data-ds-dark-theme] [data-dcu-daily-shuffle]:hover{color:var(--dsw-alias-label-secondary,#a6adb8)}\n[data-dcu-daily-modal]{position:fixed;inset:0;z-index:2147483000;display:flex;align-items:center;justify-content:center;padding:24px;background:rgba(15,17,23,.55)}\n[data-dcu-daily-modal-box]{box-sizing:border-box;width:min(620px,100%);max-height:88vh;overflow:auto;border-radius:18px;border:1px solid var(--dsw-alias-border-subtle,rgba(24,24,27,.12));background:var(--dsw-alias-background-primary,#ffffff);box-shadow:0 24px 64px rgba(0,0,0,.28);padding:24px 28px;color:var(--dsw-alias-label-primary,#18181b)}\n[data-dcu-daily-modal-meta]{display:flex;align-items:center;justify-content:space-between;gap:12px;color:var(--dsw-alias-label-tertiary,#71717a);font-size:12.5px;line-height:18px;letter-spacing:.02em}\n[data-dcu-daily-modal-title]{margin-top:14px;font-size:24px;line-height:1.45;font-weight:650;color:var(--dsw-alias-label-primary,#18181b)}\n[data-dcu-daily-modal-body]{margin-top:16px;color:var(--dsw-alias-label-secondary,#3f3f46);font-size:14.5px;line-height:1.95}\n[data-dcu-daily-modal-body] > p{margin:0 0 10px}\n[data-dcu-daily-modal-body] > p:last-child{margin-bottom:0}\n[data-dcu-daily-modal-close]{appearance:none;margin-top:20px;padding:7px 18px;border:1px solid var(--dsw-alias-border-strong,rgba(24,24,27,.2));border-radius:9px;background:var(--dsw-alias-button-secondary-fill,transparent);color:var(--dsw-alias-label-secondary,#3f3f46);font-size:13px;line-height:20px;cursor:pointer}\n[data-dcu-daily-modal-close]:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(24,24,27,.06))}\n[data-dcu-daily-modal-close]:focus-visible{outline:2px solid var(--dsw-alias-button-info-fill,#4c8dff);outline-offset:2px}\nbody[data-ds-dark-theme] [data-dcu-daily-modal]{background:rgba(0,0,0,.62)}\nbody[data-ds-dark-theme] [data-dcu-daily-modal-box]{border-color:var(--dsw-alias-border-subtle,rgba(255,255,255,.12));background:var(--dsw-alias-background-primary,#10131b);color:var(--dsw-alias-label-primary,#f2f4f8)}\nbody[data-ds-dark-theme] [data-dcu-daily-modal-meta]{color:var(--dsw-alias-label-tertiary,#9ba3b4)}\nbody[data-ds-dark-theme] [data-dcu-daily-modal-title]{color:var(--dsw-alias-label-primary,#f2f4f8)}\nbody[data-ds-dark-theme] [data-dcu-daily-modal-body]{color:var(--dsw-alias-label-secondary,#a6adb8)}\nbody[data-ds-dark-theme] [data-dcu-daily-modal-close]{border-color:var(--dsw-alias-border-subtle,rgba(255,255,255,.16));color:var(--dsw-alias-label-secondary,#a6adb8)}\nbody[data-ds-dark-theme] [data-dcu-daily-modal-close]:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(255,255,255,.08))}\n@media (prefers-reduced-motion: reduce){[data-dcu-daily-card] [data-dcu-daily-main],[data-dcu-daily-shuffle],[data-dcu-daily-shuffle] svg,[data-dcu-daily-modal-box]{transition:none}}\n";
28
+ /**
29
+ * 主行字号:基准(一行放得下时)与下限(再长也不缩到不可读,剩余溢出走截断)。
30
+ * 下限由内容池约束:最长条目 24 字在 690px 卡片宽度下需要 ≤28px,取 24px 留出余量
31
+ * (更窄的窗口或更长的条目才走省略号)。
32
+ */
33
+ export declare const DAILY_MAIN_FONT_SIZE_MAX = 64;
34
+ export declare const DAILY_MAIN_FONT_SIZE_MIN = 24;
35
+ export declare const DAILY_CARD_STYLE = "\n[data-dcu-daily-card]{box-sizing:border-box;display:flex;flex-direction:column;align-items:center;width:100%;max-width:min(880px,100%);margin:6px auto 44px;cursor:pointer;user-select:none;text-align:center}\n[data-dcu-daily-prefix]{display:inline-flex;align-items:center;gap:14px;font-size:20px;line-height:1.4;font-weight:500;letter-spacing:.22em;text-indent:.22em;color:rgba(24,24,27,.35);white-space:nowrap}\n[data-dcu-daily-prefix]::before,[data-dcu-daily-prefix]::after{content:\"\";display:inline-block;width:44px;height:2px;border-radius:1px;background:currentColor;opacity:.5}\n[data-dcu-daily-prefix][hidden]{display:none}\n[data-dcu-daily-main]{margin-top:6px;max-width:100%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;font-size:64px;line-height:1.25;font-weight:650;letter-spacing:.005em;background:linear-gradient(100deg,#2f6bf0,#1d4ed8 45%,#0e7fc2);-webkit-background-clip:text;background-clip:text;color:transparent;transition:filter .18s ease}\n[data-dcu-daily-main][data-dcu-daily-truncated]{text-align:left}\n[data-dcu-daily-card]:hover [data-dcu-daily-main]{filter:brightness(1.12)}\n[data-dcu-daily-card]:focus-visible{outline:2px solid var(--dsw-alias-button-info-fill,#4c8dff);outline-offset:4px;border-radius:6px}\n[data-dcu-daily-shuffle]{flex:none;display:inline-flex;align-items:center;justify-content:center;gap:5px;margin-top:10px;padding:3px 10px;border-radius:8px;color:var(--dsw-alias-label-tertiary,#71717a);font-size:13px;line-height:20px;letter-spacing:.08em;text-indent:.08em;cursor:pointer;transition:color .18s ease}\n[data-dcu-daily-shuffle] svg{flex:none;width:13px;height:13px;transition:transform .45s ease}\n[data-dcu-daily-shuffle]:hover svg{transform:rotate(360deg)}\n[data-dcu-daily-shuffle]:hover{color:var(--dsw-alias-label-secondary,#3f3f46)}\n[data-dcu-daily-shuffle]:focus-visible{outline:2px solid var(--dsw-alias-button-info-fill,#4c8dff);outline-offset:2px}\nbody[data-ds-dark-theme] [data-dcu-daily-prefix]{color:rgba(242,244,248,.38)}\nbody[data-ds-dark-theme] [data-dcu-daily-main]{background:linear-gradient(100deg,#6b9bff,#3d7bff 45%,#4cc3ff);-webkit-background-clip:text;background-clip:text;color:transparent}\nbody[data-ds-dark-theme] [data-dcu-daily-shuffle]{color:var(--dsw-alias-label-tertiary,#9ba3b4)}\nbody[data-ds-dark-theme] [data-dcu-daily-shuffle]:hover{color:var(--dsw-alias-label-secondary,#a6adb8)}\n[data-dcu-daily-modal]{position:fixed;inset:0;z-index:2147483000;display:flex;align-items:center;justify-content:center;padding:24px;background:rgba(15,17,23,.55)}\n[data-dcu-daily-modal-box]{box-sizing:border-box;width:min(620px,100%);max-height:88vh;overflow:auto;border-radius:18px;border:1px solid var(--dsw-alias-border-subtle,rgba(24,24,27,.12));background:var(--dsw-alias-background-primary,#ffffff);box-shadow:0 24px 64px rgba(0,0,0,.28);padding:24px 28px;color:var(--dsw-alias-label-primary,#18181b)}\n[data-dcu-daily-modal-meta]{display:flex;align-items:center;justify-content:space-between;gap:12px;color:var(--dsw-alias-label-tertiary,#71717a);font-size:12.5px;line-height:18px;letter-spacing:.02em}\n[data-dcu-daily-modal-title]{margin-top:14px;font-size:24px;line-height:1.45;font-weight:650;color:var(--dsw-alias-label-primary,#18181b)}\n[data-dcu-daily-modal-body]{margin-top:16px;color:var(--dsw-alias-label-secondary,#3f3f46);font-size:14.5px;line-height:1.95}\n[data-dcu-daily-modal-body] > p{margin:0 0 10px}\n[data-dcu-daily-modal-body] > p:last-child{margin-bottom:0}\n[data-dcu-daily-modal-close]{appearance:none;margin-top:20px;padding:7px 18px;border:1px solid var(--dsw-alias-border-strong,rgba(24,24,27,.2));border-radius:9px;background:var(--dsw-alias-button-secondary-fill,transparent);color:var(--dsw-alias-label-secondary,#3f3f46);font-size:13px;line-height:20px;cursor:pointer}\n[data-dcu-daily-modal-close]:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(24,24,27,.06))}\n[data-dcu-daily-modal-close]:focus-visible{outline:2px solid var(--dsw-alias-button-info-fill,#4c8dff);outline-offset:2px}\nbody[data-ds-dark-theme] [data-dcu-daily-modal]{background:rgba(0,0,0,.62)}\nbody[data-ds-dark-theme] [data-dcu-daily-modal-box]{border-color:var(--dsw-alias-border-subtle,rgba(255,255,255,.12));background:var(--dsw-alias-background-primary,#10131b);color:var(--dsw-alias-label-primary,#f2f4f8)}\nbody[data-ds-dark-theme] [data-dcu-daily-modal-meta]{color:var(--dsw-alias-label-tertiary,#9ba3b4)}\nbody[data-ds-dark-theme] [data-dcu-daily-modal-title]{color:var(--dsw-alias-label-primary,#f2f4f8)}\nbody[data-ds-dark-theme] [data-dcu-daily-modal-body]{color:var(--dsw-alias-label-secondary,#a6adb8)}\nbody[data-ds-dark-theme] [data-dcu-daily-modal-close]{border-color:var(--dsw-alias-border-subtle,rgba(255,255,255,.16));color:var(--dsw-alias-label-secondary,#a6adb8)}\nbody[data-ds-dark-theme] [data-dcu-daily-modal-close]:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(255,255,255,.08))}\n@media (prefers-reduced-motion: reduce){[data-dcu-daily-card] [data-dcu-daily-main],[data-dcu-daily-shuffle],[data-dcu-daily-shuffle] svg,[data-dcu-daily-modal-box]{transition:none}}\n";
24
36
  export declare function ensureDailyCardStyle(doc: Document): void;
37
+ /**
38
+ * 主行字号求解(纯函数,measure 注入以便测试):基准字号一行放得下 → null(调用方清掉
39
+ * 内联样式,回到 CSS 基准);否则二分出「放得下」的最大整数字号;低到下限仍放不下时按
40
+ * 下限返回(剩余溢出交给 CSS 截断兜底)。availableWidth 非正(无布局信息:容器隐藏 /
41
+ * jsdom)→ null,不动任何样式。
42
+ *
43
+ * 二分成立的前提是「文本宽度随字号单调递增」:字号与 em 单位字距同比例缩放,故对中文、
44
+ * 西文、混排均成立。
45
+ */
46
+ export declare function solveDailyMainFontSize(availableWidth: number, measure: (fontSize: number) => number): number | null;
47
+ /**
48
+ * 把一张卡片的主行字号收敛到「一行放得下」:可用宽度取卡片内容盒宽度(卡片宽度由 CSS
49
+ * 的 width/max-width 决定,主行溢出不会撑大它),文本宽度取主行 scrollWidth(被截断时
50
+ * 仍是完整文本宽度,故二分不受裁剪影响)。返回最终写入的字号;null = 未写内联样式。
51
+ *
52
+ * 下限仍放不下的极端条目打上 `data-dcu-daily-truncated`:居中溢出会把两端一起裁掉、
53
+ * 看不出被截断,该状态改为左对齐以便省略号落在可见边上。
54
+ */
55
+ export declare function fitDailyMainText(card: HTMLElement): number | null;
25
56
  /**
26
57
  * 探测 hero 页面并把主行插到 headline(后缀稳定,前缀哈希忽略)的「前一个兄弟」位置:
27
58
  * 返回 headline 元素本身作为插入锚点(调用方以 headline.parentElement.insertBefore(card, headline) 落位)。
@@ -1,5 +1,5 @@
1
1
  import type { InjectFace, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
2
- import type { TurnTailChatData, TurnTailOwnerProps } from '@deepseek-ai/dsh-client-ui-chat/client';
2
+ import type { ChatSnapshot, TurnTailChatData } from '@deepseek-ai/dsh-client-ui-chat/client';
3
3
  /** 计价端点名;必须与 Host `src/usage-rpc.ts` 的 `USAGE_RPC_ENDPOINTS` 一致(由测试锁定)。 */
4
4
  export declare const SESSION_COST_ENDPOINT = "estimate";
5
5
  /** 缓存 TTL:5 分钟(与设置页取数一致;同一轮的金额不会随滚动反复请求)。 */
@@ -50,11 +50,19 @@ export interface SessionCostRpcPort {
50
50
  */
51
51
  export declare function sessionCostMatchOf(data: TurnTailChatData | undefined): SessionCostMatch | null;
52
52
  /**
53
- * 链槽 `conversation.chat.turnTail` 的路由选择器:只有该轮拿得到用量时才占用位置。
54
- * @param owner - turn-tail owner(turn / seq / openFile)。
55
- * @returns 计价项;取不到用量时 null(框架不挂载组件)。
53
+ * 在该会话的 Chat 快照里,按「收尾消息 id」找到该轮的 turn-tail 数据。
54
+ *
55
+ * 为什么按 messageId 反查:本模块挂在 `conversation.chat.assistant-actions`,owner 只有
56
+ * `messageId`;而用量挂在同一轮的 `'turn-tail'` 节点上。ui-chat 正是拿
57
+ * `closing.finalNode.messageId` 去 renderSlot 这个槽的(见 `TurnTailNodeView`),
58
+ * 所以两个 id 是同一个值——不需要再猜轮号,也不会跨轮串号。
59
+ *
60
+ * 该轮尚未收尾、或节点不在已加载窗口内 → 返回 undefined(不显示金额)。
61
+ * @param snapshot - 当前 Chat 快照(`useChat` 的选择器入参)。
62
+ * @param messageId - 收尾 assistant 消息 id(槽位 owner 下发)。
63
+ * @returns 该轮 turn-tail 数据;找不到时 undefined。
56
64
  */
57
- export declare function selectSessionCost(owner: TurnTailOwnerProps): SessionCostMatch | null;
65
+ export declare function closingTurnTailData(snapshot: ChatSnapshot, messageId: string): TurnTailChatData | undefined;
58
66
  /** 缓存键:会话 + 轮次 + 计价项内容(滚动/重挂载不会重复请求同一轮)。 */
59
67
  export declare function sessionCostKey(sessionId: string, match: Pick<SessionCostMatch, 'turn' | 'items'>): string;
60
68
  /**
@@ -102,18 +110,16 @@ export interface SessionCostFace {
102
110
  store: SessionCostStore;
103
111
  }
104
112
  /**
105
- * 链槽组件 props:owner(turn/seq/openFile)+ 会话标准件(`sessionId`)+ 注入面 + `matched`。
106
- * `sessionId` 由框架下发(ui-session 的 SessionStandardProps),不在这里重复声明。
113
+ * 组件 props:owner(`messageId`)+ 会话标准件(`sessionId` / `useChat`)+ 注入面。
114
+ * `sessionId`(ui-session)与 `useChat`(ui-chat)都由框架下发,不在这里重复声明。
107
115
  */
108
- export type SessionCostProps = PropsRuntime<'conversation.chat.turnTail'> & InjectFace<SessionCostFace> & {
109
- matched: SessionCostMatch;
110
- };
116
+ export type SessionCostProps = PropsRuntime<'conversation.chat.assistant-actions'> & InjectFace<SessionCostFace>;
111
117
  /**
112
- * 渲染一轮的估算金额。
118
+ * 渲染一轮的估算金额(挂在该轮收尾消息的动作行里,与 DSH 的「用量」胶囊同一行)。
113
119
  *
114
120
  * 三种状态:全部计价且渠道已解析、单组合 → `≈ ¥x`;渠道未解析或该轮多组合 →
115
121
  * `≈ ¥x(估算)`;其余(未计价 / 失败 / 加载中)→ **不渲染任何内容**。
116
- * @param props - 链槽 props。
122
+ * @param props - 槽位 props(owner 的 `messageId` + 标准件 `sessionId` / `useChat` + 注入面)。
117
123
  * @returns 金额文本节点,或 null(不影响对话渲染)。
118
124
  */
119
- export declare function SessionCost({ matched, sessionId, store }: SessionCostProps): import("react").JSX.Element | null;
125
+ export declare function SessionCost({ messageId, sessionId, useChat, store }: SessionCostProps): import("react").JSX.Element | null;
@@ -46,8 +46,16 @@ export declare const textInput: CSSProperties;
46
46
  * data-dsh-shell=process.platform;Windows 顶栏与侧栏同色,圆角呼应顶栏,
47
47
  * 其余平台(含无该属性的纯浏览器预览)默认关闭,回到直角。
48
48
  * 内容区根节点类名是 CSS module 哈希前缀(如 wSkVaW_root),语义后缀稳定;
49
- * 用直接子链 `[class*="_centerCol"] > div > [class*="_root"]` 恰好命中会话区
50
- * 根节点(实测唯一),避免误伤内容区里 10 个嵌套组件的 _root。
49
+ * 命中内容区根节点一律走「中心列 + 直接子链」,避免误伤内容区里 10 个嵌套组件
50
+ * 的 _root。中心列到根节点中间隔几层,两代宿主不同,故规则并存:
51
+ * - `0.1.2-rc.1` 及更早:main 槽直接把会话区根节点渲染进中心列,链路是
52
+ * `centerCol > 槽位 div > _root`;
53
+ * - `0.1.5-rc.1` 起:main 槽先渲染 ConversationPanel,再由它声明的
54
+ * `main.conversation` 子槽渲染根节点,中间多一层 `data-slot` 槽位 div,
55
+ * 旧直接子链静默失配(实测表现:Windows 内容区左上角圆角消失),改用槽位属性定位。
56
+ * 中心列里除了原生会话还有 HiWork 扩展页(feature-center 的 takeover 根节点,
57
+ * 是中心列的直接子元素):同一处「内容区左上角」在两种页面上都要有圆角,否则
58
+ * 切到扩展页又变回直角。
51
59
  * 框架底色同理不能只认 `_frame` 后缀:该后缀被 6 个 CSS module 复用(主框架
52
60
  * AppFrame 之外还有会话右侧快速索引 TurnNavigator、附件图 MessageImage、
53
61
  * 提问面板 QuestionComposer/PlanReviewPanel、只读输入框 SubagentReadOnlyComposer),
@@ -22,7 +22,15 @@ export declare const CLIENT_POLICY_TIMEOUT_MS = 5000;
22
22
  /** 策略响应中本插件消费的部分。 */
23
23
  export interface ClientPolicy {
24
24
  loginMode: LoginMode;
25
+ /**
26
+ * 账号密码直连表单是否仍是受支持的登录方式(网关身份源切到统一认证后为 false)。
27
+ * 缺省(旧网关不返回该字段)/ 非法值一律按 true——绝不因为策略不全就收起可用的
28
+ * 登录入口,把用户锁在门外。
29
+ */
30
+ passwordLogin: boolean;
25
31
  }
32
+ /** fail-open 缺省:旧网关不返回 passwordLogin 时,密码表单仍视作可用。 */
33
+ export declare const DEFAULT_PASSWORD_LOGIN = true;
26
34
  /** 拉取策略所需入参(deviceId 由 `resolveDeviceInfo` 解析,永非空)。 */
27
35
  export interface FetchClientPolicyOptions {
28
36
  /** 网关地址;缺省 / 空串直接返回 undefined(不发起请求)。 */
@@ -56,6 +56,13 @@ export interface Config {
56
56
  * 一律写 `both`(fail-open,绝不把用户锁在门外)。
57
57
  */
58
58
  loginMode?: LoginMode;
59
+ /**
60
+ * 账号密码直连表单是否仍是受支持的登录方式:Host 从同一策略投影。
61
+ * 网关身份源切到统一认证(IAM)后为 false——密码由 IAM 持有,直连表单必然失败,
62
+ * 客户端据此收起密码入口,避免「密码正确却提示密码错误」。缺省 / 拉取失败一律
63
+ * 写 true(fail-open:绝不收起可用入口)。
64
+ */
65
+ passwordLogin?: boolean;
59
66
  budgets?: Array<{
60
67
  model_name: string;
61
68
  total_budget?: number | null;
@@ -140,11 +147,13 @@ export interface IpcCallbackPorts {
140
147
  */
141
148
  export declare function registerIpcMessageHandler(ports: IpcCallbackPorts): () => void;
142
149
  /**
143
- * 拉取客户端策略并把 `loginMode` 投影进 ai-router 命名空间(spec §5.4 / §6.1)。
150
+ * 拉取客户端策略并把 `loginMode` / `passwordLogin` 投影进 ai-router 命名空间
151
+ * (spec §5.4 / §6.1)。
144
152
  *
145
153
  * fail-open 是硬要求:缺 baseURL / 设备信息取不到 / 网络失败 / 响应非法一律投影
146
- * `DEFAULT_LOGIN_MODE`(`both`),**且只写 `loginMode` 一个字段**——不碰 lastError、
147
- * 不碰任何登录请求字段,策略拉取失败绝不影响登录与供给。**本函数永不抛异常**。
154
+ * `DEFAULT_LOGIN_MODE`(`both`)与 `DEFAULT_PASSWORD_LOGIN`(`true`),**且只写这两个
155
+ * 字段**——不碰 lastError、不碰任何登录请求字段,策略拉取失败绝不影响登录与供给。
156
+ * **本函数永不抛异常**。
148
157
  *
149
158
  * @returns 最终投影的登录方式(测试与调用方均可断言)。
150
159
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hiwork-core",
3
- "version": "0.0.8",
3
+ "version": "0.0.10",
4
4
  "type": "module",
5
5
  "description": "HiWork 企业统一登录与模型自动供给插件(ai-router 网关登录,Web 表单,无 CLI 认证)",
6
6
  "license": "UNLICENSED",