hiwork-core 0.0.30 → 0.0.35
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/cordis.patch.yml +15 -1
- package/lib/ai-router-client.js +394 -2
- package/lib/client-policy.js +2 -2
- package/lib/client.js +2413 -439
- package/lib/image-rpc.js +306 -0
- package/lib/imagegen.js +424 -0
- package/lib/index.js +19212 -7694
- package/lib/knowledge-config.js +91 -1
- package/lib/provision-session.js +10 -2
- package/lib/tool-names.js +10 -0
- package/lib/tools.js +205 -0
- package/lib/types/ai-router-client.d.ts +255 -2
- package/lib/types/client/about-section.d.ts +27 -7
- package/lib/types/client/controller.d.ts +17 -9
- package/lib/types/client/feature-center.d.ts +46 -1
- package/lib/types/client/feature-registry.d.ts +36 -3
- package/lib/types/client/image-cards.d.ts +218 -0
- package/lib/types/client/image-styles.d.ts +9 -0
- package/lib/types/client/images-data.d.ts +127 -0
- package/lib/types/client/images-page.d.ts +97 -0
- package/lib/types/client/index.d.ts +0 -1
- package/lib/types/client/login-gate.d.ts +2 -2
- package/lib/types/client/logo.d.ts +7 -2
- package/lib/types/client/menu-preferences.d.ts +163 -0
- package/lib/types/client/menu-section.d.ts +5 -0
- package/lib/types/client/menu-store.d.ts +101 -0
- package/lib/types/client/styles.d.ts +16 -0
- package/lib/types/client/types.d.ts +9 -0
- package/lib/types/image-rpc.d.ts +142 -0
- package/lib/types/imagegen.d.ts +241 -0
- package/lib/types/index.d.ts +70 -8
- package/lib/types/knowledge-config.d.ts +50 -2
- package/lib/types/tool-names.d.ts +11 -0
- package/lib/types/tools.d.ts +80 -0
- package/lib/types/user-agent.d.ts +31 -0
- package/lib/user-agent.js +54 -0
- package/lib/web-search.js +2 -2
- package/package.json +34 -24
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 「我的图片」画廊页(中央 feature `id: 'images'`、`order: 60`,**core 自己注册**)。
|
|
3
|
+
*
|
|
4
|
+
* 形态(spec §6.5):**左列表 + 右预览**。
|
|
5
|
+
* - 列表:分页(默认 20,「加载更多」追加),每行是提示词摘要 + 尺寸 + 时间 + 大小;
|
|
6
|
+
* - 预览:**只有选中某一条才 `fetch` 那张图**(这是「不一次拉几十 MB」的唯一守卫——
|
|
7
|
+
* 用例里断言未选中时零 `fetch` 调用);拿回来的字节用 **data URL** 渲染,
|
|
8
|
+
* **不写盘、不建指向本地文件的 `<img src>`**;
|
|
9
|
+
* - 动作:**「存到工作区」**(成功后行内显示落点路径)与**「复制提示词」**;
|
|
10
|
+
* **不做**「再来一张」——画廊是中央 takeover,退到会话才能发消息,跨屏填草稿的收益
|
|
11
|
+
* 不抵复杂度(列入 M1 评估)。
|
|
12
|
+
*
|
|
13
|
+
* 为什么注册在 core 而不是新插件:这两条 RPC 只走 `connection.rpc`(`/hiwork-image`),
|
|
14
|
+
* 而网关访问必须留在 core 的 Host 半边;本页除了 `connection.rpc` 与 feature 注册表
|
|
15
|
+
* 之外不依赖任何其它插件。历史读的是**既有**的员工自助端点,与 `image.enabled` 开关
|
|
16
|
+
* 无关(关开关只关「生成」,不关「查看」)。
|
|
17
|
+
*
|
|
18
|
+
* client bundle 纯度:不 import 任何 `@deepseek-ai/*` 运行时值,也不 import Host 代码。
|
|
19
|
+
*/
|
|
20
|
+
import { type ReactElement } from 'react';
|
|
21
|
+
import type { FeatureRegistry, HiWorkFeature } from './feature-registry.ts';
|
|
22
|
+
import { type ImageDataSource } from './images-data.ts';
|
|
23
|
+
/** 中央 feature id(冻结契约)。 */
|
|
24
|
+
export declare const IMAGES_FEATURE_ID = "images";
|
|
25
|
+
/** 中央 feature 排序:跟在 workbench 10 / automation 20 / knowledge 30 / capabilities 40 / office 50 之后。 */
|
|
26
|
+
export declare const IMAGES_FEATURE_ORDER = 60;
|
|
27
|
+
/** 侧栏菜单文案(core 未注册 locale 命名空间,硬编码中文)。 */
|
|
28
|
+
export declare const IMAGES_FEATURE_LABEL = "\u6211\u7684\u56FE\u7247";
|
|
29
|
+
/** 注册段的 effect 标签(诊断用;也是接线守卫的锚点)。 */
|
|
30
|
+
export declare const IMAGES_FEATURE_EFFECT_LABEL = "hiwork-core: images feature";
|
|
31
|
+
/**
|
|
32
|
+
* 菜单图标:16 网格、`currentColor`、描边 1.2(官方图标集里没有「我的图片」这一格,
|
|
33
|
+
* 按官方比例自绘,惯例同 hiwork-im / feature-center 的自绘图标)。
|
|
34
|
+
*/
|
|
35
|
+
export declare const IMAGES_FEATURE_ICON = "<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><rect x=\"1.6\" y=\"2.6\" width=\"12.8\" height=\"10.8\" rx=\"1.8\" stroke=\"currentColor\" stroke-width=\"1.2\"></rect><circle cx=\"5.7\" cy=\"6.3\" r=\"1.05\" fill=\"currentColor\"></circle><path d=\"M2.6 11.4l3.1-2.9c.4-.4 1-.4 1.4 0l2.1 2 1.4-1.3c.4-.4 1-.4 1.4 0l1.4 1.3\" stroke=\"currentColor\" stroke-width=\"1.2\" stroke-linecap=\"round\"></path></svg>";
|
|
36
|
+
/** 空态(spec §6.5 逐字)。 */
|
|
37
|
+
export declare const IMAGES_EMPTY_TEXT = "\u8FD8\u6CA1\u6709\u751F\u6210\u8FC7\u56FE\u7247";
|
|
38
|
+
/** 空态的第二句(告诉用户怎么产生第一张)。 */
|
|
39
|
+
export declare const IMAGES_EMPTY_HINT = "\u5728\u4F1A\u8BDD\u91CC\u8BF4\u300E\u753B\u4E00\u5F20\u2026\u300F\u5C31\u80FD\u751F\u6210";
|
|
40
|
+
/** 加载中。 */
|
|
41
|
+
export declare const IMAGES_LOADING_TEXT = "\u6B63\u5728\u52A0\u8F7D\u56FE\u7247\u8BB0\u5F55\u2026";
|
|
42
|
+
/** 预览区未选中任何一条时的占位。 */
|
|
43
|
+
export declare const IMAGES_PREVIEW_PLACEHOLDER = "\u4ECE\u5DE6\u4FA7\u9009\u4E00\u5F20\u56FE\u7247\u67E5\u770B";
|
|
44
|
+
/** 预览图正在拉取(这是唯一会拉几十 MB 的地方,必须让用户看见)。 */
|
|
45
|
+
export declare const IMAGES_PREVIEW_LOADING_TEXT = "\u6B63\u5728\u52A0\u8F7D\u56FE\u7247\u2026";
|
|
46
|
+
/** `image.enabled=false` 时仍可看历史,但要说明「生成」被关掉了(spec §7.2 V11)。 */
|
|
47
|
+
export declare const IMAGES_DISABLED_NOTE = "\u7BA1\u7406\u5458\u8FD8\u6CA1\u5F00\u542F\u56FE\u7247\u751F\u6210";
|
|
48
|
+
/** 缺失字段的占位符(**不是 0**)。 */
|
|
49
|
+
export declare const IMAGES_MISSING_TEXT = "\u2014";
|
|
50
|
+
/** 动作文案。 */
|
|
51
|
+
export declare const IMAGES_SAVE_TEXT = "\u5B58\u5230\u5DE5\u4F5C\u533A";
|
|
52
|
+
export declare const IMAGES_SAVE_SAVED_PREFIX = "\u5DF2\u4FDD\u5B58\u5230 ";
|
|
53
|
+
export declare const IMAGES_SAVE_RUNNING_TEXT = "\u6B63\u5728\u4FDD\u5B58\u2026";
|
|
54
|
+
export declare const IMAGES_COPY_TEXT = "\u590D\u5236\u63D0\u793A\u8BCD";
|
|
55
|
+
export declare const IMAGES_MORE_TEXT = "\u52A0\u8F7D\u66F4\u591A";
|
|
56
|
+
export declare const IMAGES_RETRY_TEXT = "\u91CD\u8BD5";
|
|
57
|
+
export declare const IMAGES_SAVE_HINT = "\u56FE\u7247\u4F1A\u5B58\u5230\u4F60\u7684\u9ED8\u8BA4\u5DE5\u4F5C\u533A\uFF08~/HiWork/Default\uFF09\u7684\u300C\u56FE\u7247\u300D\u76EE\u5F55\u3002";
|
|
58
|
+
/** 复制反馈(同卡片口径:不可用只把提示切到「复制失败」)。 */
|
|
59
|
+
export declare const IMAGES_COPY_HINTS: {
|
|
60
|
+
readonly copied: "已复制";
|
|
61
|
+
readonly failed: "复制失败";
|
|
62
|
+
};
|
|
63
|
+
/** 画廊页属性:只依赖一个窄取数面(测试可注入假实现)。 */
|
|
64
|
+
export interface ImagesPageProps {
|
|
65
|
+
readonly source: ImageDataSource;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* 「我的图片」画廊页。
|
|
69
|
+
* @param props - 取数面。
|
|
70
|
+
* @returns 页面元素。
|
|
71
|
+
*/
|
|
72
|
+
export declare function ImagesPage({ source }: ImagesPageProps): ReactElement;
|
|
73
|
+
/**
|
|
74
|
+
* 注册所需的上下文面(只需要 `effect`)。
|
|
75
|
+
*
|
|
76
|
+
* 为什么写成窄面而不是要求真实 `ClientContext`:注册的**生命周期**才是这条链路的
|
|
77
|
+
* 不变量——`ctx.effect(() => registry.register(...))` 让注销随插件 fiber 走,插件热替换
|
|
78
|
+
* 后注册表里不会留下指向旧实例的条目。窄面让这条不变量可以单测(假 scope 记录
|
|
79
|
+
* factory 并手动触发清理),也不必在测试里拉起整个 cordis。
|
|
80
|
+
*/
|
|
81
|
+
export interface ImagesFeatureScope {
|
|
82
|
+
/** cordis 的 effect 体**必须**返回 disposer(`SyncEffect` 不含纯 void),与真实 ctx.effect 同形。 */
|
|
83
|
+
effect(factory: () => () => void, label?: string): unknown;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* 构造 feature 定义(纯函数:测试可直接断言 id / order / label / icon)。
|
|
87
|
+
* @param source - 画廊取数面。
|
|
88
|
+
* @returns feature 定义。
|
|
89
|
+
*/
|
|
90
|
+
export declare function imagesFeature(source: ImageDataSource): HiWorkFeature;
|
|
91
|
+
/**
|
|
92
|
+
* 注册「我的图片」中央页(**用 `ctx.effect` 包住**:disposer 幂等,注销后从注册表消失)。
|
|
93
|
+
* @param scope - 带 `effect` 的上下文(`apply` 传真实 client ctx)。
|
|
94
|
+
* @param registry - core 自己的 Feature Center 注册表。
|
|
95
|
+
* @param source - 画廊取数面(经 `connection.rpc` 指向 `/hiwork-image`)。
|
|
96
|
+
*/
|
|
97
|
+
export declare function installImagesFeature(scope: ImagesFeatureScope, registry: FeatureRegistry, source: ImageDataSource): void;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { Context as ClientContext } from '@deepseek-ai/cordis';
|
|
2
2
|
export type { FeatureRenderContext, FeatureRegistry, HiWorkFeature } from './feature-registry.ts';
|
|
3
3
|
export type { HiWorkCoreClientFace } from './contracts.ts';
|
|
4
|
-
/** 浏览器插件契约(dsh client module system)。connection 承载 loopback RPC(见 src/client/usage-data.ts)。 */
|
|
5
4
|
export declare const inject: string[];
|
|
6
5
|
/** 挂载网关模型设置 section(一级 tab)与未登录弹窗。 */
|
|
7
6
|
export declare function apply(ctx: ClientContext): void;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ConfigForm } from '@deepseek-ai/dsh-client-ui-settings/client';
|
|
2
2
|
import type { CredentialStorePort } from './controller.ts';
|
|
3
3
|
import type { LoginError } from './types.ts';
|
|
4
4
|
import { type AiRouterCardSettings } from './controller.ts';
|
|
@@ -19,4 +19,4 @@ export declare function loginGateVisible(snapshot: {
|
|
|
19
19
|
};
|
|
20
20
|
} | undefined;
|
|
21
21
|
} | undefined): boolean;
|
|
22
|
-
export declare function mountLoginGate(
|
|
22
|
+
export declare function mountLoginGate(form: ConfigForm<AiRouterCardSettings>, storeCredential: CredentialStorePort): () => void;
|
|
@@ -2,5 +2,10 @@
|
|
|
2
2
|
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
|
-
/**
|
|
6
|
-
|
|
5
|
+
/**
|
|
6
|
+
* HiWork 产品版本兜底常量:只在**无桌面壳**(纯 web / 浏览器 dev,`/hiwork-app` 频道
|
|
7
|
+
* 取不到值)时显示——侧栏 brand 行与设置「关于」分区的回退值。桌面端在壳内永远走
|
|
8
|
+
* RPC 动态取真实版本,本常量不参与;跟随桌面端当前发布线手工维护(2026-09-25 对齐
|
|
9
|
+
* 1.0.7)。
|
|
10
|
+
*/
|
|
11
|
+
export declare const HIWORK_VERSION = "1.0.7";
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 侧栏菜单的每用户偏好(settings 命名空间 `hiwork-menu` 的读侧模型与纯函数)。
|
|
3
|
+
*
|
|
4
|
+
* 为什么单独一份:菜单适配器(`feature-center.ts`)与设置分区(`menu-section.tsx`)必须按**同一套**
|
|
5
|
+
* 规则回答「谁在侧栏、谁进『更多』、谁被隐藏、按什么顺序」,规则只允许有一处实现——这里。
|
|
6
|
+
* 本模块不碰 DOM、不碰 cordis,全部是可单测的纯函数。
|
|
7
|
+
*
|
|
8
|
+
* 命名空间字面量 `hiwork-menu` 在 host 半边(`src/index.ts` 的 `MENU_NS`)另有一份:client 半边的
|
|
9
|
+
* 打包 rootDir 是 `src/client`,不能 import host 模块(与 `ai-router` 同款处理)。两侧一致性由
|
|
10
|
+
* `tests/menu-namespace.spec.ts` 静态守着,改一处必须改两处。
|
|
11
|
+
*/
|
|
12
|
+
import { type MenuFeature } from './feature-registry.ts';
|
|
13
|
+
/** settings 命名空间(host 侧同名注册;两侧字面量必须逐字一致)。 */
|
|
14
|
+
export declare const HIWORK_MENU_NS = "hiwork-menu";
|
|
15
|
+
/**
|
|
16
|
+
* 未自定义时侧栏直显的条数上限(不含原生的「新会话」)。
|
|
17
|
+
*
|
|
18
|
+
* 达到上限之后,用户没有明确标记的入口一律进「更多」——这正是「以后再装插件也不会把侧栏顶高」
|
|
19
|
+
* 的机制(新插件的 id 不在偏好里 = 未标记 = 超出上限就走「更多」)。
|
|
20
|
+
*/
|
|
21
|
+
export declare const DEFAULT_SIDEBAR_LIMIT = 5;
|
|
22
|
+
/** 菜单里「更多」那一行的固定 id(与 feature id 同域,但永不来自注册表)。 */
|
|
23
|
+
export declare const MORE_MENU_ITEM_ID = "__hiwork_more__";
|
|
24
|
+
/** 「更多」这一行的文案(hiwork-core 未注册 locale 命名空间,沿用硬编码中文)。 */
|
|
25
|
+
export declare const MORE_MENU_LABEL = "\u66F4\u591A";
|
|
26
|
+
/**
|
|
27
|
+
* 设置分区与分区标题共用文案。
|
|
28
|
+
*
|
|
29
|
+
* `settings.section` 注册的 label 同时是设置弹窗导航按钮的可见文本,`openSettingsSection`
|
|
30
|
+
* 按它精确匹配跳转(见 `settings-navigation.ts`),改措辞要一起改。
|
|
31
|
+
*/
|
|
32
|
+
export declare const MENU_SECTION_LABEL = "\u4FA7\u680F\u83DC\u5355";
|
|
33
|
+
/** 一个入口的三种去向。 */
|
|
34
|
+
export declare const MENU_PLACEMENTS: readonly ["sidebar", "more", "hidden"];
|
|
35
|
+
export type MenuPlacement = (typeof MENU_PLACEMENTS)[number];
|
|
36
|
+
/**
|
|
37
|
+
* 一个用户的侧栏菜单偏好(settings 命名空间的解析值)。
|
|
38
|
+
*
|
|
39
|
+
* 四个字段都是 `string[]`(feature id),不用三态对象数组:读写都发生在「把某个 id 挪一下」这一个
|
|
40
|
+
* 动作上,分开存可以直接把「一次移动」写成一次原子 `mutate`,也不怕以后加态。
|
|
41
|
+
*/
|
|
42
|
+
export interface MenuPreferences {
|
|
43
|
+
/** 展示顺序;未列出的排在后面,按插件注册的 `order`。 */
|
|
44
|
+
readonly order: readonly string[];
|
|
45
|
+
/** 用户明确要求显示在侧栏的 id。 */
|
|
46
|
+
readonly sidebar: readonly string[];
|
|
47
|
+
/** 用户明确要求收进「更多」的 id。 */
|
|
48
|
+
readonly more: readonly string[];
|
|
49
|
+
/** 用户明确要求隐藏的 id(侧栏与「更多」都不出现)。 */
|
|
50
|
+
readonly hidden: readonly string[];
|
|
51
|
+
}
|
|
52
|
+
/** 空偏好:等价于「用户从未自定义」,分组按 {@link DEFAULT_SIDEBAR_LIMIT} 与注册顺序推导。 */
|
|
53
|
+
export declare const EMPTY_MENU_PREFERENCES: MenuPreferences;
|
|
54
|
+
/** 一次原子写入要落的一个字段操作(结构上与 settings 的 path op 相同,保持本模块无依赖)。 */
|
|
55
|
+
export interface MenuPreferenceOp {
|
|
56
|
+
readonly op: 'set';
|
|
57
|
+
readonly path: readonly string[];
|
|
58
|
+
/** 四个字段都是 id 数组,所以这里能给出比 settings 的 `unknown` 更紧的形状。 */
|
|
59
|
+
readonly value: readonly string[];
|
|
60
|
+
}
|
|
61
|
+
/** 分组结果:侧栏直显项与「更多」项(均按有效顺序,都不含原生的「新会话」)。 */
|
|
62
|
+
export interface MenuLayout {
|
|
63
|
+
readonly sidebar: readonly MenuFeature[];
|
|
64
|
+
readonly more: readonly MenuFeature[];
|
|
65
|
+
}
|
|
66
|
+
/** 当前偏好的可编辑快照(设置分区与菜单共用;`status` 决定控件是否可用)。 */
|
|
67
|
+
export interface MenuPreferencesView {
|
|
68
|
+
readonly status: 'loading' | 'ready' | 'unavailable';
|
|
69
|
+
readonly writable: boolean;
|
|
70
|
+
readonly value: MenuPreferences;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* 把 settings 的解析值归一成偏好。
|
|
74
|
+
*
|
|
75
|
+
* 宽容优先:settings 文档可能被外部编辑过(旧版本、手工改过的 JSON、缺字段),这里只取认得的形状,
|
|
76
|
+
* 剩下的当空——菜单是导航入口,宁可退回默认分组,也不能因为偏好坏了就不出菜单。
|
|
77
|
+
* @param raw - 命名空间解析值(任意形状)。
|
|
78
|
+
* @returns 归一后的偏好。
|
|
79
|
+
*/
|
|
80
|
+
export declare function normalizeMenuPreferences(raw: unknown): MenuPreferences;
|
|
81
|
+
/**
|
|
82
|
+
* 某个入口当前被明确标记的去向。
|
|
83
|
+
*
|
|
84
|
+
* 同一 id 同时出现在多个数组时按 `hidden` > `more` > `sidebar` 取舍(正常路径不会产生这种数据,
|
|
85
|
+
* 这是读侧兜底)。
|
|
86
|
+
* @param preferences - 偏好。
|
|
87
|
+
* @param id - feature id。
|
|
88
|
+
* @returns 明确标记时返回该去向,未标记返回 `undefined`。
|
|
89
|
+
*/
|
|
90
|
+
export declare function placementOf(preferences: MenuPreferences, id: string): MenuPlacement | undefined;
|
|
91
|
+
/**
|
|
92
|
+
* 有效顺序:用户 `order` 里出现且当前在册的 id(按用户次序),再补上其余在册 id(按注册顺序)。
|
|
93
|
+
* @param features - 当前菜单条目全集(已注册 feature + 非注册表代理项,结构上都是 {@link MenuFeature})。
|
|
94
|
+
* @param preferences - 偏好。
|
|
95
|
+
* @returns 归一后的 id 序列(长度 = 在册条目数)。
|
|
96
|
+
*/
|
|
97
|
+
export declare function orderedFeatureIds(features: readonly MenuFeature[], preferences: MenuPreferences): string[];
|
|
98
|
+
/**
|
|
99
|
+
* 按偏好把当前注册的 feature 分成「侧栏直显」与「更多」两组。
|
|
100
|
+
*
|
|
101
|
+
* 规则(spec §2,实施期不得另立一套):
|
|
102
|
+
* - `hidden` 的既不进侧栏也不进「更多」(**不占名额**:腾出的位置由后面的入口补上);
|
|
103
|
+
* - 明确标记为侧栏的**永远直显**;
|
|
104
|
+
* - 明确标记为「更多」的**永远进更多,并且占掉一个侧栏名额**(`budget -= 1`)——
|
|
105
|
+
* 这条是「用户在设置页把一项挪进『更多』,侧栏就真的少一行」的实现:不占名额的话,
|
|
106
|
+
* 腾出的位置会被后面的入口立刻补上,行数不变,用户会看到「点了没反应」;
|
|
107
|
+
* - 未标记的按有效顺序填空,填到侧栏凑满 `budget` 为止,其余进「更多」。
|
|
108
|
+
*
|
|
109
|
+
* 名额(`budget`)只统计**已注册**的标记项:偏好里残留的未知 id(别的设备装的插件)不该
|
|
110
|
+
* 在本机吃掉名额。
|
|
111
|
+
* @param features - 当前菜单条目全集。
|
|
112
|
+
* @param preferences - 偏好。
|
|
113
|
+
* @returns 两组(各自保持有效顺序)。
|
|
114
|
+
*/
|
|
115
|
+
export declare function menuLayout(features: readonly MenuFeature[], preferences: MenuPreferences): MenuLayout;
|
|
116
|
+
/**
|
|
117
|
+
* 设置某个入口的去向(设置分区的一次点击)。
|
|
118
|
+
*
|
|
119
|
+
* **只动去向数组,不碰 `order`**:`order` 只由显式排序(拖拽 / 键盘上·下移)写。
|
|
120
|
+
* 早先这里会顺手把 id 追加进 `order`,后果是「点一下『更多』」会把该项钉到有效顺序首位
|
|
121
|
+
* (`orderedFeatureIds` 先排 `order` 里的 id)——设置页的列表会毫无理由地重排,实机可见。
|
|
122
|
+
* @param preferences - 当前偏好。
|
|
123
|
+
* @param id - feature id。
|
|
124
|
+
* @param placement - 目标去向。
|
|
125
|
+
* @returns 新偏好;与该去向已经一致时原样返回(调用方据此跳过写盘)。
|
|
126
|
+
*/
|
|
127
|
+
export declare function setMenuPlacement(preferences: MenuPreferences, id: string, placement: MenuPlacement): MenuPreferences;
|
|
128
|
+
/**
|
|
129
|
+
* 把某个入口移到有效顺序的第 `targetIndex` 位(拖拽落点与键盘上/下移共用)。
|
|
130
|
+
*
|
|
131
|
+
* 只写 `order` 一个字段:去向(侧栏/更多/隐藏)是另一维,排序动作不碰它。
|
|
132
|
+
* @param preferences - 当前偏好。
|
|
133
|
+
* @param features - 当前注册的 feature。
|
|
134
|
+
* @param id - 被移动的 feature id。
|
|
135
|
+
* @param targetIndex - 目标下标(越界自动收敛到两端)。
|
|
136
|
+
* @returns 新偏好;位置未变时原样返回。
|
|
137
|
+
*/
|
|
138
|
+
export declare function moveMenuEntry(preferences: MenuPreferences, features: readonly MenuFeature[], id: string, targetIndex: number): MenuPreferences;
|
|
139
|
+
/**
|
|
140
|
+
* 把某个入口放到另一个入口之前/之后(设置页拖拽落点)。
|
|
141
|
+
*
|
|
142
|
+
* 与 {@link moveMenuEntry} 的下标语义区分开:拖拽时用户看到的落点是「在谁的前/后」,
|
|
143
|
+
* 换算成下标要考虑自身被摘掉后的位移,直接用下标会差一位(拖到目标行下方却落在上方)。
|
|
144
|
+
* @param preferences - 当前偏好。
|
|
145
|
+
* @param features - 当前注册的 feature。
|
|
146
|
+
* @param id - 被拖动的 feature id。
|
|
147
|
+
* @param targetId - 落点所在的 feature id。
|
|
148
|
+
* @param position - 落在目标之前还是之后。
|
|
149
|
+
* @returns 新偏好;结果与当前顺序相同时原样返回。
|
|
150
|
+
*/
|
|
151
|
+
export declare function moveMenuEntryNear(preferences: MenuPreferences, features: readonly MenuFeature[], id: string, targetId: string, position: 'before' | 'after'): MenuPreferences;
|
|
152
|
+
/** 两份偏好的四个字段是否逐项相同(数组按内容比较)。 */
|
|
153
|
+
export declare function sameMenuPreferences(left: MenuPreferences, right: MenuPreferences): boolean;
|
|
154
|
+
/**
|
|
155
|
+
* 两份偏好之间真正变化的字段(只写变化的那几个)。
|
|
156
|
+
*
|
|
157
|
+
* 用 `set` 而不是整段替换:设置分区在弹窗里跑,用户改一项就走一次写盘,没必要把另外三个字段一起
|
|
158
|
+
* 重写(写盘带着 revision 栅栏,写得越少越不容易撞上并发的自我冲突)。
|
|
159
|
+
* @param current - 写盘前已知的偏好。
|
|
160
|
+
* @param next - 目标偏好。
|
|
161
|
+
* @returns 按字段的路径操作;无变化时为空数组。
|
|
162
|
+
*/
|
|
163
|
+
export declare function menuPreferenceOps(current: MenuPreferences, next: MenuPreferences): MenuPreferenceOp[];
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { InjectFace, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
|
|
2
|
+
import type { MenuSectionFace } from './menu-store.ts';
|
|
3
|
+
export type MenuSectionProps = PropsRuntime<'settings.section'> & InjectFace<MenuSectionFace>;
|
|
4
|
+
/** 设置页「侧栏菜单」分区。 */
|
|
5
|
+
export declare function MenuSection(props: MenuSectionProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 「侧栏菜单」设置分区的状态控制器:把 `ai-router` 配置表单里的菜单偏好(`menu` 字段)
|
|
3
|
+
* 与 feature 注册表合成一份可渲染状态,并把设置页的动作翻译成一次原子写盘。
|
|
4
|
+
*
|
|
5
|
+
* rc.2 起 settings 只服务「有 profile entry 的 Config」,纯客户端偏好的 `hiwork-menu`
|
|
6
|
+
* 命名空间不复存在——偏好并入 `ai-router` entry 的 `menu` volatile 字段,读写都通过与
|
|
7
|
+
* 网关卡片**同一个** ConfigForm 实例(configForms 按 entry id 共享控制器,见
|
|
8
|
+
* `src/client/index.ts`),菜单适配器与设置分区因此天然同源。
|
|
9
|
+
* 本模块不碰 DOM,可单测。
|
|
10
|
+
*
|
|
11
|
+
* 写盘纪律(spec §4):
|
|
12
|
+
* - 只在 `status === 'ready' && writable` 时写;否则动作是 no-op(`menu-section` 已禁用控件)。
|
|
13
|
+
* - 一次动作 = 一次 `mutate`,只带真正变化的字段(见 `menuPreferenceOps`,路径带 `menu` 前缀)。
|
|
14
|
+
* - 目标去向与当前**生效**去向一致时不写盘:用户在设置页点到已经成立的状态不该产生写入,
|
|
15
|
+
* 否则「点『侧栏』」会给一项已直显的入口加上显式标记,反而挤掉后面某个入口的名额。
|
|
16
|
+
*/
|
|
17
|
+
import type { SnapshotStore } from '@deepseek-ai/dsh-client-store';
|
|
18
|
+
import type { ConfigForm } from '@deepseek-ai/dsh-client-ui-settings/client';
|
|
19
|
+
import type { AiRouterCardSettings } from './controller.ts';
|
|
20
|
+
import type { FeatureRegistry, MenuFeature } from './feature-registry.ts';
|
|
21
|
+
import { type MenuPlacement, type MenuPreferences } from './menu-preferences.ts';
|
|
22
|
+
/** 设置页一行:一个入口的当前去向与在列表中的位置。 */
|
|
23
|
+
export interface MenuEntryView {
|
|
24
|
+
/** feature id(拖拽与写盘都按它定位)。 */
|
|
25
|
+
readonly id: string;
|
|
26
|
+
/** 菜单文案(已求值;语言变化由订阅方重建)。 */
|
|
27
|
+
readonly label: string;
|
|
28
|
+
/** 插件给的图标(内联 SVG 字符串),与侧栏菜单同源。 */
|
|
29
|
+
readonly icon: string | undefined;
|
|
30
|
+
/** **生效**去向(未标记的项也给出推导结果,控件据此显示当前状态)。 */
|
|
31
|
+
readonly placement: MenuPlacement;
|
|
32
|
+
/** 用户是否明确标记过(UI 用它区分「你选的」与「默认推导」)。 */
|
|
33
|
+
readonly marked: boolean;
|
|
34
|
+
/** 在有效顺序中的下标与总数(键盘移动与 aria-label 用)。 */
|
|
35
|
+
readonly index: number;
|
|
36
|
+
readonly total: number;
|
|
37
|
+
}
|
|
38
|
+
/** 设置分区的渲染状态。 */
|
|
39
|
+
export interface MenuSectionState {
|
|
40
|
+
/** 与 settings 作用域同义:`loading` / `ready` / `unavailable`。 */
|
|
41
|
+
readonly status: 'loading' | 'ready' | 'unavailable';
|
|
42
|
+
/** 宿主文档是否接受写入(memory 模式恒 false)。 */
|
|
43
|
+
readonly writable: boolean;
|
|
44
|
+
/** 按有效顺序的全部入口(不含原生的「新会话」)。 */
|
|
45
|
+
readonly entries: readonly MenuEntryView[];
|
|
46
|
+
/** 侧栏当前直显条数上限(= 默认上限 − 已标记「更多」的个数),UI 用来说明规则。 */
|
|
47
|
+
readonly limit: number;
|
|
48
|
+
/** 控件不可用时的原因文案;可用时为 undefined。 */
|
|
49
|
+
readonly reason: string | undefined;
|
|
50
|
+
}
|
|
51
|
+
/** 设置分区注入面(`settings.section#hiwork-menu`)。 */
|
|
52
|
+
export interface MenuSectionFace {
|
|
53
|
+
hooks: {
|
|
54
|
+
menu: SnapshotStore<MenuSectionState>;
|
|
55
|
+
};
|
|
56
|
+
actions: {
|
|
57
|
+
/** 设置某个入口的去向。 */
|
|
58
|
+
setPlacement(id: string, placement: MenuPlacement): Promise<void>;
|
|
59
|
+
/** 把某个入口上/下移一格(键盘路径)。 */
|
|
60
|
+
move(id: string, offset: number): Promise<void>;
|
|
61
|
+
/** 把某个入口拖到另一个入口之前/之后。 */
|
|
62
|
+
moveNear(id: string, targetId: string, position: 'before' | 'after'): Promise<void>;
|
|
63
|
+
/** 恢复默认(清掉全部四个字段,回到默认分组)。 */
|
|
64
|
+
reset(): Promise<void>;
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/** 侧栏菜单的设置分区控制器。 */
|
|
68
|
+
export declare class MenuStore {
|
|
69
|
+
private readonly form;
|
|
70
|
+
private readonly registry;
|
|
71
|
+
private readonly additionalFeatures?;
|
|
72
|
+
private readonly store;
|
|
73
|
+
private readonly unsubscribe;
|
|
74
|
+
/**
|
|
75
|
+
* @param form - `ai-router` entry 的配置表单(与网关卡片同一个共享实例,见 `src/client/index.ts`)。
|
|
76
|
+
* @param registry - feature 注册表(列表内容与顺序的另一个来源)。
|
|
77
|
+
* @param additionalFeatures - 追加的非注册表条目(官方「插件」代理项;可用性由调用方判断)。
|
|
78
|
+
* 缺省时列表只有注册表成员——与旧版行为一致。
|
|
79
|
+
*/
|
|
80
|
+
constructor(form: ConfigForm<AiRouterCardSettings>, registry: FeatureRegistry, additionalFeatures?: (() => readonly MenuFeature[]) | undefined);
|
|
81
|
+
/** 当前偏好(未就绪 / 值非法时按「从未自定义」处理)。 */
|
|
82
|
+
preferences(): MenuPreferences;
|
|
83
|
+
/** 订阅偏好变化(菜单适配器用;与 `preferences()` 同源)。 */
|
|
84
|
+
subscribePreferences(listener: () => void): () => void;
|
|
85
|
+
/** 菜单条目全集(注册表 + 追加的代理项),与侧栏适配器的来源同构。 */
|
|
86
|
+
private menuFeatures;
|
|
87
|
+
/** 当前渲染状态。 */
|
|
88
|
+
view(): MenuSectionState;
|
|
89
|
+
/** 清掉在这份 store 上的全部订阅(插件卸载时调用)。 */
|
|
90
|
+
dispose(): void;
|
|
91
|
+
/** 立即重算渲染状态(追加条目的可用性变化等非表单/注册表来源的变化走这里)。 */
|
|
92
|
+
refresh(): void;
|
|
93
|
+
/** 注入面:设置分区组件消费。 */
|
|
94
|
+
inject(): MenuSectionFace;
|
|
95
|
+
private setPlacement;
|
|
96
|
+
private move;
|
|
97
|
+
private moveNear;
|
|
98
|
+
private reset;
|
|
99
|
+
/** 未标记项的生效去向已由 `view()` 给出,这里不再另算一份。 */
|
|
100
|
+
private write;
|
|
101
|
+
}
|
|
@@ -130,6 +130,22 @@ export declare const brandCss: string;
|
|
|
130
130
|
* 由 client 入口 apply() 注入一次(带 id 防重)。
|
|
131
131
|
*/
|
|
132
132
|
export declare const interactionCss: string;
|
|
133
|
+
/**
|
|
134
|
+
* 生图能力的样式(会话内工具卡片 + 「我的图片」画廊页),随本插件一起分发。
|
|
135
|
+
*
|
|
136
|
+
* 类名统一 `hiwork-image-*` / `hiwork-images-*` 前缀,颜色全部走 dsh 主题变量
|
|
137
|
+
* (亮暗自动切换、与设置页分区同一套 token);只有「放大遮罩」是自带的半透明黑
|
|
138
|
+
* ——那是浮层,与宿主内部结构无关。
|
|
139
|
+
*
|
|
140
|
+
* 注入走 `image-styles.ts` 的 `ensureImagesStyle`,**必须**经 {@link ownStyleTag}
|
|
141
|
+
* 打归属标记(见下方事故记录):裸注入会被随后装载的插件认领并连带删除。
|
|
142
|
+
*/
|
|
143
|
+
export declare const imageCardsCss: string;
|
|
144
|
+
/**
|
|
145
|
+
* 「我的图片」画廊页(中央 feature `images`)的样式:左列表 + 右预览。
|
|
146
|
+
* 左栏 grid 固定 240–320px、右栏自适应;两栏各自滚动,页面本身不滚。
|
|
147
|
+
*/
|
|
148
|
+
export declare const imagesPageCss: string;
|
|
133
149
|
/**
|
|
134
150
|
* 手写注入的 <style> 必须带 DSH 的归属标记(`data-plugin`)。
|
|
135
151
|
*
|
|
@@ -32,6 +32,15 @@ export type LoginErrorCode = 'AUTH_FAILED' | 'RATE_LIMITED' | 'GATEWAY_UNREACHAB
|
|
|
32
32
|
* 消费方据此显示「本部署尚未启用工作台」而不是报错。别顺手把两边对齐。
|
|
33
33
|
*/
|
|
34
34
|
| 'WORKBENCH_DISABLED'
|
|
35
|
+
/**
|
|
36
|
+
* 本部署未配置知识库:取配置或取令牌时 503。
|
|
37
|
+
*
|
|
38
|
+
* 语义与 `OFFICE_NOT_CONFIGURED` 同族:**部署状态**,不是故障。消费方(hiwork-knowledge)
|
|
39
|
+
* 据此给出「管理员还没开启知识库」并把状态判成终局——不排重试、不把员工指向「范围未分配」。
|
|
40
|
+
* 之前 503 一律收敛成 `GATEWAY_UNREACHABLE`,插件把「管理员没配签名私钥」读成「服务暂时不可用」
|
|
41
|
+
* 并白打约 10 分钟重试,员工看到的还是「管理员尚未分配知识库访问范围」,指向错误的部门。
|
|
42
|
+
*/
|
|
43
|
+
| 'KNOWLEDGE_NOT_CONFIGURED'
|
|
35
44
|
/** 本部署未启用语音识别(管理员把开关关了):识别时 503(或 503 无 body code)。 */
|
|
36
45
|
| 'VOICE_DISABLED'
|
|
37
46
|
/** 本部署未配置语音识别上游服务(网关侧没有可用 ASR 配置):识别时 503。 */
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `/hiwork-image` loopback RPC 的 Host 实现(浏览器半边 ⇄ 生图的唯一通道)。
|
|
3
|
+
*
|
|
4
|
+
* 设计要点(与 `src/voice-rpc.ts` / `src/daily-rpc.ts` 完全同构):
|
|
5
|
+
* - 只注册**一个** channel,五个 endpoint 的白名单在 {@link IMAGE_RPC_ENDPOINTS},未知
|
|
6
|
+
* endpoint 直接 `bad-request`;一个能力一个频道,`list`/`fetch`/`save` 与
|
|
7
|
+
* `status`/`generate` 共用它(与 managed-mcp 的 snapshot/refresh/assertion 同款);
|
|
8
|
+
* - payload 严格校验:每个 endpoint 只放行它声明的字段与类型,任何其它字段一律
|
|
9
|
+
* `bad-request`(拼错字段的失败方式必须是响的,不是静默用默认值);
|
|
10
|
+
* - **网关会话 cookie 绝不离开 Host**:调用方只拿到端点结果,cookie 只在
|
|
11
|
+
* `resolveSession()` 内部出现;
|
|
12
|
+
* - 网关 `GatewayError` 的稳定 code 原样透传,message 先经
|
|
13
|
+
* {@link displayImageGatewayMessage} 消毒——baseURL / HTTP 状态码这类实现细节不进 UI;
|
|
14
|
+
* - 其余异常收敛为通用失败(code `internal`),Host 只记日志、不把堆栈或原始消息交给客户端。
|
|
15
|
+
*
|
|
16
|
+
* 两处**与 voice 不同**的语义,都写在这里免得后来的人「对齐」掉:
|
|
17
|
+
* 1. `status` 的失败**不是错误信封**——网关故障时回落 {@link IMAGE_FALLBACK_CONFIG}
|
|
18
|
+
* (`enabled:false`)。理由与语音逐字相同:这条端点回答的是「该不该显示生图能力」,
|
|
19
|
+
* 网关挂了就报红字,只会让一个本来就没有的功能显得像坏了;
|
|
20
|
+
* 2. `fetch` 有**体积闸**:超过 {@link MAX_IMAGE_BYTES} 直接拒绝(`IMAGE_TOO_LARGE`),
|
|
21
|
+
* 不把巨图塞进 loopback RPC 信封(语音那条的大对象在**上行**,本条的在下行)。
|
|
22
|
+
*
|
|
23
|
+
* 本模块不 import cordis 运行时,只依赖结构化的 {@link ImageRpcContext} 端口,便于单测伪造。
|
|
24
|
+
*/
|
|
25
|
+
import { type GatewayImageConfig } from './ai-router-client.ts';
|
|
26
|
+
import { type ImageGatewayPort, type ImageWorkspacePort } from './imagegen.ts';
|
|
27
|
+
/** RPC 频道;必须与 client 半边 `src/client/images-data.ts` 的 `IMAGE_CHANNEL` 一致(由测试锁定)。 */
|
|
28
|
+
export declare const IMAGE_RPC_CHANNEL = "/hiwork-image";
|
|
29
|
+
/** 端点白名单:生图(`status`/`generate`)与「我的图片」画廊页(`list`/`fetch`/`save`)。 */
|
|
30
|
+
export declare const IMAGE_RPC_ENDPOINTS: readonly ["status", "generate", "list", "fetch", "save"];
|
|
31
|
+
export type ImageRpcEndpoint = (typeof IMAGE_RPC_ENDPOINTS)[number];
|
|
32
|
+
/** 画廊页 `list` 的分页缺省与上限(Host 侧夹紧:网关的 page_size 是它自己的缺省)。 */
|
|
33
|
+
export declare const IMAGE_LIST_DEFAULT_PAGE = 1;
|
|
34
|
+
export declare const IMAGE_LIST_DEFAULT_PAGE_SIZE = 20;
|
|
35
|
+
export declare const IMAGE_LIST_MAX_PAGE_SIZE = 100;
|
|
36
|
+
/**
|
|
37
|
+
* 端点入参(一次性把所有端点的可放行字段列在这里,解析函数按 endpoint 取子集)。
|
|
38
|
+
*
|
|
39
|
+
* `save` 的 `prompt` 是**有意的增量**:设计 §6.5 冻结的入参是 `{ id, workspaceId? }`,
|
|
40
|
+
* 但文件名规则要求「slug 来自该记录的 prompt」——记录本体在网关,而 `save` 只拿到 id,
|
|
41
|
+
* 于是把页面手里**已经有**的那条 prompt 顺带传上来(页面没传就用 `image` 兜底)。
|
|
42
|
+
*/
|
|
43
|
+
export interface ImageRpcPayload {
|
|
44
|
+
prompt?: string | undefined;
|
|
45
|
+
size?: string | undefined;
|
|
46
|
+
page?: number | undefined;
|
|
47
|
+
pageSize?: number | undefined;
|
|
48
|
+
id?: number | undefined;
|
|
49
|
+
workspaceId?: string | undefined;
|
|
50
|
+
}
|
|
51
|
+
/** 客户端可安全展示的失败。 */
|
|
52
|
+
export interface ImageRpcFailure {
|
|
53
|
+
code: string;
|
|
54
|
+
message: string;
|
|
55
|
+
/** DSH Connection 信封要求 `details` 是对象;协议层没有额外细节时补空对象。 */
|
|
56
|
+
details: object;
|
|
57
|
+
}
|
|
58
|
+
/** 端点结果信封(与 DSH Connection 的 `ConnectionRpcResult` 同形)。 */
|
|
59
|
+
export type ImageRpcResult<T> = {
|
|
60
|
+
readonly ok: true;
|
|
61
|
+
readonly value: T;
|
|
62
|
+
} | {
|
|
63
|
+
readonly ok: false;
|
|
64
|
+
readonly error: ImageRpcFailure;
|
|
65
|
+
};
|
|
66
|
+
/** 请求侧错误(只有 `bad-request`:未知端点 / 非法 payload)。 */
|
|
67
|
+
export declare class ImageRequestError extends Error {
|
|
68
|
+
readonly code: 'bad-request';
|
|
69
|
+
constructor(code: 'bad-request', message: string);
|
|
70
|
+
}
|
|
71
|
+
/** Host 侧 RPC 依赖的最小上下文(结构兼容 cordis Context,便于单测伪造)。 */
|
|
72
|
+
export interface ImageRpcConnection {
|
|
73
|
+
rpc: {
|
|
74
|
+
handle(channel: string, handler: (endpoint: string, payload: unknown, signal: AbortSignal) => Promise<unknown>, options: {
|
|
75
|
+
authority: 'loopback';
|
|
76
|
+
}): () => Promise<void> | void;
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
export interface ImageRpcContext {
|
|
80
|
+
readonly connection: ImageRpcConnection;
|
|
81
|
+
readonly logger: {
|
|
82
|
+
warn(message: string): void;
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Host 侧取数依赖:会话解析 + 当前 baseURL 下的网关客户端 + 工作区(画廊页 `save` 用)。
|
|
87
|
+
* 形状就是 `src/imagegen.ts` 的 {@link ImagegenDeps} 再加一个 `workspaces`。
|
|
88
|
+
*/
|
|
89
|
+
export interface ImageRpcDeps {
|
|
90
|
+
/**
|
|
91
|
+
* 解析网关会话 cookie。未登录 / 会话缺失返回 undefined。
|
|
92
|
+
* **返回值只在本模块内部使用,绝不出现在 RPC 响应里。**
|
|
93
|
+
*/
|
|
94
|
+
resolveSession(): Promise<string | undefined>;
|
|
95
|
+
/** 当前 baseURL 对应的网关客户端;baseURL 未配置时返回 undefined。 */
|
|
96
|
+
gateway(): ImageGatewayPort | undefined;
|
|
97
|
+
/** 工作区注册表读取面(画廊页 `save` 的 `workspaceId` → 目录路径)。 */
|
|
98
|
+
workspaces?: ImageWorkspacePort | undefined;
|
|
99
|
+
/** `image-config` 读取器(带 ≤5 分钟缓存);缺省直接读网关。 */
|
|
100
|
+
imageConfig?: ((sessionCookie: string) => Promise<GatewayImageConfig | undefined>) | undefined;
|
|
101
|
+
/** 时钟(测试注入)。 */
|
|
102
|
+
now?: (() => Date) | undefined;
|
|
103
|
+
/** Host 日志出口(只记事件与错误类型,不记会话值、不记 base64)。 */
|
|
104
|
+
log?: ((message: string) => void) | undefined;
|
|
105
|
+
}
|
|
106
|
+
/** 各稳定错误码的用户可见文案(与卡片侧 `IMAGE_ERROR_TEXT` 逐字对齐)。 */
|
|
107
|
+
export { IMAGE_GENERIC_FAILURE_MESSAGE, IMAGE_MESSAGE_FALLBACK, IMAGE_SESSION_EXPIRED_MESSAGE, displayImageGatewayMessage, } from './imagegen.ts';
|
|
108
|
+
/**
|
|
109
|
+
* `status` 端点读不到配置时的回落值(**成功信封**,见模块头注释第 1 条):
|
|
110
|
+
* 能力消失,而不是弹一条用户看不懂的错误。
|
|
111
|
+
*/
|
|
112
|
+
export declare const IMAGE_FALLBACK_CONFIG: GatewayImageConfig;
|
|
113
|
+
/**
|
|
114
|
+
* 网关 message 消毒 / 错误码兜底文案都从 `imagegen.ts` 再导出(那里是唯一实现,
|
|
115
|
+
* 因为工具的失败收敛与这里的信封都要用它,而反向 import 会成环)。
|
|
116
|
+
*
|
|
117
|
+
* 为什么还要 `export`:`/hiwork-image` 的消费方(以及 spec §6.7 点名的用例)拿的是
|
|
118
|
+
* **频道模块**的公开面——消毒规则属于这条频道的契约,不该逼调用方去 import 服务层。
|
|
119
|
+
*/
|
|
120
|
+
/** 是否为白名单端点。 */
|
|
121
|
+
export declare function isImageRpcEndpoint(endpoint: string): endpoint is ImageRpcEndpoint;
|
|
122
|
+
/**
|
|
123
|
+
* payload 校验:每个 endpoint 只放行它声明的字段,其余字段 / 类型不符一律 `bad-request`。
|
|
124
|
+
*
|
|
125
|
+
* 缺省 / null 一律等价于空对象(页面挂载时 `status` 的调用就是 `{}` 或不带 payload)。
|
|
126
|
+
* **不在这里做业务校验**(prompt 长度、size 枚举由网关与 `imagegen.ts` 负责)——
|
|
127
|
+
* 这里只做协议形状,越早失败越便宜,但重复实现一遍业务规则只会多一处漂移。
|
|
128
|
+
*/
|
|
129
|
+
export declare function parseImagePayload(endpoint: ImageRpcEndpoint, raw: unknown): ImageRpcPayload;
|
|
130
|
+
/**
|
|
131
|
+
* 处理一次端点调用:返回 DSH Connection 的结果信封,**永不抛异常**。
|
|
132
|
+
* 导出以便单测直接驱动(无需伪造 `ctx.connection`)。
|
|
133
|
+
*
|
|
134
|
+
* `status` 走 {@link readImageConfigSafe} 的**降级**分支:网关故障 → 成功信封里的
|
|
135
|
+
* `enabled:false`。「没有会话」「网关地址未配置」两条**不是**降级——那时还没有任何功能
|
|
136
|
+
* 状态可言,照常回错误信封。
|
|
137
|
+
*/
|
|
138
|
+
export declare function handleImageEndpoint(endpoint: string, rawPayload: unknown, deps: ImageRpcDeps, signal?: AbortSignal): Promise<ImageRpcResult<unknown>>;
|
|
139
|
+
/**
|
|
140
|
+
* 注册 `/hiwork-image` 频道,返回异步 disposer(交给 `ctx.effect` 管理生命周期)。
|
|
141
|
+
*/
|
|
142
|
+
export declare function registerImageRpc(ctx: ImageRpcContext, deps: ImageRpcDeps): () => Promise<void>;
|