hiwork-core 0.0.7 → 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.
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.7";
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";
@@ -3614,6 +3614,9 @@ function setDeviceInfo(info) {
3614
3614
  ...appVersion === "" ? {} : { appVersion }
3615
3615
  };
3616
3616
  }
3617
+ function pushedAppVersion() {
3618
+ return pushedDeviceInfo?.appVersion;
3619
+ }
3617
3620
  function hostName() {
3618
3621
  try {
3619
3622
  return hostname().trim();
@@ -3763,6 +3766,7 @@ async function completeBrowserLogin(input, opts) {
3763
3766
  var DEFAULT_LOGIN_MODE = "both";
3764
3767
  var CLIENT_POLICY_PATH = "/api/client/policy";
3765
3768
  var CLIENT_POLICY_TIMEOUT_MS = 5e3;
3769
+ var DEFAULT_PASSWORD_LOGIN = true;
3766
3770
  function buildClientPolicyURL(opts) {
3767
3771
  const baseURL = typeof opts.baseURL === "string" ? opts.baseURL.trim().replace(/\/+$/, "") : "";
3768
3772
  if (baseURL === "") return void 0;
@@ -3780,6 +3784,11 @@ function parseLoginMode(payload) {
3780
3784
  const value = payload.loginMode;
3781
3785
  return value === "browser" || value === "form" || value === "both" ? value : void 0;
3782
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
+ }
3783
3792
  async function fetchClientPolicy(opts) {
3784
3793
  const url = buildClientPolicyURL(opts);
3785
3794
  if (url === void 0) return void 0;
@@ -3797,7 +3806,7 @@ async function fetchClientPolicy(opts) {
3797
3806
  return void 0;
3798
3807
  }
3799
3808
  const loginMode = parseLoginMode(payload);
3800
- return loginMode === void 0 ? void 0 : { loginMode };
3809
+ return loginMode === void 0 ? void 0 : { loginMode, passwordLogin: parsePasswordLogin(payload) };
3801
3810
  } catch {
3802
3811
  return void 0;
3803
3812
  }
@@ -3938,6 +3947,61 @@ function registerDailyRpc(ctx, deps) {
3938
3947
  };
3939
3948
  }
3940
3949
 
3950
+ // src/app-version-rpc.ts
3951
+ var APP_VERSION_RPC_CHANNEL = "/hiwork-app";
3952
+ var APP_VERSION_RPC_ENDPOINTS = ["version"];
3953
+ var APP_VERSION_GENERIC_FAILURE_MESSAGE = "\u6682\u65F6\u65E0\u6CD5\u83B7\u53D6\u684C\u9762\u7AEF\u7248\u672C";
3954
+ var AppVersionRequestError = class extends Error {
3955
+ constructor(code, message) {
3956
+ super(message);
3957
+ this.code = code;
3958
+ this.name = "AppVersionRequestError";
3959
+ }
3960
+ };
3961
+ function isAppVersionRpcEndpoint(endpoint) {
3962
+ return APP_VERSION_RPC_ENDPOINTS.includes(endpoint);
3963
+ }
3964
+ function isAborted2(signal) {
3965
+ return signal?.aborted === true;
3966
+ }
3967
+ function parseAppVersionPayload(raw) {
3968
+ if (raw === void 0 || raw === null) return;
3969
+ if (typeof raw !== "object" || Array.isArray(raw)) {
3970
+ throw new AppVersionRequestError("bad-request", "\u684C\u9762\u7AEF\u7248\u672C\u8BF7\u6C42\u53C2\u6570\u4E0D\u5408\u6CD5\u3002");
3971
+ }
3972
+ const keys = Object.keys(raw);
3973
+ if (keys.length > 0) throw new AppVersionRequestError("bad-request", `\u684C\u9762\u7AEF\u7248\u672C\u8BF7\u6C42\u5305\u542B\u672A\u77E5\u5B57\u6BB5 '${keys[0]}'\u3002`);
3974
+ }
3975
+ function normalizedVersion(raw) {
3976
+ const version = typeof raw === "string" ? raw.trim() : "";
3977
+ return version === "" ? null : version;
3978
+ }
3979
+ async function handleAppVersionEndpoint(endpoint, rawPayload, deps, signal) {
3980
+ try {
3981
+ if (isAborted2(signal)) return failure2("cancelled", "\u684C\u9762\u7AEF\u7248\u672C\u8BF7\u6C42\u5DF2\u53D6\u6D88\u3002");
3982
+ if (!isAppVersionRpcEndpoint(endpoint)) {
3983
+ throw new AppVersionRequestError("bad-request", `\u672A\u77E5\u7684\u684C\u9762\u7AEF\u7248\u672C\u7AEF\u70B9 '${endpoint}'`);
3984
+ }
3985
+ parseAppVersionPayload(rawPayload);
3986
+ const value = { version: normalizedVersion(deps.appVersion()) };
3987
+ return { ok: true, value };
3988
+ } catch (error) {
3989
+ if (error instanceof AppVersionRequestError) return failure2(error.code, error.message);
3990
+ deps.log?.(`hiwork-core: \u684C\u9762\u7AEF\u7248\u672C RPC '${endpoint}' \u5931\u8D25\uFF08${error instanceof Error ? error.name : typeof error}\uFF09`);
3991
+ return failure2("internal", APP_VERSION_GENERIC_FAILURE_MESSAGE);
3992
+ }
3993
+ }
3994
+ function failure2(code, message) {
3995
+ return { ok: false, error: { code, message, details: {} } };
3996
+ }
3997
+ function registerAppVersionRpc(ctx, deps) {
3998
+ const handler = (endpoint, payload, signal) => handleAppVersionEndpoint(endpoint, payload, deps, signal);
3999
+ const remove = ctx.connection.rpc.handle(APP_VERSION_RPC_CHANNEL, handler, { authority: "loopback" });
4000
+ return async () => {
4001
+ await remove();
4002
+ };
4003
+ }
4004
+
3941
4005
  // src/orchestration.ts
3942
4006
  async function runLogin(opts) {
3943
4007
  const pending = opts.value.pendingLogin;
@@ -4255,7 +4319,7 @@ function summarizeEstimate(results, expected) {
4255
4319
  async function handleEstimateEndpoint(items, deps, signal) {
4256
4320
  const sessionCookie = await deps.resolveSession();
4257
4321
  if (sessionCookie === void 0 || sessionCookie === "") return { ok: true, value: unpriced("no_session") };
4258
- if (isAborted2(signal)) return failure2("cancelled", "\u7528\u91CF\u8BF7\u6C42\u5DF2\u53D6\u6D88\u3002");
4322
+ if (isAborted3(signal)) return failure3("cancelled", "\u7528\u91CF\u8BF7\u6C42\u5DF2\u53D6\u6D88\u3002");
4259
4323
  if (items.length > USAGE_ESTIMATE_MAX_ITEMS) return { ok: true, value: unpriced("too_many_items") };
4260
4324
  const gatewayItems = [];
4261
4325
  for (const item of items) {
@@ -4274,23 +4338,23 @@ async function handleEstimateEndpoint(items, deps, signal) {
4274
4338
  if (gatewayItems.length === 0) return { ok: true, value: unpriced("no_route") };
4275
4339
  const gateway = deps.gateway();
4276
4340
  if (gateway === void 0) {
4277
- return failure2("GATEWAY_UNREACHABLE", "\u7F51\u5173\u5730\u5740\u672A\u914D\u7F6E\uFF0C\u8BF7\u5148\u5728\u300C\u7F51\u5173\u6A21\u578B\u300D\u91CC\u5B8C\u6210\u767B\u5F55");
4341
+ return failure3("GATEWAY_UNREACHABLE", "\u7F51\u5173\u5730\u5740\u672A\u914D\u7F6E\uFF0C\u8BF7\u5148\u5728\u300C\u7F51\u5173\u6A21\u578B\u300D\u91CC\u5B8C\u6210\u767B\u5F55");
4278
4342
  }
4279
- if (isAborted2(signal)) return failure2("cancelled", "\u7528\u91CF\u8BF7\u6C42\u5DF2\u53D6\u6D88\u3002");
4343
+ if (isAborted3(signal)) return failure3("cancelled", "\u7528\u91CF\u8BF7\u6C42\u5DF2\u53D6\u6D88\u3002");
4280
4344
  const response = await gateway.estimateCost(sessionCookie, gatewayItems);
4281
4345
  const results = response?.items;
4282
4346
  if (!Array.isArray(results)) {
4283
4347
  deps.log?.("hiwork-core: \u8BA1\u4EF7 RPC \u6536\u5230\u65E0\u6548\u54CD\u5E94\uFF08items \u4E0D\u662F\u6570\u7EC4\uFF09");
4284
- return failure2("internal", USAGE_GENERIC_FAILURE_MESSAGE);
4348
+ return failure3("internal", USAGE_GENERIC_FAILURE_MESSAGE);
4285
4349
  }
4286
4350
  return { ok: true, value: summarizeEstimate(results, gatewayItems.length) };
4287
4351
  }
4288
- function isAborted2(signal) {
4352
+ function isAborted3(signal) {
4289
4353
  return signal?.aborted === true;
4290
4354
  }
4291
4355
  async function handleUsageEndpoint(endpoint, rawPayload, deps, signal) {
4292
4356
  try {
4293
- if (isAborted2(signal)) return failure2("cancelled", "\u7528\u91CF\u8BF7\u6C42\u5DF2\u53D6\u6D88\u3002");
4357
+ if (isAborted3(signal)) return failure3("cancelled", "\u7528\u91CF\u8BF7\u6C42\u5DF2\u53D6\u6D88\u3002");
4294
4358
  if (!isUsageRpcEndpoint(endpoint)) {
4295
4359
  throw new UsageRequestError("bad-request", `\u672A\u77E5\u7684\u7528\u91CF\u7AEF\u70B9 '${endpoint}'`);
4296
4360
  }
@@ -4298,22 +4362,22 @@ async function handleUsageEndpoint(endpoint, rawPayload, deps, signal) {
4298
4362
  const payload = parseUsagePayload(rawPayload);
4299
4363
  const sessionCookie = await deps.resolveSession();
4300
4364
  if (sessionCookie === void 0 || sessionCookie === "") {
4301
- return failure2("SESSION_EXPIRED", USAGE_SESSION_EXPIRED_MESSAGE);
4365
+ return failure3("SESSION_EXPIRED", USAGE_SESSION_EXPIRED_MESSAGE);
4302
4366
  }
4303
4367
  const gateway = deps.gateway();
4304
4368
  if (gateway === void 0) {
4305
- return failure2("GATEWAY_UNREACHABLE", "\u7F51\u5173\u5730\u5740\u672A\u914D\u7F6E\uFF0C\u8BF7\u5148\u5728\u300C\u7F51\u5173\u6A21\u578B\u300D\u91CC\u5B8C\u6210\u767B\u5F55");
4369
+ return failure3("GATEWAY_UNREACHABLE", "\u7F51\u5173\u5730\u5740\u672A\u914D\u7F6E\uFF0C\u8BF7\u5148\u5728\u300C\u7F51\u5173\u6A21\u578B\u300D\u91CC\u5B8C\u6210\u767B\u5F55");
4306
4370
  }
4307
- if (isAborted2(signal)) return failure2("cancelled", "\u7528\u91CF\u8BF7\u6C42\u5DF2\u53D6\u6D88\u3002");
4371
+ if (isAborted3(signal)) return failure3("cancelled", "\u7528\u91CF\u8BF7\u6C42\u5DF2\u53D6\u6D88\u3002");
4308
4372
  const value = await callEndpoint2(gateway, sessionCookie, endpoint, payload);
4309
4373
  return { ok: true, value };
4310
4374
  } catch (error) {
4311
- if (error instanceof UsageRequestError) return failure2(error.code, error.message);
4375
+ if (error instanceof UsageRequestError) return failure3(error.code, error.message);
4312
4376
  if (error instanceof GatewayError) {
4313
- return failure2(error.code, displayGatewayMessage(error.code, error.message));
4377
+ return failure3(error.code, displayGatewayMessage(error.code, error.message));
4314
4378
  }
4315
4379
  deps.log?.(`hiwork-core: \u7528\u91CF RPC '${endpoint}' \u5931\u8D25\uFF08${error instanceof Error ? error.name : typeof error}\uFF09`);
4316
- return failure2("internal", USAGE_GENERIC_FAILURE_MESSAGE);
4380
+ return failure3("internal", USAGE_GENERIC_FAILURE_MESSAGE);
4317
4381
  }
4318
4382
  }
4319
4383
  async function callEndpoint2(gateway, sessionCookie, endpoint, payload) {
@@ -4332,7 +4396,7 @@ async function callEndpoint2(gateway, sessionCookie, endpoint, payload) {
4332
4396
  return await gateway.getMyKey(sessionCookie, payload.keyId);
4333
4397
  }
4334
4398
  }
4335
- function failure2(code, message) {
4399
+ function failure3(code, message) {
4336
4400
  return { ok: false, error: { code, message, details: {} } };
4337
4401
  }
4338
4402
  function registerUsageRpc(ctx, deps) {
@@ -4364,6 +4428,7 @@ var Config = Schema.object({
4364
4428
  browserLogin: Schema.object({ authorizeUrl: Schema.string().required(), startedAt: Schema.string().required() }).default(void 0),
4365
4429
  // 三值枚举:非法值写入直接被 schema 拒绝(客户端还会再归一一次,双保险)。
4366
4430
  loginMode: Schema.union([Schema.const("browser"), Schema.const("form"), Schema.const("both")]),
4431
+ passwordLogin: Schema.boolean(),
4367
4432
  budgets: Schema.array(Schema.object({
4368
4433
  model_name: Schema.string().required(),
4369
4434
  total_budget: Schema.number(),
@@ -4535,6 +4600,7 @@ function registerIpcMessageHandler(ports) {
4535
4600
  }
4536
4601
  async function projectLoginMode(opts) {
4537
4602
  let loginMode = DEFAULT_LOGIN_MODE;
4603
+ let passwordLogin = DEFAULT_PASSWORD_LOGIN;
4538
4604
  try {
4539
4605
  const baseURL = opts.baseURL;
4540
4606
  if (baseURL !== void 0 && baseURL !== "") {
@@ -4547,13 +4613,16 @@ async function projectLoginMode(opts) {
4547
4613
  deviceId: device.deviceId,
4548
4614
  ...opts.timeoutMs === void 0 ? {} : { timeoutMs: opts.timeoutMs }
4549
4615
  });
4550
- if (policy !== void 0) loginMode = policy.loginMode;
4616
+ if (policy !== void 0) {
4617
+ loginMode = policy.loginMode;
4618
+ passwordLogin = policy.passwordLogin;
4619
+ }
4551
4620
  }
4552
4621
  } catch (error) {
4553
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`);
4554
4623
  }
4555
4624
  try {
4556
- await opts.state.patch({ set: { loginMode } });
4625
+ await opts.state.patch({ set: { loginMode, passwordLogin } });
4557
4626
  } catch (error) {
4558
4627
  opts.log?.(`hiwork-core: \u5BA2\u6237\u7AEF\u7B56\u7565\u5199\u5165\u5931\u8D25\uFF08${errorName(error)}\uFF09`);
4559
4628
  }
@@ -4693,6 +4762,12 @@ function apply(ctx, config) {
4693
4762
  ctx.logger.warn(message);
4694
4763
  }
4695
4764
  }), "hiwork-core: daily rpc");
4765
+ ctx.effect(() => registerAppVersionRpc({ connection, logger: ctx.logger }, {
4766
+ appVersion: () => pushedAppVersion(),
4767
+ log: (message) => {
4768
+ ctx.logger.warn(message);
4769
+ }
4770
+ }), "hiwork-core: app version rpc");
4696
4771
  ctx.effect(() => registerIpcMessageHandler({
4697
4772
  source: process,
4698
4773
  complete: (input) => {
@@ -0,0 +1,85 @@
1
+ /**
2
+ * `/hiwork-app` loopback RPC 的 Host 实现:把「当前桌面端版本」报给渲染端。
3
+ *
4
+ * 为什么需要这个频道:侧栏品牌行显示的是**桌面端**版本,而这个值只有 Host 拿得到
5
+ * ——桌面端主进程启动时经 `hiwork:device-info`(stdio:ipc)把 `app.getVersion()` 推给
6
+ * Host,缓存在 `src/browser-login.ts` 的模块级 `pushedDeviceInfo` 里。渲染端看不到
7
+ * 那条 IPC,因此必须经 loopback RPC 取。此前的做法是在 client 侧硬编码产品版本,
8
+ * 每次桌面端发版都要手工同步一次;本频道就是为了去掉这步手工同步。
9
+ *
10
+ * 设计要点(与 `src/daily-rpc.ts` / `src/usage-rpc.ts` 同构):
11
+ * - 只注册一个 channel,endpoint 白名单在 {@link APP_VERSION_RPC_ENDPOINTS},
12
+ * 未知 endpoint 直接 `bad-request`;
13
+ * - `version` 端点无入参:payload 出现任何字段都判 `bad-request`;
14
+ * - **「桌面端没推过」不是错误**:纯 web / 浏览器 dev 下永远不会有值,与本频道要
15
+ * 回答的问题无关,所以返回 `{ version: null }` 的成功信封,由客户端自行回退到
16
+ * 常量——不产生任何错误 UI,也不进日志。
17
+ *
18
+ * 本模块不 import cordis 运行时,只依赖结构化的 {@link AppVersionRpcContext} 端口,
19
+ * 便于单测直接伪造。
20
+ */
21
+ /** RPC 频道;必须与 client 半边 `src/client/app-version.ts` 的 `APP_VERSION_CHANNEL` 一致(由测试锁定)。 */
22
+ export declare const APP_VERSION_RPC_CHANNEL = "/hiwork-app";
23
+ /** 端点白名单;必须与 client 半边 `APP_VERSION_ENDPOINTS` 一致(由测试锁定)。 */
24
+ export declare const APP_VERSION_RPC_ENDPOINTS: readonly ["version"];
25
+ export type AppVersionRpcEndpoint = (typeof APP_VERSION_RPC_ENDPOINTS)[number];
26
+ /** 通用失败文案:不暴露任何实现细节。 */
27
+ export declare const APP_VERSION_GENERIC_FAILURE_MESSAGE = "\u6682\u65F6\u65E0\u6CD5\u83B7\u53D6\u684C\u9762\u7AEF\u7248\u672C";
28
+ /** `version` 端点的响应:`null` = 桌面端尚未投递(或当前根本不是桌面端)。 */
29
+ export interface AppVersionRpcValue {
30
+ version: string | null;
31
+ }
32
+ /** 客户端可安全展示的失败。 */
33
+ export interface AppVersionRpcFailure {
34
+ code: string;
35
+ message: string;
36
+ /** DSH Connection 信封要求 `details` 是对象;协议层没有额外细节时补空对象。 */
37
+ details: object;
38
+ }
39
+ /** 端点结果信封(与 DSH Connection 的 `ConnectionRpcResult` 同形)。 */
40
+ export type AppVersionRpcResult<T> = {
41
+ readonly ok: true;
42
+ readonly value: T;
43
+ } | {
44
+ readonly ok: false;
45
+ readonly error: AppVersionRpcFailure;
46
+ };
47
+ /** 请求侧错误(只有 `bad-request`:未知端点 / 非法 payload)。 */
48
+ export declare class AppVersionRequestError extends Error {
49
+ readonly code: 'bad-request';
50
+ constructor(code: 'bad-request', message: string);
51
+ }
52
+ /** Host 侧 RPC 依赖的最小上下文(结构兼容 cordis Context,便于单测伪造)。 */
53
+ export interface AppVersionRpcConnection {
54
+ rpc: {
55
+ handle(channel: string, handler: (endpoint: string, payload: unknown, signal: AbortSignal) => Promise<unknown>, options: {
56
+ authority: 'loopback';
57
+ }): () => Promise<void> | void;
58
+ };
59
+ }
60
+ export interface AppVersionRpcContext {
61
+ readonly connection: AppVersionRpcConnection;
62
+ readonly logger: {
63
+ warn(message: string): void;
64
+ };
65
+ }
66
+ /** Host 侧取数依赖:桌面端经 IPC 推来的版本(未推过时为 undefined)。 */
67
+ export interface AppVersionRpcDeps {
68
+ /** 桌面端版本;纯 web / 尚未投递时为 undefined。 */
69
+ appVersion(): string | undefined;
70
+ /** Host 日志出口(只记事件与错误类型)。 */
71
+ log?: ((message: string) => void) | undefined;
72
+ }
73
+ /** 是否为白名单端点。 */
74
+ export declare function isAppVersionRpcEndpoint(endpoint: string): endpoint is AppVersionRpcEndpoint;
75
+ /** payload 校验:`version` 无任何入参,出现字段即客户端实现缺陷。 */
76
+ export declare function parseAppVersionPayload(raw: unknown): void;
77
+ /**
78
+ * 处理一次端点调用:返回 DSH Connection 的结果信封,**永不抛异常**。
79
+ * 导出以便单测直接驱动(无需伪造 `ctx.connection`)。
80
+ */
81
+ export declare function handleAppVersionEndpoint(endpoint: string, rawPayload: unknown, deps: AppVersionRpcDeps, signal?: AbortSignal): Promise<AppVersionRpcResult<unknown>>;
82
+ /**
83
+ * 注册 `/hiwork-app` 频道,返回异步 disposer(交给 `ctx.effect` 管理生命周期)。
84
+ */
85
+ export declare function registerAppVersionRpc(ctx: AppVersionRpcContext, deps: AppVersionRpcDeps): () => Promise<void>;
@@ -52,6 +52,11 @@ export declare function setDeviceInfo(info: {
52
52
  deviceName?: string;
53
53
  appVersion?: string;
54
54
  }): void;
55
+ /**
56
+ * 桌面端经 IPC 推来的版本号(未推过 / 纯 web 时为 undefined)。
57
+ * 只读、无副作用:`/hiwork-app` 频道拿它给侧栏品牌行展示(见 `src/app-version-rpc.ts`)。
58
+ */
59
+ export declare function pushedAppVersion(): string | undefined;
55
60
  /** 清空模块级状态(IPC 设备缓存 + 待完成授权);供测试与插件卸载使用。 */
56
61
  export declare function resetBrowserLoginState(): void;
57
62
  /**
@@ -1,10 +1,10 @@
1
1
  import type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
2
2
  /**
3
3
  * 设置弹窗「关于」分区(自 hiwork-dsh-ui 迁移,注册为 settings.section 列表末位):
4
- * 品牌横标、标语、可点击复制的版本胶囊、能力关键词与安全承诺。dsh-ui 的「配套
5
- * 管理插件」区块在其源码中已被 HIDE_DEPENDENCIES 隐藏且依赖其 host 端点,不迁移。
4
+ * 品牌横标、标语、能力关键词与安全承诺。dsh-ui 的「配套管理插件」区块在其源码中
5
+ * 已被 HIDE_DEPENDENCIES 隐藏且依赖其 host 端点,不迁移。
6
6
  * 横标只有深色资产,暗色主题复用 brandCss 对 .hiwork-brand-wordmark 的反白规则。
7
7
  */
8
8
  export type AboutSectionProps = PropsRuntime<'settings.section'>;
9
- /** HiWork 品牌区(标语、版本复制)、能力关键词与安全承诺。 */
9
+ /** HiWork 品牌区(标语)、能力关键词与安全承诺。 */
10
10
  export declare function AboutSection(_props: AboutSectionProps): import("react").JSX.Element;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * 侧栏品牌行的「桌面端版本」取数层:经 `/hiwork-app` loopback RPC 向 Host 取。
3
+ *
4
+ * 桌面端主进程把 `app.getVersion()` 经 `hiwork:device-info` 推给 Host,渲染端看不到
5
+ * 那条 IPC,只能走这个频道(Host 半边见 `src/app-version-rpc.ts`)。这样做的好处是
6
+ * 桌面端发版不必再回本仓库改常量。
7
+ *
8
+ * 铁律:
9
+ * - **永不对调用方抛异常**:频道不可达 / 信封不合法 / 值缺失一律归一为 undefined,
10
+ * 视图不为此显示任何错误 UI,直接回退到 `HIWORK_VERSION` 常量;
11
+ * - `undefined` 即「拿不到桌面端版本」(纯 web、浏览器 dev、桌面端尚未投递),
12
+ * 与「拿到空串」同义——两者都不得被渲染成裸 `v`;
13
+ * - 不做缓存:值在一次应用生命周期内不变,单槽位组件只取一次。
14
+ *
15
+ * client bundle 纯度:本文件不 import 任何 `@deepseek-ai/*` 运行时值,也不 import
16
+ * Host 代码(响应字段在这里按结构重声明)。
17
+ */
18
+ /** RPC 频道;必须与 Host `src/app-version-rpc.ts` 的 `APP_VERSION_RPC_CHANNEL` 一致(由测试锁定)。 */
19
+ export declare const APP_VERSION_CHANNEL = "/hiwork-app";
20
+ /** 端点白名单;必须与 Host `APP_VERSION_RPC_ENDPOINTS` 一致(由测试锁定)。 */
21
+ export declare const APP_VERSION_ENDPOINTS: readonly ["version"];
22
+ export type AppVersionEndpoint = (typeof APP_VERSION_ENDPOINTS)[number];
23
+ /** `version` 端点名(本频道唯一端点)。 */
24
+ export declare const APP_VERSION_ENDPOINT: AppVersionEndpoint;
25
+ /** 宿主 loopback RPC 调用面(结构兼容 `ctx.connection.rpc`)。 */
26
+ export interface AppVersionRpcPort {
27
+ call(channel: string, endpoint: string, payload: unknown, signal?: AbortSignal): Promise<unknown>;
28
+ }
29
+ /** 组件消费的最小取数面(便于视图与测试注入假实现)。 */
30
+ export interface AppVersionFace {
31
+ /** 桌面端版本;拿不到时返回 undefined(不抛异常)。 */
32
+ version(): Promise<string | undefined>;
33
+ }
34
+ /**
35
+ * 解开 DSH Connection 的结果信封并取出版本号。
36
+ * 任何形状不符(信封缺失 / `ok:false` / `version` 既非字符串也非 null)都归一为
37
+ * undefined——绝不把未知结构当版本号渲染。
38
+ */
39
+ export declare function unwrapAppVersion(value: unknown): string | undefined;
40
+ /** 造一个取数面:一次 RPC,失败 / 缺值一律 undefined。 */
41
+ export declare function createAppVersion(rpc: AppVersionRpcPort): AppVersionFace;
@@ -1,14 +1,27 @@
1
- import type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
1
+ import type { InjectFace, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
2
+ import type { AppVersionFace } from './app-version.ts';
2
3
  /**
3
4
  * 左上角品牌区(sidebar.brand.*):官方侧栏把「宽栏 brand 行」与「折叠 rail 的小
4
5
  * logo」共用到 `sidebar.brand.mark` 插槽(fallback 是 DeepSeek 鱼 logo),注册本
5
6
  * 插槽即同时替换两处;`sidebar.brand.name` 只在宽栏渲染。mark 用方形 App 图标,
6
7
  * name 用 HiWork 词标 + 产品版本,与 hiwork-dsh-ui 的侧栏头部一致;词标资产只有
7
8
  * 深色版,暗色主题由 styles.ts 的 brandCss 整体反白。
9
+ *
10
+ * 版本号取**当前运行的桌面端版本**(经 `/hiwork-app` 频道,源自桌面端主进程经 IPC
11
+ * 推给 Host 的 `app.getVersion()`),所以桌面端发版不再需要回来改本仓库的常量。
8
12
  */
9
13
  export type BrandMarkProps = PropsRuntime<'sidebar.brand.mark'>;
10
14
  /** 品牌方标:宽栏 brand 行与折叠 rail 小 logo 共用(官方传 size,缺省 24)。 */
11
15
  export declare function BrandMark(props: BrandMarkProps): import("react").JSX.Element;
12
- export type BrandNameProps = PropsRuntime<'sidebar.brand.name'>;
13
- /** HiWork 词标 + 产品版本:只在宽栏 brand 行渲染(折叠态官方不渲染 name 插槽)。 */
14
- export declare function BrandName(_props: BrandNameProps): import("react").JSX.Element;
16
+ export type BrandNameProps = PropsRuntime<'sidebar.brand.name'> & InjectFace<{
17
+ appVersion: AppVersionFace;
18
+ }>;
19
+ /**
20
+ * HiWork 词标 + 桌面端版本:只在宽栏 brand 行渲染(折叠态官方不渲染 name 插槽)。
21
+ *
22
+ * 版本异步到达(一次 loopback RPC),**未返回时整个版本 span 不渲染**——若先在原地渲染
23
+ * `HIWORK_VERSION` 兜底值,桌面端升版后首帧会闪现一个过时的版本号,正是本次要消除的
24
+ * 问题。拿到桌面端版本就显示它;拿到 `null`(无桌面壳 / 桌面端尚未投递)才回退常量,
25
+ * 此时 `HIWORK_VERSION` 是唯一来源,不必再跟着桌面端发版走。
26
+ */
27
+ export declare function BrandName({ appVersion }: BrandNameProps): import("react").JSX.Element;
@@ -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) 落位)。
@@ -3,4 +3,4 @@ export declare const HIWORK_LOGO: string;
3
3
  export declare const HIWORK_LOGO_TEXT: string;
4
4
  export declare const HIWORK_LOGO_WITH_TEXT: string;
5
5
  /** HiWork 产品版本:品牌展示元素,与 hiwork-dsh-ui 的 HIWORK_VERSION 保持一致。 */
6
- export declare const HIWORK_VERSION = "1.0.2";
6
+ export declare const HIWORK_VERSION = "1.0.4";
@@ -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(不发起请求)。 */