hiwork-core 0.0.21 → 0.0.24

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.
@@ -104,23 +104,25 @@ export interface HiWorkCoreClientFace {
104
104
  readonly managedWorkbench?: ManagedWorkbenchFace;
105
105
  /**
106
106
  * PMS 个人待办(工作台插件 hiwork-workbench 的右栏 tab 消费,见 `src/client/pms-todo.ts`;
107
- * 契约见 hiwork-workbench 的 spec `docs/superpowers/specs/2026-09-17-pms-todo-integration-design.md`
108
- * §4.1 / §4.3 与 §3 D7)。
107
+ * 契约见 hiwork-workbench 的 spec `docs/superpowers/specs/2026-09-21-pms-zero-input-login-design.md`
108
+ * §4.3 / §4.4 与 §3 D7)。
109
109
  *
110
110
  * **可选成员**(与 `managedMcp` / `managedSkills` / `managedWorkbench` 同款的两级可选语义):
111
111
  * 新 core 遇到老 workbench 只是**没人来取**;老 core 遇到新 workbench 只是**没有这个面**
112
112
  * (`face.pmsTodo` 为 `undefined` → 右栏 tab 显示「请升级 HiWork 客户端」、工作台入口卡片不显示,
113
113
  * spec §6 的降级矩阵第一行,不报错、不空白)。两侧因此不必严格同发一个版本。
114
114
  *
115
+ * ⚠️ **这一面没有登录动作**(spec §3 D3):零输入登录(网关身份断言 → PMS 换令牌)整段在 Host
116
+ * 半边发生,客户端连断言都看不到;`login` / `logout` 已随账号密码通道一起删除,本面**没有任何**
117
+ * 取 token / 取密码 / 收密码的成员(§3 D6 / §7:JWT 只在 Host 内存,前端 bundle 里搜不到 PMS 凭据)。
118
+ * 未就绪的两种态看 `status.reason`(`no-assignment` → 找管理员;`gateway-signed-out` → 重新登录
119
+ * HiWork 客户端)——面板据此选文案,**不许**给出任何可填表单。
120
+ *
115
121
  * ⚠️ **这一面里还有第二级可选**:§3 D7 追加的 `myTasks` / `completeTask` **在面里也是可选的**
116
- * (旧 core 有这一面但只有前七项)。消费方按 `face.pmsTodo.myTasks !== undefined` 判断,
122
+ * (旧 core 有这一面但只有前六项)。消费方按 `face.pmsTodo.myTasks !== undefined` 判断,
117
123
  * 拿不到就只渲染待办那一半——**不许**把「没有这个能力」渲染成「我的任务为空」,那会让人
118
124
  * 以为任务都完成了。同理,新的那一侧也不许把它们改成必填(详见 `PmsTodoFace` 的注释)。
119
- *
120
- * ⚠️ **账号密码不经过这一面**:`login()` 的入参里出现的密码只走这一次 RPC 调用,随即由 Host
121
- * 侧交给 credentials 服务;本面**没有任何**取 token / 取密码的成员,返回的状态里也没有凭据
122
- * (spec §3 D6 / §7:JWT 只在 Host 内存 + credentials,前端 bundle 里搜不到 PMS 凭据)。
123
- * `myTasks` 也不收 `userId`——「谁的任务」由 Host 从凭据记录里取(同 §3 D6 的形状)。
125
+ * `myTasks` 也不收 `userId`——「谁的任务」由 Host 从 mint 响应里取(同 §3 D6 的形状)。
124
126
  */
125
127
  readonly pmsTodo?: PmsTodoFace;
126
128
  }
@@ -158,8 +158,16 @@ export declare function browserLoginViewOf(value: AiRouterCardSettings | undefin
158
158
  browserLoginPending: boolean;
159
159
  };
160
160
  /**
161
- * 打开授权页(渲染层职责):桌面端 `shell.openExternal` 路由接管 https 外链,桌面端无需改动。
162
- * 命名空间值可被任意写入,因此只放行 http(s),其余静默忽略。
161
+ * 在系统浏览器里打开外链(渲染层职责):桌面端 `shell.openExternal` 路由接管 https 外链,
162
+ * 桌面端无需改动。
163
+ *
164
+ * 只放行 http(s):调用方给的可能是命名空间里被任意写入的值,也可能是将来变成用户可填的常量,
165
+ * `javascript:` / `file:` 之类一律静默忽略。URL 非法同样 no-op —— 外链打开失败不该抛错打断调用方。
166
+ * @param url - 目标地址;缺省 / 非 http(s) / 非法时为 no-op。
167
+ */
168
+ export declare function openExternalUrl(url: string | undefined): void;
169
+ /**
170
+ * 打开授权页。
163
171
  * @param url - Host 回写的授权页 URL;缺省 / 非法时为 no-op。
164
172
  */
165
173
  export declare function openAuthorizeUrl(url: string | undefined): void;
@@ -72,8 +72,10 @@ export declare function decorateDailyCard(doc: Document, quotes: DailyQuoteSourc
72
72
  export declare function removeStaleDailyCard(doc: Document): void;
73
73
  /**
74
74
  * 观察新建任务 hero 页并装饰同页组件:每日一句(服务端下发)+ hero 问候语轮换
75
- * (见 hero-greeting.ts)。DOM 变更按帧合并,避免流式输出时每个 token 都全文档扫描;
76
- * 页面完全空闲跨午夜时 MutationObserver 不会触发,靠周期轮询兜底换日(本地日变更
77
- * 触发重新拉取,服务端日期随之更新)。返回卸载清理函数。
75
+ * (见 hero-greeting.ts)+ 输入框右上方居右的「使用手册」问号按钮(见 manual-link.ts)。
76
+ * DOM 变更按帧合并,避免流式输出时每个 token 都全文档扫描;页面完全空闲跨午夜时
77
+ * MutationObserver 不会触发,靠周期轮询兜底换日(本地日变更触发重新拉取,服务端日期
78
+ * 随之更新)。三个装饰共用一个观察循环,不各自起 MutationObserver。
79
+ * 返回卸载清理函数。
78
80
  */
79
81
  export declare function observeHeroDecorations(doc: Document, quotes: DailyQuoteSource): () => void;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * 使用手册地址 —— **全仓唯一一处**写死这个域名的地方(与 pms-client 的 PMS_BASE_URL 同一约定)。
3
+ *
4
+ * 为什么固定而不是配置项:官网是组织自建且只有这一个生产部署,做成设置项只会多一个能填错、
5
+ * 又没人能集中改的地方;真要换部署,改这一个常量、发一版即可。**任何别处都不许再写这个域名**
6
+ * (包括错误消息、日志、文档示例之外的代码),否则「唯一的基址」立刻变成两处能漂移的真相。
7
+ */
8
+ export declare const MANUAL_URL = "https://hiworkapp.hivery.cn/manual.html";
9
+ export declare const MANUAL_STYLE_ID = "hiwork-core-manual-style";
10
+ /**
11
+ * 问号按钮:28×28 的圆形热区、无边框(与输入框自身的图标按钮同语言),hover 才浮出一层浅底。
12
+ * `margin-left:auto` 是「居右」的全部机制——该行是 flex 容器,自动外边距吃掉剩余空间。
13
+ * `flex:none` 保证窄窗口下被两个 chip 挤扁时不至于缩成一个点。
14
+ */
15
+ export declare const MANUAL_STYLE = "\n[data-hiwork-core-manual-entry]{box-sizing:border-box;flex:none;display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;margin-left:auto;padding:0;border:0;border-radius:999px;background:transparent;color:var(--dsw-alias-label-tertiary,#71717a);font-family:inherit;font-size:14px;line-height:1;font-weight:600;cursor:pointer;user-select:none;transition:background .16s ease,color .16s ease}\n[data-hiwork-core-manual-entry]:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(24,24,27,.06));color:var(--dsw-alias-label-secondary,#3f3f46)}\n[data-hiwork-core-manual-entry]:focus-visible{outline:2px solid var(--dsw-alias-button-info-fill,#4c8dff);outline-offset:2px}\nbody[data-ds-dark-theme] [data-hiwork-core-manual-entry]{color:var(--dsw-alias-label-tertiary,#9ba3b4)}\nbody[data-ds-dark-theme] [data-hiwork-core-manual-entry]:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(255,255,255,.08));color:var(--dsw-alias-label-secondary,#a6adb8)}\n@media (prefers-reduced-motion: reduce){[data-hiwork-core-manual-entry]{transition:none}}\n";
16
+ export declare function ensureManualStyle(doc: Document): void;
17
+ /** 在系统浏览器里打开使用手册(桌面端 shell 接管 https 外链)。 */
18
+ export declare function openManual(): void;
19
+ /**
20
+ * hero 页里 workspace 行的定位:只在 `[data-phase="hero"]` 下找。
21
+ * 类名后缀稳定、前缀是构建哈希,故按 `*=` 匹配(同 daily-card 的 `[class$="_headline"]`)。
22
+ * 行本身缺失(非 hero 页 / 宿主改版)→ null,调用方只做清理。
23
+ */
24
+ export declare function findManualMount(doc: Document): HTMLElement | null;
25
+ /** 清掉非 hero 状态下(以及行被重建后)残留的按钮节点。 */
26
+ export declare function removeStaleManualEntry(doc: Document): void;
27
+ /**
28
+ * 幂等装饰:把问号按钮挂到 hero 输入框那一行的末尾(`margin-left:auto` 使其居右)。
29
+ *
30
+ * 已挂着且仍在该行内 → 不动(宿主流式重渲染时不做无谓的摘挂,避免按钮闪一下);
31
+ * 行不在场(会话记录页 / 登录门禁 / 宿主未渲染)→ 清掉残留。
32
+ * 只在**最后一个**子节点插入:宿主自己的 chip 顺序不受影响,将来它再往行里加东西,
33
+ * 我们的按钮仍在右侧。
34
+ */
35
+ export declare function decorateManualEntry(doc: Document): void;
@@ -1,22 +1,21 @@
1
1
  /**
2
- * 「PMS 个人待办」客户端取数层:经 `/hiwork-pms-todo` loopback RPC 拿到自己的待办、计数,
3
- * 并托管登录 / 登出(凭据本身**不经过本层**,见下),交给工作台插件(hiwork-workbench)的
4
- * 右栏 tab 渲染。
2
+ * 「PMS 个人待办」客户端取数层:经 `/hiwork-pms-todo` loopback RPC 拿到自己的待办、计数与项目任务,
3
+ * 交给工作台插件(hiwork-workbench)的右栏 tab 渲染。
5
4
  *
6
- * 契约见 spec `docs/superpowers/specs/2026-09-17-pms-todo-integration-design.md` §4.1(Host 频道)、
7
- * §4.2(`TodoItem`)、§4.3(本面)、§3 D7(**我的任务**:待办之外还要直接查未完成的项目任务)。
8
- * 本文件与 `src/client/managed-workbench.ts` 同款,是同一副骨架的**第四条链**,差别只有一处:
9
- * 那条链是纯只读,这一条还带「登录 / 登出」两个动作。
5
+ * 契约见 spec `docs/superpowers/specs/2026-09-21-pms-zero-input-login-design.md` §4.3(Host 频道)、
6
+ * §4.4(本面)与 §3 D7(**我的任务**:待办之外还要直接查未完成的项目任务)。
7
+ * 本文件与 `src/client/managed-workbench.ts` 同款,是同一副骨架的**第四条链**。
10
8
  *
11
9
  * 铁律:
12
- * - **本层不持有任何凭据**:账号密码只在 `login()` 的入参里出现一次,随即交给 Host;access /
13
- * refresh token、PMS 的域名与路径**全都不在本文件里**(client bundle 里搜不到 PMS 凭据,§7);
10
+ * - **本层不持有任何凭据,也没有登录动作**:零输入登录(网关身份断言 → PMS 换令牌)整段发生在
11
+ * Host 半边,客户端连断言长什么样都看不到;`login` / `logout` 两个端点已经随账号密码通道一起
12
+ * 删除(spec §3 D3)——本文件里**不许**再长出任何输入框式的成员;
14
13
  * - **不 fetch、不直连 PMS**:一切经 Host 频道——这正是 §3 D6「凭据与数据流都在 Host」在
15
14
  * 客户端一侧的落地形状;
16
15
  * - **错误码原样透传**:Host 的六个稳定码(`NOT_CONFIGURED` / `UNAUTHORIZED` / `FORBIDDEN` /
17
- * `UPSTREAM_UNAVAILABLE` / `BAD_REQUEST` / `INTERNAL`)由消费方决定文案(未配置 → 引导填
18
- * 凭据;`UNAUTHORIZED` → 要求重新输入;`FORBIDDEN` → 「到 PMS 操作」,**不是**重新登录)。
19
- * 本层**不改写、不吞掉**它们。
16
+ * `UPSTREAM_UNAVAILABLE` / `BAD_REQUEST` / `INTERNAL`)由消费方决定文案(`NOT_CONFIGURED` →
17
+ * 「找管理员」;`UNAUTHORIZED` → 「重新登录 HiWork 客户端」;`FORBIDDEN` → 「到 PMS 操作」,
18
+ * **不是**重新登录)。本层**不改写、不吞掉**它们。
20
19
  *
21
20
  * client bundle 纯度:本文件不 import 任何 `@deepseek-ai/*` 运行时值,也不 import Host 代码
22
21
  * (类型按结构重声明,字段名与契约一致)。
@@ -25,10 +24,14 @@
25
24
  export declare const PMS_TODO_CHANNEL = "/hiwork-pms-todo";
26
25
  /**
27
26
  * 端点白名单;必须与 Host `PMS_TODO_RPC_ENDPOINTS` 一致(由测试锁定)。
28
- * 顺序即冻结契约里的顺序(`status` / `login` / `logout` 是凭据生命周期,
29
- * `today` / `statistics` / `setStatus` 是待办数据,`myTasks` / `completeTask` 是项目任务 —— §3 D7)。
27
+ * 顺序即冻结契约里的顺序(`status` 是能力状态,`today` / `statistics` / `setStatus` 是待办数据,
28
+ * `myTasks` / `completeTask` 是项目任务 —— §3 D7)。
29
+ *
30
+ * ⚠️ 六项与 v1 的八项**刻意不同**(v1 还有 `login` / `logout`):那是版本闸口——老 client 拿八项
31
+ * 去比对新 Host 会对不上,于是走「请升级 HiWork 客户端」的降级,而不是带着一个已经删掉的
32
+ * 账号密码表单硬跑(spec §4.3)。
30
33
  */
31
- export declare const PMS_TODO_ENDPOINTS: readonly ["status", "login", "logout", "today", "statistics", "setStatus", "myTasks", "completeTask"];
34
+ export declare const PMS_TODO_ENDPOINTS: readonly ["status", "today", "statistics", "setStatus", "myTasks", "completeTask"];
32
35
  export type PmsTodoEndpoint = (typeof PMS_TODO_ENDPOINTS)[number];
33
36
  /** 勾选用的两个字面量(PMS 只认这两个)。 */
34
37
  export type PmsTodoStatusValue = 'PENDING' | 'COMPLETED';
@@ -58,14 +61,38 @@ export interface PmsTodoItem {
58
61
  isOverdue?: boolean;
59
62
  isDueSoon?: boolean;
60
63
  }
61
- /** `status` / `login` / `logout` 的返回值(spec §4.1)。 */
64
+ /**
65
+ * `status` 的返回值(spec §4.4;字段名沿用 v1,语义重定义)。
66
+ *
67
+ * `configured` = **网关里存在 `slug='pms'` 的分配**(false → 「找管理员」,面板不显示任何可填表单);
68
+ * `loggedIn` = **手上有一张未过期的 PMS JWT**。`username`(mint 响应里的 PMS 工号)与 `expiresAt`
69
+ * (ISO 8601,**不是** token 本身)只给界面展示用;`userId` 是「我的任务」的查询键(§3 D7),
70
+ * 客户端**没有**任何按它查询的入口。
71
+ */
62
72
  export interface PmsTodoStatus {
63
73
  configured: boolean;
64
74
  loggedIn: boolean;
65
75
  username?: string;
66
- /** access token 的绝对过期时刻(ISO 8601)。**不是** token 本身。 */
67
76
  expiresAt?: string;
77
+ userId?: number;
78
+ /**
79
+ * 为什么还没就绪(spec §4.4 新增的可选字段;三种取值对应三个不同的人 / 动作)。
80
+ *
81
+ * 不认识的取值**原样保留**语义上的开放:这里是联合类型,但 `toStatus` 遇到未知串会**丢掉**它
82
+ * (不是把整份状态判为无效)——老客户端遇到新 reason 只会少一条提示,不会崩页。
83
+ */
84
+ reason?: PmsTodoStatusReason;
68
85
  }
86
+ /**
87
+ * `reason` 的三个取值(spec §4.4)。
88
+ *
89
+ * `no-assignment` → 找管理员(客户端没被接进 PMS);`gateway-signed-out` → 用户自己在 HiWork
90
+ * 客户端重新登录;`unavailable` → 稍后重试(保留上次数据 + 陈旧标记)。合并成一个码会让用户
91
+ * 对着一个自己修不了的问题反复重试(§3 D8)。
92
+ */
93
+ export type PmsTodoStatusReason = 'no-assignment' | 'gateway-signed-out' | 'unavailable';
94
+ /** `reason` 的白名单(`toStatus` 用它挡掉未知串;顺序即契约里的顺序)。 */
95
+ export declare const PMS_TODO_STATUS_REASONS: readonly ["no-assignment", "gateway-signed-out", "unavailable"];
69
96
  /** `today` 的返回值。 */
70
97
  export interface PmsTodoToday {
71
98
  items: PmsTodoItem[];
@@ -139,20 +166,16 @@ export interface PmsTaskCompleteResult {
139
166
  /** 任务状态数字(完成是 2)。 */
140
167
  status: number;
141
168
  }
142
- /** 登录入参。 */
143
- export interface PmsTodoLoginInput {
144
- username: string;
145
- password: string;
146
- /** 是否把凭据交给 credentials 服务加密保存(Host 侧语义,见 spec §4.4)。 */
147
- remember?: boolean;
148
- }
149
169
  /**
150
- * 「PMS 待办」面(spec §4.3 冻结的七项 + §3 D7 追加的两项)。
170
+ * 「PMS 待办」面(spec §4.4 冻结的六项 + §3 D7 追加的两项)。
151
171
  *
152
172
  * 它挂在 `HiWorkCoreClientFace` 上作为**可选成员** `pmsTodo?`:老 core 没有这个面,老
153
173
  * workbench 也不会来取;两侧因此不必同发一个版本——老 core 遇新 workbench 就是
154
174
  * 「请升级 HiWork 客户端」(spec §6 的降级矩阵第一行)。
155
175
  *
176
+ * ⚠️ **没有 `login` / `logout`**(v1 有,spec §3 D3 随账号密码通道一起删除):零输入登录整段在
177
+ * Host 半边发生,客户端没有任何凭据入口——**不许**为了「兼容」把它们加回来。
178
+ *
156
179
  * ⚠️ **`myTasks` / `completeTask` 是这一面里的两级可选**(版本闸口):§3 D7 是 2026-09-18 追加的,
157
180
  * 装着的旧 core 有 `pmsTodo` 面但**没有**这两项。所以它们是**可选成员**,消费方必须按
158
181
  * `face.pmsTodo.myTasks !== undefined` 判断(拿不到就只显示待办那一半,而不是崩页、也不是把
@@ -163,15 +186,15 @@ export interface PmsTodoFace {
163
186
  /**
164
187
  * 本地镜像(**同步读**,可能是 `undefined`)。
165
188
  *
166
- * ⚠️ 它是镜像是**有意的**:界面渲染是同步的,而「有没有配置凭据」是异步来的。镜像由
167
- * `login` / `logout` 的返回值、以及**首次调用本方法时的一次惰性探测**写入(结果到达后通知
168
- * 订阅者),所以调用方必须 `subscribe()` ——首次渲染很可能拿到 `undefined`,等通知到了再重渲。
189
+ * ⚠️ 它是镜像是**有意的**:界面渲染是同步的,而「网关里有没有 pms 分配 / 手上有没有令牌」是
190
+ * 异步来的。镜像由 `refreshStatus()` 的成功返回值、以及**首次调用本方法时的一次惰性探测**写入
191
+ * (结果到达后通知订阅者),所以调用方必须 `subscribe()` ——首次渲染很可能拿到 `undefined`,
192
+ * 等通知到了再重渲。
193
+ *
194
+ * ⚠️ `undefined` **不是**「未配置」:那是「还不知道」(探测还没回来)。未就绪的两种态要看
195
+ * `status.reason`(`no-assignment` → 找管理员;`gateway-signed-out` → 重新登录 HiWork 客户端)。
169
196
  */
170
197
  readonly status: () => PmsTodoStatus | undefined;
171
- /** 登录(凭据经 Host 换成 JWT;密码只在这一次调用里出现,本层不留存)。失败抛错。 */
172
- readonly login: (input: PmsTodoLoginInput) => Promise<PmsTodoStatus>;
173
- /** 登出(Host 清内存凭据 + 抹掉 credentials 记录)。 */
174
- readonly logout: () => Promise<PmsTodoStatus>;
175
198
  /** 今日待办(⚠️ 口径比名字宽:今天 + 明天 + 无截止日期,spec §D4)。失败抛错。 */
176
199
  readonly today: (signal?: AbortSignal) => Promise<PmsTodoToday>;
177
200
  /** total / pending / completed / overdue 计数。失败抛错。 */
@@ -189,7 +212,7 @@ export interface PmsTodoFace {
189
212
  /**
190
213
  * **我的未完成项目任务**(spec §3 D7)。**可选成员**——旧 core 上没有它(见本接口的抬头)。
191
214
  *
192
- * 「谁的任务」由 Host 从凭据记录里取(客户端传不进来),所以这里没有 `userId` 入参。
215
+ * 「谁的任务」由 Host 从 mint 响应里取(客户端传不进来),所以这里没有 `userId` 入参。
193
216
  * 失败抛错,错误码可判 `FORBIDDEN`(没有完成任务权限的角色会在**完成**时报它)。
194
217
  */
195
218
  readonly myTasks?: (signal?: AbortSignal) => Promise<PmsTaskList>;
@@ -198,12 +221,12 @@ export interface PmsTodoFace {
198
221
  *
199
222
  * ⚠️ 与 `setStatus`(待办)是**两条不同的上游路径**:待办那条没有权限注解,这一条需要
200
223
  * `task:list:edit`;缺权限时抛 `FORBIDDEN`,界面要按 §3 D7 说「在 HiWork 里没有完成任务的权限,
201
- * 请到 PMS 操作」,**不要**当成「登录过期」让用户重新输密码。
224
+ * 请到 PMS 操作」,**不要**当成「登录过期」让用户重新登录。
202
225
  */
203
226
  readonly completeTask?: (input: {
204
227
  id: number;
205
228
  }) => Promise<void>;
206
- /** 订阅镜像变化(登出 / 换账号时会被清空)。返回退订函数。 */
229
+ /** 订阅镜像变化(换账号时会被清空)。返回退订函数。 */
207
230
  readonly subscribe: (listener: () => void) => () => void;
208
231
  }
209
232
  /** 宿主 loopback RPC 调用面(结构兼容 `ctx.connection.rpc`)。 */
@@ -234,7 +257,8 @@ export declare function pmsTodoErrorOf(error: unknown): {
234
257
  * PMS 待办的取数仓库。
235
258
  *
236
259
  * 本地镜像(`status()` 同步读)是有必要的:DSH 的 client 半边在不同插件之间只通过这个面同步取
237
- * 状态,而「配没配凭据」是异步来的。镜像**只由成功响应覆写**,失败时不改动它。
260
+ * 状态,而「网关里有没有 pms 分配、手上有没有未过期的令牌」是异步来的。镜像**只由成功响应覆写**,
261
+ * 失败时不改动它。
238
262
  */
239
263
  export declare class PmsTodoStore {
240
264
  private readonly rpc;
@@ -243,7 +267,7 @@ export declare class PmsTodoStore {
243
267
  /**
244
268
  * 在飞请求**连同它所属的世代与端点**一起记账。
245
269
  *
246
- * 世代:`clear()`(登出 / 换账号)之后,账上那条是**上一个人**发起的,它的待办属于上一个人
270
+ * 世代:`clear()`(换账号)之后,账上那条是**上一个人**发起的,它的待办属于上一个人
247
271
  * ——复用它等于让新账号看到上一个人的待办。端点:`today` 与 `statistics` 的返回值形状不同,
248
272
  * 只有同一个端点的并发调用才允许合并。
249
273
  */
@@ -257,8 +281,9 @@ export declare class PmsTodoStore {
257
281
  * 本地镜像(同步)。从没同步过返回 `undefined`。
258
282
  *
259
283
  * ⚠️ 本方法有一次**惰性探测**:镜像还空着且从没探过时,它顺手发起一次 `status` 端点调用
260
- * (结果到达后写镜像 + 通知订阅者)。理由——渲染是同步的,而「配没配凭据」只有主机知道;
261
- * 没有这一步,`status` 端点就永远没有调用方,界面也只能靠一次失败的 `today()` 去反推。
284
+ * (结果到达后写镜像 + 通知订阅者)。理由——渲染是同步的,而「网关里有没有 pms 分配、手上
285
+ * 有没有令牌」只有主机知道;没有这一步,`status` 端点就永远没有调用方,界面也只能靠一次
286
+ * 失败的 `today()` 去反推(而且那反推出来的原因往往正好是错的)。
262
287
  * 因此调用方看到 `undefined` **不是**「未配置」,而是「还不知道」:必须订阅。
263
288
  */
264
289
  status(): PmsTodoStatus | undefined;
@@ -269,14 +294,6 @@ export declare class PmsTodoStore {
269
294
  * 调用方(或下一次 `today()`)自己决定怎么展示。
270
295
  */
271
296
  refreshStatus(signal?: AbortSignal): Promise<PmsTodoStatus>;
272
- /**
273
- * 登录(凭据交给 Host;**本层不留存密码、不写任何缓存**)。
274
- *
275
- * 成功时把返回的状态写进镜像——登录是「身份刚变过」,镜像必须跟着变。
276
- */
277
- login(input: PmsTodoLoginInput): Promise<PmsTodoStatus>;
278
- /** 登出(Host 侧清凭据);返回登出后的状态并写镜像。 */
279
- logout(): Promise<PmsTodoStatus>;
280
297
  /**
281
298
  * 今日待办。
282
299
  *
@@ -299,7 +316,7 @@ export declare class PmsTodoStore {
299
316
  /**
300
317
  * **我的未完成项目任务**(spec §3 D7):`myTasks` 端点的取数。
301
318
  *
302
- * 入参是**空对象**,没有 `userId`——「谁的任务」由 Host 从凭据记录里取(客户端连自己的
319
+ * 入参是**空对象**,没有 `userId`——「谁的任务」由 Host 从 mint 响应里取(客户端连自己的
303
320
  * PMS userId 都拿不到,这是 §3 D6 形状的一部分)。
304
321
  *
305
322
  * 与 `today` 同款:**不做本地缓存**(返回刚拉到的那一份),但**并发调用合并**(同一时刻问两遍
@@ -323,7 +340,8 @@ export declare class PmsTodoStore {
323
340
  /** 订阅镜像变化(右栏 tab 据此重渲染)。返回退订函数。 */
324
341
  subscribe(listener: () => void): () => void;
325
342
  /**
326
- * 清空镜像(登出 / 换账号:状态是**按人**的,留着会串号),并**作废在飞的请求**。
343
+ * 清空镜像(换账号:状态是**按人**的——分配是按人下发的、令牌是按人换的,留着会串号),
344
+ * 并**作废在飞的请求**。
327
345
  *
328
346
  * 只清镜像是不够的:清空那一刻正有一次拉取在飞时,那次拉取是**上一个人**发起的。所以世代
329
347
  * +1:那之后的调用者一律不复用旧世代的那条,自己重新发一次 RPC。用世代号而**不是**把
@@ -13,8 +13,9 @@ export declare const PMS_BASE_URL = "https://pms.hivery.cn";
13
13
  */
14
14
  export declare const PMS_REQUEST_TIMEOUT_MS = 10000;
15
15
  /**
16
- * access token 的**兜底**有效期:10 小时(= PMS `jwt.expiration=36000000`)。
17
- * 只在响应缺 `expiresIn` 时用;正常路径一律按服务端给的 `expiresIn` 算过期时刻。
16
+ * 令牌的**兜底**有效期:10 小时(= PMS `jwt.expiration=36000000`)。
17
+ * 只在兑换响应缺 `expiresAt`(或它不是一个能解析的时间串)时用;正常路径一律按服务端给的那个
18
+ * ISO 串走(见 {@link PmsExchangeResult.expiresAt})。
18
19
  */
19
20
  export declare const PMS_ACCESS_TOKEN_TTL_MS: number;
20
21
  /** 本客户端只有三个错误码:凭据失效 / 上游不可用 / 权限不足。其余(输入不合法等)由 Bridge 层判。 */
@@ -25,7 +26,13 @@ export declare class PmsError extends Error {
25
26
  readonly status?: number | undefined;
26
27
  constructor(code: PmsErrorCode, message: string, status?: number | undefined);
27
28
  }
28
- /** 未登录 / 凭据失效的文案(与 Bridge 侧 `UNAUTHORIZED` 同口径:引导重新输入,不静默失败)。 */
29
+ /**
30
+ * 未登录 / 凭据失效的文案(与 Bridge 侧 `UNAUTHORIZED` 同口径:引导重新输入,不静默失败)。
31
+ *
32
+ * ⚠️ 换令牌被拒(断言过期 / `aud` 不符 / 工号在 PMS 不存在)也归这个码:那时**该做的是重新登录
33
+ * HiWork 客户端**(断言来自网关会话),不是再输一次 PMS 密码(spec D8)——文案随 Bridge 面的错误
34
+ * 映射一起重写;本层先复用同一个常量,不为一次兑换失败给 `PmsErrorCode` 添新码。
35
+ */
29
36
  export declare const PMS_UNAUTHORIZED_MESSAGE = "\u767B\u5F55\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0\u8F93\u5165 PMS \u8D26\u53F7\u5BC6\u7801";
30
37
  /** 通用失败文案:不暴露任何 PMS 实现细节(baseURL / 路径 / 状态码 / 响应体)。 */
31
38
  export declare const PMS_GENERIC_FAILURE_MESSAGE = "PMS \u6682\u65F6\u4E0D\u53EF\u7528\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5";
@@ -51,24 +58,20 @@ export declare const PMS_TASK_OPEN_STATUSES: readonly [0, 1, 3];
51
58
  /** 「我的任务」的默认页大小(spec §3 D7)。50 条足够覆盖一个人的未完成任务,且一次请求就够。 */
52
59
  export declare const PMS_TASK_PAGE_SIZE = 50;
53
60
  /**
54
- * 一次成功登录 / 刷新换来的会话。
61
+ * 一次「网关断言 → PMS 令牌」兑换的结果(spec §4.1b 的响应投影)。
55
62
  *
56
- * `accessToken` **绝不出 Host**;`expiresAt` 是绝对时刻(epoch ms)而不是剩余秒数——剩余秒数
57
- * 一旦跨过任何一个 await 就开始漂,而「过期了没有」是授权路径上唯一的判据。
63
+ * `token` **绝不出 Host**;`expiresAt` 是服务端给的 **ISO 串**、**原样透传**——mint 端点不属于 PMS 的
64
+ * `Result` 信封,它给的时间串格式由 PMS 侧定,本层不替它归一(把它折成绝对时刻是调用方的事;当年
65
+ * `PmsTokenSet` 用 epoch ms 是因为那个时刻是**我方**自己算的——`now + expiresIn`)。
66
+ * `userId` 是「我的任务」唯一的查询键(`assigneeId`,spec D7),`username` 是 PMS 工号(仅用于展示)。
67
+ * 其余字段(`hint` 等)忽略——演进纪律同 {@link PmsTodoItem}。
58
68
  */
59
- export interface PmsTokenSet {
60
- accessToken: string;
61
- /** 7 天有效;PMS 没回时缺省(此时 access token 过期就只能重新输入账号密码)。 */
62
- refreshToken?: string;
63
- expiresAt: number;
64
- /**
65
- * PMS 的**用户 id**(登录响应 `data.userId`,spec §3 D7 要求登录时捕获)。
66
- *
67
- * 它**不是**凭据(是个人数字 id,PMS 侧内部的 `user_id`),但它是「我的任务」唯一的查询键
68
- * (`assigneeId`),所以必须跟着会话一起存下来。`refresh` 的响应里也可能有,但**不以它为准**
69
- * ——见 `src/pms-todo-bridge.ts` 的 `authorize`(刷新不改变 userId)。
70
- */
71
- userId?: number;
69
+ export interface PmsExchangeResult {
70
+ token: string;
71
+ /** ISO 串(原样透传;缺失 / 解析不了时本层兜底成 `now + 10 小时` 的 ISO 串)。 */
72
+ expiresAt: string;
73
+ userId: number;
74
+ username: string;
72
75
  }
73
76
  /**
74
77
  * 一条待办(**冻结契约**,spec §4.2 的 `TodoItem`,字段名与 PMS 的 `TodoTaskVO` 一一对应)。
@@ -207,21 +210,30 @@ export declare class PmsClient {
207
210
  private readonly log;
208
211
  constructor(options?: PmsClientOptions);
209
212
  /**
210
- * `POST /api/auth/login`:用账号密码换 JWT(access + refresh)。
213
+ * `POST {mintUrl}`:用一张网关签发的 **120 秒身份断言**换一把 **10 小时 PMS JWT**
214
+ * (spec `2026-09-21-pms-zero-input-login-design.md` §4.1b)。
211
215
  *
212
- * 失败语义(由 {@link PmsClient.call} 统一给出):
213
- * - 账号密码不对 → `UNAUTHORIZED`(HTTP 401 **或** 信封 `code:401`,两者同义);
214
- * - 其余(网络 / 超时 / 5xx / 非业务信封)→ `UPSTREAM_UNAVAILABLE`。
215
- * **本方法不重试**,调用方也不许替它重试(§7:避免用错密码连续触发 PMS 的失败锁定)。
216
- */
217
- login(username: string, password: string): Promise<PmsTokenSet>;
218
- /**
219
- * `POST /api/auth/refresh`:用 refresh token 续一次 access token。
216
+ * 与其余方法的两处形状差异都是契约给定的,不是本层偷懒:
217
+ * - **`mintUrl` 是绝对地址**,来自网关的连接器分配(`auth.mintUrl`)——它**不拼** {@link PMS_BASE_URL}:
218
+ * 那个常量是「PMS 部署地址」,而兑换端点由网关的分配清单说了算(本层不猜也不改写,见
219
+ * {@link PmsClient.send});
220
+ * - **不解 `Result` 信封**:成功体就是 `{token, expiresAt, userId, username}`,失败体是
221
+ * `{error:"assertion_rejected"}` 这类——两者都不是业务信封。
222
+ *
223
+ * 失败语义:
224
+ * - HTTP 401 / 403(断言过期、`aud` 不符、签名不过、工号在 PMS 不存在)→ `UNAUTHORIZED`:用户该做的
225
+ * 是**重新登录 HiWork 客户端**(断言来自网关会话),不是再输一次 PMS 密码(spec D8);
226
+ * - 其余(网络 / 超时 / 400 / 503 / 非 JSON / 2xx 却形状不符)→ `UPSTREAM_UNAVAILABLE`。
220
227
  *
221
- * refresh token 失效(7 天到期 / 已被 PMS 撤销)→ `UNAUTHORIZED`,此时**只能**重新输入账号密码;
222
- * 网络问题 → `UPSTREAM_UNAVAILABLE`(那是「稍后重试」,不是「要重新登录」——两者对用户是两件事)。
228
+ * **本方法不重试**:断言是一次性的(PMS 侧拿 `jti` 做 2 分钟防重放),要重试必须由调用方**换一张新
229
+ * 断言**再调——重发同一张只会换来第二个 403(spec §6)。
230
+ *
231
+ * @param mintUrl - 网关分配里的 `auth.mintUrl`(绝对 URL)。
232
+ * @param assertion - 网关签发的短命断言(**不进日志、不落盘**,用完即弃)。
233
+ * @param deviceRef - 设备标识串(可空;只进 PMS 侧审计,不参与鉴权)。
234
+ * @returns 令牌与它的元数据(`token` 交给 Host 内存,见 `src/pms-todo-bridge.ts`)。
223
235
  */
224
- refresh(refreshToken: string): Promise<PmsTokenSet>;
236
+ exchangeAssertion(mintUrl: string, assertion: string, deviceRef: string): Promise<PmsExchangeResult>;
225
237
  /**
226
238
  * `GET /api/todo/today`:今日待办(⚠️ 口径比名字宽:**今天 + 明天 + 无截止日期**都算,spec §D4)。
227
239
  *
@@ -280,7 +292,21 @@ export declare class PmsClient {
280
292
  */
281
293
  completeTask(accessToken: string, taskId: number): Promise<PmsTaskStatusResult>;
282
294
  /**
283
- * 发一次调用并解开 `Result` 信封。**所有错误都在这里收敛**,返回值只有 `data`。
295
+ * 发一次请求:拼地址、塞请求头、按 {@link PmsClientOptions.timeoutMs} 超时。**只有传输**。
296
+ *
297
+ * 与 {@link PmsClient.call} 的分工:`call` 负责 `Result` 信封与业务码,本方法不管响应体是什么形状
298
+ * ——换令牌那条路({@link PmsClient.exchangeAssertion})既不解信封也不认业务码,但**超时口径与请求头
299
+ * 必须与其余调用逐字相同**(spec §7:统一 10s、令牌只在有身份时发),所以两条路共用这一处,
300
+ * 而不是各抄一份、将来各自漂移。
301
+ *
302
+ * @param url - 相对路径(拼 {@link PmsClient.baseURL})或**绝对 URL**(原样使用)。
303
+ * @param opts - 方法 / 可选 JSON 体 / 可选 Bearer 令牌。
304
+ * @returns 原始 `Response`:状态码由调用方判定,本方法不把任何状态码当成错误。
305
+ */
306
+ private send;
307
+ /**
308
+ * 发一次调用并解开 `Result` 信封。**PMS 的业务错误都在这里收敛**,返回值只有 `data`
309
+ * (传输层的失败已在 {@link PmsClient.send} 收敛)。
284
310
  *
285
311
  * 顺序是刻意的:**先看 HTTP 401,再解信封**——PMS 未登录时回的是 Spring Security 形状
286
312
  * (`{path, code:"AUTH_001", message}`),它的 `message` 是给浏览器看跳登录页的,既不是业务文案
@@ -288,6 +314,4 @@ export declare class PmsClient {
288
314
  * 误判成「上游故障」而让用户不知道要重新输入密码。
289
315
  */
290
316
  private call;
291
- /** 登录 / 刷新的 `data` → {@link PmsTokenSet}。200 却不给 token 视为契约破坏(上游不可用)。 */
292
- private tokenSet;
293
317
  }