@fa-software/semi2-dsh 0.0.13-preview.20260921160207 → 0.0.13
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/README.md +51 -0
- package/lib/api/generated/documents.js +1 -0
- package/lib/client.js +16 -0
- package/lib/client.js.map +1 -1
- package/lib/contract.js +18 -0
- package/lib/domains/default-model/domain.js +4 -5
- package/lib/domains/models/domain.js +2 -3
- package/lib/domains/self-portrait/domain.js +50 -0
- package/lib/domains/self-portrait/index.js +33 -0
- package/lib/hub/facts.js +1 -0
- package/lib/index.js +46 -6
- package/lib/service/config-service.js +1 -1
- package/lib/service/namespaces.js +9 -3
- package/lib/service/orchestrator.js +5 -6
- package/lib/service/ports.js +17 -11
- package/lib/service/registry.js +2 -0
- package/lib/service/settings-sections.js +3 -5
- package/lib/types/api/generated/documents.d.ts +1 -1
- package/lib/types/api/generated/graphql.d.ts +1 -0
- package/lib/types/api/generated/schema.d.ts +42 -0
- package/lib/types/contract.d.ts +17 -0
- package/lib/types/domains/self-portrait/domain.d.ts +22 -0
- package/lib/types/domains/self-portrait/index.d.ts +14 -0
- package/lib/types/domains/types.d.ts +5 -1
- package/lib/types/domains/workspace/feed.d.ts +4 -20
- package/lib/types/hub/facts.d.ts +5 -0
- package/lib/types/index.d.ts +51 -6
- package/lib/types/service/config-service.d.ts +4 -0
- package/lib/types/service/namespaces.d.ts +2 -0
- package/lib/types/service/orchestrator.d.ts +4 -4
- package/lib/types/service/ports.d.ts +25 -26
- package/lib/types/web/fence.d.ts +3 -2
- package/lib/types/web/index.d.ts +7 -1
- package/lib/types/web/routes.d.ts +7 -9
- package/lib/types/web/welcome-ack.d.ts +2 -1
- package/lib/web/fence.js +3 -3
- package/lib/web/index.js +4 -3
- package/lib/web/routes.js +5 -5
- package/lib/web/welcome-ack.js +2 -1
- package/package.json +24 -8
|
@@ -16,7 +16,7 @@ export function installConfiguration(ctx, options = {}) {
|
|
|
16
16
|
// 无 logger 的宿主(单测)忽略。
|
|
17
17
|
}
|
|
18
18
|
};
|
|
19
|
-
const ports = createPorts(ctx, warn);
|
|
19
|
+
const ports = createPorts(ctx, warn, options);
|
|
20
20
|
// api 门面读连接凭据的桥:闭包延迟求值(编排层构造完成后才可能被调用)。
|
|
21
21
|
const engineRef = { current: undefined };
|
|
22
22
|
const orchestrator = new Orchestrator(ctx, options.api ??
|
|
@@ -13,13 +13,20 @@ export const OWNED_SCHEMA = z.object({
|
|
|
13
13
|
credentials: z.array(z.string()).default([]),
|
|
14
14
|
deepseek: z.boolean().default(false),
|
|
15
15
|
});
|
|
16
|
+
/** 一个命名空间的当前有效段:官方描述符的 `value` 是不透明值,本插件只认对象段,其余按空段处理。 */
|
|
17
|
+
export function sectionValue(settings, ns) {
|
|
18
|
+
const value = settings.describe().find(entry => entry.ns === ns)?.value;
|
|
19
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
20
|
+
? value
|
|
21
|
+
: {};
|
|
22
|
+
}
|
|
16
23
|
function stringsOf(input) {
|
|
17
24
|
return Array.isArray(input)
|
|
18
25
|
? input.filter((item) => typeof item === 'string')
|
|
19
26
|
: [];
|
|
20
27
|
}
|
|
21
28
|
export function readOwned(settings) {
|
|
22
|
-
const value = settings
|
|
29
|
+
const value = sectionValue(settings, OWNED_NAMESPACE);
|
|
23
30
|
return {
|
|
24
31
|
routes: stringsOf(value.routes),
|
|
25
32
|
credentials: stringsOf(value.credentials),
|
|
@@ -32,8 +39,7 @@ function text(value) {
|
|
|
32
39
|
}
|
|
33
40
|
/** 持久化段 → 工作区条目;形状不合的行直接丢弃,不让坏数据拖垮启动。 */
|
|
34
41
|
export function readWorkspaces(settings) {
|
|
35
|
-
const value = settings
|
|
36
|
-
{};
|
|
42
|
+
const value = sectionValue(settings, WORKSPACES_NAMESPACE);
|
|
37
43
|
const rows = Array.isArray(value.entries) ? value.entries : [];
|
|
38
44
|
const entries = [];
|
|
39
45
|
for (const row of rows) {
|
|
@@ -3,7 +3,7 @@ import { initialState, } from '../hub/facts.js';
|
|
|
3
3
|
import { DOMAINS } from './registry.js';
|
|
4
4
|
import { CONTRACT_TIMEOUT_MS, CREDENTIAL_RETRY } from './connection.js';
|
|
5
5
|
import { healthSnapshot } from './publish.js';
|
|
6
|
-
/** 领域 attach
|
|
6
|
+
/** 领域 attach 的等待上限缺省值:超时按「该面不提供此能力」审计(依赖永不到达不能永久悬空)。 */
|
|
7
7
|
const DOMAIN_ATTACH_TIMEOUT_MS = 2_000;
|
|
8
8
|
const DOMAIN_POLL_MS = 25;
|
|
9
9
|
export const NOOP_WORKSPACE = {
|
|
@@ -25,6 +25,7 @@ export class Orchestrator {
|
|
|
25
25
|
environment;
|
|
26
26
|
startupDir;
|
|
27
27
|
contractTimeoutMs;
|
|
28
|
+
domainAttachTimeoutMs;
|
|
28
29
|
credentialRetry;
|
|
29
30
|
runtimeContract;
|
|
30
31
|
resolved = false;
|
|
@@ -42,6 +43,7 @@ export class Orchestrator {
|
|
|
42
43
|
this.environment = { ...environment };
|
|
43
44
|
this.startupDir = process.cwd();
|
|
44
45
|
this.contractTimeoutMs = options.contractTimeoutMs ?? CONTRACT_TIMEOUT_MS;
|
|
46
|
+
this.domainAttachTimeoutMs = options.domainAttachTimeoutMs ?? DOMAIN_ATTACH_TIMEOUT_MS;
|
|
45
47
|
this.credentialRetry = { ...CREDENTIAL_RETRY, ...options.credentialRetry };
|
|
46
48
|
const settled = Promise.withResolvers();
|
|
47
49
|
this.ready = settled.promise;
|
|
@@ -90,10 +92,7 @@ export class Orchestrator {
|
|
|
90
92
|
setConnection(connection) {
|
|
91
93
|
this.activeConnection = connection;
|
|
92
94
|
}
|
|
93
|
-
/**
|
|
94
|
-
* 注册表边界的唯一一次类型收窄:map 按 id 存异构领域,取用方按 `DomainId` 拿回自己的 peer 面。
|
|
95
|
-
* 各领域类 `implements DomainPeers[自己的 id]`,形状对不上在编译期就报错。
|
|
96
|
-
*/
|
|
95
|
+
/** 注册表边界的唯一一次类型收窄:各领域类 implements 自己那份 peer 面,形状不对在编译期就报错。 */
|
|
97
96
|
peer(id) {
|
|
98
97
|
return this.domainMap.get(id);
|
|
99
98
|
}
|
|
@@ -150,7 +149,7 @@ export class Orchestrator {
|
|
|
150
149
|
async mountDomains() {
|
|
151
150
|
for (const descriptor of DOMAINS)
|
|
152
151
|
this.ctx.plugin(descriptor, { host: this });
|
|
153
|
-
const deadline = Date.now() +
|
|
152
|
+
const deadline = Date.now() + this.domainAttachTimeoutMs;
|
|
154
153
|
while (this.domainMap.size < DOMAINS.length) {
|
|
155
154
|
if (Date.now() >= deadline)
|
|
156
155
|
break;
|
package/lib/service/ports.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { setTimeout as delay } from 'node:timers/promises';
|
|
2
2
|
import { ConfigurationError } from '../hub/errors.js';
|
|
3
3
|
const SETTINGS_TIMEOUT_MS = 5_000;
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
const NAMESPACE_WAIT_MS = 2_000;
|
|
5
|
+
/** 命名空间注册轮询间隔(纯内部预算,不单列到 Config)。 */
|
|
6
|
+
const NAMESPACE_POLL_MS = 50;
|
|
7
|
+
export function createPorts(ctx, warn, options = {}) {
|
|
8
|
+
const settingsTimeoutMs = options.settingsTimeoutMs ?? SETTINGS_TIMEOUT_MS;
|
|
9
|
+
const namespaceWaitMs = options.namespaceWaitMs ?? NAMESPACE_WAIT_MS;
|
|
7
10
|
const service = (key) => new Promise((resolve) => {
|
|
8
11
|
ctx.inject([key], (injected) => {
|
|
9
12
|
resolve(injected.get(key));
|
|
@@ -14,7 +17,7 @@ export function createPorts(ctx, warn) {
|
|
|
14
17
|
const { promise, resolve } = Promise.withResolvers();
|
|
15
18
|
const timer = globalThis.setTimeout(() => {
|
|
16
19
|
resolve(undefined);
|
|
17
|
-
},
|
|
20
|
+
}, settingsTimeoutMs);
|
|
18
21
|
ctx.inject(['settings'], (injected) => {
|
|
19
22
|
globalThis.clearTimeout(timer);
|
|
20
23
|
resolve(injected.get('settings'));
|
|
@@ -25,14 +28,14 @@ export function createPorts(ctx, warn) {
|
|
|
25
28
|
credentialsNow: () => ctx.get('credentials', false),
|
|
26
29
|
agentDefaultModel: () => ctx.get('agentDefaultModel', false),
|
|
27
30
|
hasWorkspaceController: () => ctx.get('workspaceController', false) !== undefined,
|
|
28
|
-
updateNamespace: (settings, ns, patch) => writeNamespace(settings, ns, () => settings.update(ns, patch)),
|
|
31
|
+
updateNamespace: (settings, ns, patch) => writeNamespace(settings, ns, () => settings.update(ns, patch), namespaceWaitMs),
|
|
29
32
|
replaceNamespace: (settings, ns, section) => {
|
|
30
33
|
const replace = settings.replace?.bind(settings);
|
|
31
34
|
if (replace === undefined) {
|
|
32
35
|
warn(`宿主 settings 无 replace,${ns} 退回合并写入:服务端已撤下的条目可能残留`);
|
|
33
|
-
return writeNamespace(settings, ns, () => settings.update(ns, section));
|
|
36
|
+
return writeNamespace(settings, ns, () => settings.update(ns, section), namespaceWaitMs);
|
|
34
37
|
}
|
|
35
|
-
return writeNamespace(settings, ns, () => replace(ns, section));
|
|
38
|
+
return writeNamespace(settings, ns, () => replace(ns, section), namespaceWaitMs);
|
|
36
39
|
},
|
|
37
40
|
clearKeys: async (settings, ns, keys) => {
|
|
38
41
|
const mutate = settings.mutate?.bind(settings);
|
|
@@ -41,18 +44,21 @@ export function createPorts(ctx, warn) {
|
|
|
41
44
|
return;
|
|
42
45
|
}
|
|
43
46
|
const ops = keys.map(key => ({ op: 'unset', path: [key] }));
|
|
44
|
-
await writeNamespace(settings, ns, () => mutate(ns, ops));
|
|
47
|
+
await writeNamespace(settings, ns, () => mutate(ns, ops), namespaceWaitMs);
|
|
45
48
|
},
|
|
46
49
|
};
|
|
47
50
|
}
|
|
48
51
|
/** 等命名空间注册完成再写(settings 服务与各 bundle 的注册有先后);写失败原样上抛由调用方降级。 */
|
|
49
|
-
async function writeNamespace(settings, ns, write) {
|
|
50
|
-
|
|
52
|
+
async function writeNamespace(settings, ns, write, waitMs) {
|
|
53
|
+
const deadline = Date.now() + waitMs;
|
|
54
|
+
for (;;) {
|
|
51
55
|
if (settings.describe().some(entry => entry.ns === ns)) {
|
|
52
56
|
await write();
|
|
53
57
|
return;
|
|
54
58
|
}
|
|
55
|
-
|
|
59
|
+
if (Date.now() >= deadline)
|
|
60
|
+
break;
|
|
61
|
+
await delay(NAMESPACE_POLL_MS);
|
|
56
62
|
}
|
|
57
63
|
throw new ConfigurationError('CONFIG_APPLY_FAILED');
|
|
58
64
|
}
|
package/lib/service/registry.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { defaultModelDomain } from '../domains/default-model/index.js';
|
|
2
2
|
import { modelsDomain } from '../domains/models/index.js';
|
|
3
3
|
import { secretsDomain } from '../domains/secrets/index.js';
|
|
4
|
+
import { selfPortraitDomain } from '../domains/self-portrait/index.js';
|
|
4
5
|
import { skillsDomain } from '../domains/skills/index.js';
|
|
5
6
|
import { workspaceDomain } from '../domains/workspace/index.js';
|
|
6
7
|
/** 挂载顺序 = 领域间数据流顺序:models 先落地编译产物(secrets 的回收与 default-model 的选择都依赖它)。 */
|
|
@@ -10,4 +11,5 @@ export const DOMAINS = [
|
|
|
10
11
|
skillsDomain,
|
|
11
12
|
workspaceDomain,
|
|
12
13
|
defaultModelDomain,
|
|
14
|
+
selfPortraitDomain,
|
|
13
15
|
];
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import z from '@deepseek-ai/schemastery';
|
|
4
4
|
import { STATUS_BASE_FIELDS, WORKSPACE_TABS_FIELD } from '../hub/status.js';
|
|
5
5
|
import { DOMAINS } from './registry.js';
|
|
6
|
-
import { OWNED_NAMESPACE, OWNED_SCHEMA, SETTINGS_NAMESPACE, WORKSPACES_NAMESPACE, } from './namespaces.js';
|
|
6
|
+
import { OWNED_NAMESPACE, OWNED_SCHEMA, sectionValue, SETTINGS_NAMESPACE, WORKSPACES_NAMESPACE, } from './namespaces.js';
|
|
7
7
|
/** UI 触发重载的字段:写入任意非空值即触发(写后由状态回报复位为空,无令牌比对)。 */
|
|
8
8
|
export const SYNC_FIELD = 'sync';
|
|
9
9
|
/** 完整状态字段顺序(基座 + 按注册表顺序的领域字段);UI 与健康字段共用命名空间。 */
|
|
@@ -54,9 +54,7 @@ export function installSections(ctx, settings, hooks) {
|
|
|
54
54
|
onChange: () => {
|
|
55
55
|
// 用户写入才会走到这里;读有效值判断是否 sync 触发(本插件自己的 replace 也会触发 onChange,
|
|
56
56
|
// 但回报时 sync 恒写空串,不会成环)。
|
|
57
|
-
const value = settings
|
|
58
|
-
.describe()
|
|
59
|
-
.find(entry => entry.ns === SETTINGS_NAMESPACE)?.value?.[SYNC_FIELD];
|
|
57
|
+
const value = sectionValue(settings, SETTINGS_NAMESPACE)[SYNC_FIELD];
|
|
60
58
|
if (typeof value === 'string' && value !== '')
|
|
61
59
|
hooks.onSyncRequest(value);
|
|
62
60
|
},
|
|
@@ -75,6 +73,6 @@ export function installSections(ctx, settings, hooks) {
|
|
|
75
73
|
}
|
|
76
74
|
/** 读 `semi2` 命名空间的一个字段(用户写入后、回报前的过渡值)。 */
|
|
77
75
|
export function readSetting(settings, key) {
|
|
78
|
-
const value = settings
|
|
76
|
+
const value = sectionValue(settings, SETTINGS_NAMESPACE)[key];
|
|
79
77
|
return typeof value === 'string' ? value : '';
|
|
80
78
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** 启动装配:五个用户级领域一个文档(项目绑定按工作区另行按需取)。 */
|
|
2
|
-
export declare const RUNTIME_BOOT_DOCUMENT = "query RuntimeBoot {\n runtimeViewer {\n id\n username\n displayName\n }\n runtimeModels {\n id\n upstreamId\n displayName\n metadata { contextWindow maxTokens input reasoningLevels }\n defaultReasoningLevel\n connection { id provider protocol baseUrl }\n }\n runtimeDefaultModelId\n runtimeHeadlessModels {\n planModelId\n executeModelId\n }\n runtimeModelCredentials {\n connectionId\n value\n }\n runtimeSkills {\n id\n projectId\n name\n description\n whenToUse\n content\n version\n files {\n path\n sizeBytes\n mimeType\n isExecutable\n }\n fileCount\n filesBytes\n }\n}";
|
|
2
|
+
export declare const RUNTIME_BOOT_DOCUMENT = "query RuntimeBoot {\n runtimeViewer {\n id\n username\n displayName\n }\n runtimeModels {\n id\n upstreamId\n displayName\n metadata { contextWindow maxTokens input reasoningLevels }\n defaultReasoningLevel\n connection { id provider protocol baseUrl }\n }\n runtimeDefaultModelId\n runtimeHeadlessModels {\n planModelId\n executeModelId\n }\n runtimeModelCredentials {\n connectionId\n value\n }\n runtimeSkills {\n id\n projectId\n name\n description\n whenToUse\n content\n version\n files {\n path\n sizeBytes\n mimeType\n isExecutable\n }\n fileCount\n filesBytes\n }\n runtimeSelfPortrait\n}";
|
|
3
3
|
/** 技能附属资源字节:boot 文档只带清单,真正加载某个技能时才按需取这一条。可空根字段:读取者不可见 / 不存在回空数组(权威「没有」),领域失败回 null(降级,不物化)。 */
|
|
4
4
|
export declare const RUNTIME_SKILL_FILES_DOCUMENT = "query RuntimeSkillFiles($skillId: ID!) {\n runtimeSkillFiles(skillId: $skillId) {\n path\n content\n encoding\n sizeBytes\n mimeType\n isExecutable\n }\n}";
|
|
5
5
|
/** 项目绑定按 slug 取:只问已登记工作区命中的那几个项目,绝不把整张项目表拉回本地。`slugs` 是读范围之上的过滤(不是绕过),空数组 = 空集合。 */
|
|
@@ -423,6 +423,8 @@ export type Mutation = {
|
|
|
423
423
|
saveModelConnection: ModelConnection;
|
|
424
424
|
saveProjectDetails: Project;
|
|
425
425
|
saveProjectHeadlessModels: Project;
|
|
426
|
+
/** 保存自画像(追加版本);同内容幂等不涨版本,expectedVersion 过期即 REVISION_CONFLICT;空正文 = 撤下。 */
|
|
427
|
+
saveSelfPortrait: SelfPortrait;
|
|
426
428
|
/** 整份内容提交;同内容幂等不涨版本,expectedVersion 过期即 SKILL_VERSION_CONFLICT。 */
|
|
427
429
|
saveSkill: Skill;
|
|
428
430
|
saveUserConfiguration: UserConfiguration;
|
|
@@ -497,6 +499,9 @@ export type MutationSaveProjectDetailsArgs = {
|
|
|
497
499
|
export type MutationSaveProjectHeadlessModelsArgs = {
|
|
498
500
|
input: SaveProjectHeadlessModelsInput;
|
|
499
501
|
};
|
|
502
|
+
export type MutationSaveSelfPortraitArgs = {
|
|
503
|
+
input: SaveSelfPortraitInput;
|
|
504
|
+
};
|
|
500
505
|
export type MutationSaveSkillArgs = {
|
|
501
506
|
input: SaveSkillInput;
|
|
502
507
|
};
|
|
@@ -647,6 +652,11 @@ export type Query = {
|
|
|
647
652
|
* 工作区逐个项目要,不必把整个项目表拉回本地。
|
|
648
653
|
*/
|
|
649
654
|
runtimeProjects?: Maybe<Array<RuntimeProject>>;
|
|
655
|
+
/**
|
|
656
|
+
* 自画像正文(agent 会话开头注入的人设);未配置为空串,本次读取失败为 null。
|
|
657
|
+
* 全局一份,与读取者身份无关。
|
|
658
|
+
*/
|
|
659
|
+
runtimeSelfPortrait?: Maybe<Scalars['String']['output']>;
|
|
650
660
|
/**
|
|
651
661
|
* 某技能 head 版本的附件内容(运行面物化用);读取者对该技能不可见时返回空数组。
|
|
652
662
|
* 字节不回填进 boot 文档,只在真正加载技能时按需取。
|
|
@@ -659,6 +669,10 @@ export type Query = {
|
|
|
659
669
|
runtimeSkills?: Maybe<Array<RuntimeSkill>>;
|
|
660
670
|
/** 本次读取者(服务器里当前用户);失败为 null。 */
|
|
661
671
|
runtimeViewer?: Maybe<RuntimeViewer>;
|
|
672
|
+
/** 自画像的当前版本(全局一份 agent 人设)。 */
|
|
673
|
+
selfPortrait: SelfPortrait;
|
|
674
|
+
/** 自画像的历史快照(新在前,最多 50 条)。 */
|
|
675
|
+
selfPortraitVersions: Array<SelfPortraitVersion>;
|
|
662
676
|
/** 单个技能详情(含已归档;没给 version 就是 head,给了就取该历史版本);不存在即 SKILL_NOT_FOUND。 */
|
|
663
677
|
skill: Skill;
|
|
664
678
|
/** 某版本(缺省 head)的全部附件内容;导出 / 编辑回填 / 下载按需取。 */
|
|
@@ -954,6 +968,12 @@ export type SaveProjectHeadlessModelsInput = {
|
|
|
954
968
|
planReasoningLevel?: InputMaybe<ReasoningLevel>;
|
|
955
969
|
projectId: Scalars['ID']['input'];
|
|
956
970
|
};
|
|
971
|
+
export type SaveSelfPortraitInput = {
|
|
972
|
+
/** 正文;trim 后为空即撤下(运行面不再注入)。 */
|
|
973
|
+
content: Scalars['String']['input'];
|
|
974
|
+
/** 当前版本号(0 = 从未配置过);过期即 REVISION_CONFLICT。 */
|
|
975
|
+
expectedVersion: Scalars['Int']['input'];
|
|
976
|
+
};
|
|
957
977
|
export type SaveSkillInput = {
|
|
958
978
|
content: Scalars['String']['input'];
|
|
959
979
|
/** 当前头版本号;过期即 SKILL_VERSION_CONFLICT(同内容重复提交不消耗它)。 */
|
|
@@ -971,6 +991,28 @@ export type SecretInput = {
|
|
|
971
991
|
reuse?: InputMaybe<Scalars['Boolean']['input']>;
|
|
972
992
|
value?: InputMaybe<Scalars['String']['input']>;
|
|
973
993
|
};
|
|
994
|
+
/** 自画像的当前版本;从未配置过时 version = 0、content 为空串。 */
|
|
995
|
+
export type SelfPortrait = {
|
|
996
|
+
__typename?: 'SelfPortrait';
|
|
997
|
+
/** 注入 agent 会话开头的正文;空串 = 未配置(运行面不注入)。 */
|
|
998
|
+
content: Scalars['String']['output'];
|
|
999
|
+
updatedAt?: Maybe<Scalars['DateTime']['output']>;
|
|
1000
|
+
updatedBy?: Maybe<Scalars['ID']['output']>;
|
|
1001
|
+
updatedByDisplayName?: Maybe<Scalars['String']['output']>;
|
|
1002
|
+
updatedByUsername?: Maybe<Scalars['String']['output']>;
|
|
1003
|
+
/** 当前版本号;保存时作为 CAS 令牌(0 = 从未配置过)。 */
|
|
1004
|
+
version: Scalars['Int']['output'];
|
|
1005
|
+
};
|
|
1006
|
+
/** 一个不可变历史快照;作者用扁平字段(同 AuditEvent 口径)。 */
|
|
1007
|
+
export type SelfPortraitVersion = {
|
|
1008
|
+
__typename?: 'SelfPortraitVersion';
|
|
1009
|
+
content: Scalars['String']['output'];
|
|
1010
|
+
createdAt: Scalars['DateTime']['output'];
|
|
1011
|
+
createdBy: Scalars['ID']['output'];
|
|
1012
|
+
createdByDisplayName?: Maybe<Scalars['String']['output']>;
|
|
1013
|
+
createdByUsername?: Maybe<Scalars['String']['output']>;
|
|
1014
|
+
version: Scalars['Int']['output'];
|
|
1015
|
+
};
|
|
974
1016
|
export type SetModelEnabledInput = {
|
|
975
1017
|
enabled: Scalars['Boolean']['input'];
|
|
976
1018
|
expectedRevision: Scalars['Int']['input'];
|
package/lib/types/contract.d.ts
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* semi2 提示词段的号段分配(偏移量,基准 = 官方 persona 段)。
|
|
3
|
+
* 三段都落在官方人设区(persona + 1..3),不与任何官方工具 schema 号段重叠;
|
|
4
|
+
* 段名与绝对位置由 `semi2SectionOrder` 现算,官方占用 1..3 时改这里并同步守卫。
|
|
5
|
+
*/
|
|
6
|
+
export declare const SEMI2_SECTION_ORDERS: {
|
|
7
|
+
readonly selfPortrait: 1;
|
|
8
|
+
readonly npcContract: 2;
|
|
9
|
+
readonly cnbWorkstation: 3;
|
|
10
|
+
};
|
|
11
|
+
export type Semi2SectionKey = keyof typeof SEMI2_SECTION_ORDERS;
|
|
12
|
+
/** 只读 systemPrompt 的号段解析面:老宿主没有 `getSectionOrder` 时回落本表偏移。 */
|
|
13
|
+
export interface SectionOrderSource {
|
|
14
|
+
getSectionOrder?(name: string): number | undefined;
|
|
15
|
+
}
|
|
16
|
+
/** 段的绝对位置 = 官方 persona 段号 + 本表偏移(服务缺席 / 不认名字时以偏移值兜底)。 */
|
|
17
|
+
export declare function semi2SectionOrder(systemPrompt: SectionOrderSource | undefined, key: Semi2SectionKey): number;
|
|
1
18
|
/** 连接 semi2-hub 的凭据;两项齐备才算可连。 */
|
|
2
19
|
export interface Semi2Credentials {
|
|
3
20
|
configUrl?: string;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { DomainHost, DomainService, LoadRun } from '../types.js';
|
|
2
|
+
import type { Semi2ConfigState } from '../../hub/facts.js';
|
|
3
|
+
export declare class SelfPortraitDomain implements DomainService {
|
|
4
|
+
private readonly host;
|
|
5
|
+
readonly id: "self-portrait";
|
|
6
|
+
constructor(host: DomainHost);
|
|
7
|
+
/** 注入段的文本;每次装配现读共享状态,配置重载与领域重建都不需要重新注册或重新领取。 */
|
|
8
|
+
text(): string;
|
|
9
|
+
applyBoot(run: LoadRun): Promise<void>;
|
|
10
|
+
statusFields(state: Semi2ConfigState): Record<string, string>;
|
|
11
|
+
summary(state: Semi2ConfigState): string | null;
|
|
12
|
+
health(): {
|
|
13
|
+
state: "ok";
|
|
14
|
+
note: string;
|
|
15
|
+
} | {
|
|
16
|
+
state: "degraded";
|
|
17
|
+
note: string;
|
|
18
|
+
} | {
|
|
19
|
+
state: "ok";
|
|
20
|
+
note?: never;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
2
|
+
import type { SystemPrompt } from '@deepseek-ai/dsh-system-prompt';
|
|
3
|
+
import type { DomainPlugin } from '../types.js';
|
|
4
|
+
/** dsh 原生 systemPrompt 段注册面(官方 `SystemPrompt` 的子集;老宿主没有 `getSectionOrder`)。 */
|
|
5
|
+
export type SystemPromptLike = Pick<SystemPrompt, 'section'> & Partial<Pick<SystemPrompt, 'getSectionOrder'>>;
|
|
6
|
+
/** 本段的名字(人设区里与官方 persona 段并列,`semi2:` 前缀与其它 semi2 段同口径)。 */
|
|
7
|
+
export declare const SELF_PORTRAIT_SECTION = "semi2:self-portrait";
|
|
8
|
+
/**
|
|
9
|
+
* 在官方 persona 段之后注册自画像段:正文由 provider 每次装配现读(配置重载后自动跟上)。
|
|
10
|
+
* `interpolate: false` 是硬要求——正文是管理员手写的自由文本,里面的 `{{…}}` 会让插值阶段抛错,
|
|
11
|
+
* 一条坏正文就等于整轮会话起不来。空文本由 renderPrompt 丢弃,等于「未配置」。
|
|
12
|
+
*/
|
|
13
|
+
export declare function installSelfPortraitSection(ctx: Context, text: () => string): void;
|
|
14
|
+
export declare const selfPortraitDomain: DomainPlugin;
|
|
@@ -28,6 +28,9 @@ export interface ModelsPeer {
|
|
|
28
28
|
/** 密钥领域没有跨领域被调的成员;占位保证 `DomainPeers` 对 DomainId 完备。 */
|
|
29
29
|
export interface SecretsPeer {
|
|
30
30
|
}
|
|
31
|
+
/** 自画像领域没有跨领域被调的成员(注入段由领域自己注册);占位同 SecretsPeer 口径。 */
|
|
32
|
+
export interface SelfPortraitPeer {
|
|
33
|
+
}
|
|
31
34
|
export interface SkillsPeer {
|
|
32
35
|
/** 当前控制台技能目录(null = 没拿到权威目录,≠ 空目录)。 */
|
|
33
36
|
skills(): RuntimeSkillFact[] | null;
|
|
@@ -85,6 +88,7 @@ export interface DomainPeers {
|
|
|
85
88
|
skills: SkillsPeer;
|
|
86
89
|
workspace: WorkspacePeer;
|
|
87
90
|
'default-model': DefaultModelPeer;
|
|
91
|
+
'self-portrait': SelfPortraitPeer;
|
|
88
92
|
}
|
|
89
93
|
/** 一轮 boot 装配的共享上下文(编排层创建,按注册表顺序流经各领域)。 */
|
|
90
94
|
export interface LoadRun {
|
|
@@ -166,4 +170,4 @@ export interface DomainPlugin {
|
|
|
166
170
|
readonly inject?: readonly string[];
|
|
167
171
|
apply(ctx: Context, config: DomainMountConfig): void;
|
|
168
172
|
}
|
|
169
|
-
export type DomainId = 'models' | 'secrets' | 'skills' | 'workspace' | 'default-model';
|
|
173
|
+
export type DomainId = 'models' | 'secrets' | 'skills' | 'workspace' | 'default-model' | 'self-portrait';
|
|
@@ -1,25 +1,9 @@
|
|
|
1
1
|
import type { Context } from '@deepseek-ai/cordis';
|
|
2
|
+
import type { WorkspaceController, WorkspaceFollowFrame } from '@deepseek-ai/dsh-api-workspace-controller';
|
|
2
3
|
import type { WorkspaceViewLike } from '../../hub/workspace-book.js';
|
|
3
|
-
/** `workspaceController.follow`
|
|
4
|
-
export type WorkspaceFrameLike =
|
|
5
|
-
|
|
6
|
-
value: {
|
|
7
|
-
items: readonly WorkspaceViewLike[];
|
|
8
|
-
};
|
|
9
|
-
} | {
|
|
10
|
-
type: 'upsert';
|
|
11
|
-
workspace: WorkspaceViewLike;
|
|
12
|
-
} | {
|
|
13
|
-
type: 'remove';
|
|
14
|
-
workspaceId: string;
|
|
15
|
-
} | {
|
|
16
|
-
type: 'order';
|
|
17
|
-
} | {
|
|
18
|
-
type: 'archived';
|
|
19
|
-
};
|
|
20
|
-
export interface WorkspaceControllerLike {
|
|
21
|
-
follow(signal: AbortSignal): AsyncIterable<WorkspaceFrameLike>;
|
|
22
|
-
}
|
|
4
|
+
/** `workspaceController.follow` 的帧(官方 Workspace Remote 状态流);order / archived 与本插件无关。 */
|
|
5
|
+
export type WorkspaceFrameLike = WorkspaceFollowFrame;
|
|
6
|
+
export type WorkspaceControllerLike = Pick<WorkspaceController, 'follow'>;
|
|
23
7
|
export interface WorkspaceFeedHandlers {
|
|
24
8
|
baseline(items: readonly WorkspaceViewLike[]): void | Promise<void>;
|
|
25
9
|
upsert(view: WorkspaceViewLike): void | Promise<void>;
|
package/lib/types/hub/facts.d.ts
CHANGED
|
@@ -56,6 +56,11 @@ export interface Semi2ConfigState {
|
|
|
56
56
|
} | null;
|
|
57
57
|
/** 控制台技能目录;null = 没拿到权威目录(≠ 空目录),provider 据此报 complete:false。 */
|
|
58
58
|
skills: RuntimeSkillFact[] | null;
|
|
59
|
+
/**
|
|
60
|
+
* 自画像正文:null = 没拿到(读取失败或还没读过),空串 = 权威「未配置」,其余即正文。
|
|
61
|
+
* 注入段按它现读——两种空都不注入。
|
|
62
|
+
*/
|
|
63
|
+
selfPortrait: string | null;
|
|
59
64
|
/** 已登记工作区命中的项目(含生效绑定,按 slug 现取并缓存);会话按 cwd 经 `projectFor` 匹配。 */
|
|
60
65
|
projects: RuntimeProject[];
|
|
61
66
|
/** 已登记工作区(dsh 工作区 → git 项目映射);未接入工作区服务时为单条隐式条目。 */
|
package/lib/types/index.d.ts
CHANGED
|
@@ -5,12 +5,57 @@ export type { RuntimeIdentity, RuntimeSkillFact, RuntimeSkillFileContentFact, Ru
|
|
|
5
5
|
export type { ConfigStatus, Semi2Config, Semi2Audit, Semi2Health, Semi2Session, } from './service/config-service.js';
|
|
6
6
|
export type { HeadlessModelOutcome } from './domains/types.js';
|
|
7
7
|
export type { Semi2Surface } from './hub/facts.js';
|
|
8
|
-
export { SEMI2_RUNTIME_SERVICE, runtimeFromCnbEnv, runtimeFromContainerEnv, } from './contract.js';
|
|
9
|
-
export type { Semi2Credentials, Semi2Platform, Semi2Runtime, } from './contract.js';
|
|
8
|
+
export { SEMI2_RUNTIME_SERVICE, SEMI2_SECTION_ORDERS, runtimeFromCnbEnv, runtimeFromContainerEnv, semi2SectionOrder, } from './contract.js';
|
|
9
|
+
export type { SectionOrderSource, Semi2Credentials, Semi2Platform, Semi2Runtime, Semi2SectionKey, } from './contract.js';
|
|
10
10
|
import './events.js';
|
|
11
11
|
export declare const name = "semi2-dsh";
|
|
12
|
-
/**
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
/**
|
|
13
|
+
* 可调参数:默认值 = 各模块曾经的常量;bundle patch 行不写 config,消费方 overlay 才覆盖。
|
|
14
|
+
* 「config 明确值 → 环境变量 → 内置常量」的取值顺序在 apply 里一次归一。
|
|
15
|
+
*/
|
|
16
|
+
export declare const Config: z<Schemastery.ObjectS<{
|
|
17
|
+
/** 等运行面契约(`semi2Runtime`)出现的上限:超时按宿主直跑处理。 */
|
|
18
|
+
contractTimeoutMs: z<number, number>;
|
|
19
|
+
/** 等领域装配的上限:超时的领域记「本面不提供该能力」。 */
|
|
20
|
+
domainAttachTimeoutMs: z<number, number>;
|
|
21
|
+
/** 等 settings 服务的上限:本面无该服务时不阻塞启动。 */
|
|
22
|
+
settingsTimeoutMs: z<number, number>;
|
|
23
|
+
/** 等命名空间注册完成再写的上限。 */
|
|
24
|
+
namespaceWaitMs: z<number, number>;
|
|
25
|
+
/** 凭据存储首次读盘的重试次数与间隔。 */
|
|
26
|
+
credentialRetryTries: z<number, number>;
|
|
27
|
+
credentialRetryIntervalMs: z<number, number>;
|
|
28
|
+
/** webServer 在而校验面迟迟不出现时,多久补一条诊断日志。 */
|
|
29
|
+
webFenceTimeoutMs: z<number, number>;
|
|
30
|
+
/** 登录 / 登出 / 健康路由的请求体上限(字节)。 */
|
|
31
|
+
webRouteBodyLimitBytes: z<number, number>;
|
|
32
|
+
}>, Schemastery.ObjectT<{
|
|
33
|
+
/** 等运行面契约(`semi2Runtime`)出现的上限:超时按宿主直跑处理。 */
|
|
34
|
+
contractTimeoutMs: z<number, number>;
|
|
35
|
+
/** 等领域装配的上限:超时的领域记「本面不提供该能力」。 */
|
|
36
|
+
domainAttachTimeoutMs: z<number, number>;
|
|
37
|
+
/** 等 settings 服务的上限:本面无该服务时不阻塞启动。 */
|
|
38
|
+
settingsTimeoutMs: z<number, number>;
|
|
39
|
+
/** 等命名空间注册完成再写的上限。 */
|
|
40
|
+
namespaceWaitMs: z<number, number>;
|
|
41
|
+
/** 凭据存储首次读盘的重试次数与间隔。 */
|
|
42
|
+
credentialRetryTries: z<number, number>;
|
|
43
|
+
credentialRetryIntervalMs: z<number, number>;
|
|
44
|
+
/** webServer 在而校验面迟迟不出现时,多久补一条诊断日志。 */
|
|
45
|
+
webFenceTimeoutMs: z<number, number>;
|
|
46
|
+
/** 登录 / 登出 / 健康路由的请求体上限(字节)。 */
|
|
47
|
+
webRouteBodyLimitBytes: z<number, number>;
|
|
48
|
+
}>>;
|
|
15
49
|
export type Config = Schemastery.TypeT<typeof Config>;
|
|
16
|
-
|
|
50
|
+
/** apply 内归一后的不可变参数(配置面 → 编排 / web 面的唯一形状)。 */
|
|
51
|
+
export interface InstallOptions {
|
|
52
|
+
contractTimeoutMs: number;
|
|
53
|
+
domainAttachTimeoutMs: number;
|
|
54
|
+
settingsTimeoutMs: number;
|
|
55
|
+
namespaceWaitMs: number;
|
|
56
|
+
credentialRetryTries: number;
|
|
57
|
+
credentialRetryIntervalMs: number;
|
|
58
|
+
webFenceTimeoutMs: number;
|
|
59
|
+
webRouteBodyLimitBytes: number;
|
|
60
|
+
}
|
|
61
|
+
export declare function apply(ctx: Context, config: Config): void;
|
|
@@ -48,6 +48,10 @@ export interface Semi2Audit {
|
|
|
48
48
|
}>;
|
|
49
49
|
}
|
|
50
50
|
export interface ConfigurationOptions extends OrchestratorOptions {
|
|
51
|
+
/** 等 settings 服务的上限(ms):本面无该服务时不阻塞启动。 */
|
|
52
|
+
settingsTimeoutMs?: number;
|
|
53
|
+
/** 等命名空间注册完成再写的上限(ms)。 */
|
|
54
|
+
namespaceWaitMs?: number;
|
|
51
55
|
/** 测试缝:替换网络层(假件)。 */
|
|
52
56
|
api?: Semi2Api;
|
|
53
57
|
}
|
|
@@ -17,6 +17,8 @@ export declare const OWNED_SCHEMA: z<Schemastery.ObjectS<{
|
|
|
17
17
|
credentials: z<string[], string[]>;
|
|
18
18
|
deepseek: z<boolean, boolean>;
|
|
19
19
|
}>>;
|
|
20
|
+
/** 一个命名空间的当前有效段:官方描述符的 `value` 是不透明值,本插件只认对象段,其余按空段处理。 */
|
|
21
|
+
export declare function sectionValue(settings: SettingsLike, ns: string): Record<string, unknown>;
|
|
20
22
|
export declare function readOwned(settings: SettingsLike): OwnedFacts;
|
|
21
23
|
/** 持久化段 → 工作区条目;形状不合的行直接丢弃,不让坏数据拖垮启动。 */
|
|
22
24
|
export declare function readWorkspaces(settings: SettingsLike): WorkspaceEntry[];
|
|
@@ -49,6 +49,8 @@ export interface OrchestratorInternals {
|
|
|
49
49
|
export interface OrchestratorOptions {
|
|
50
50
|
/** 等运行面契约出现的上限(ms):默认 5s,宿主直跑即等满。 */
|
|
51
51
|
contractTimeoutMs?: number;
|
|
52
|
+
/** 等领域装配的上限(ms):超时的领域记「本面不提供该能力」。 */
|
|
53
|
+
domainAttachTimeoutMs?: number;
|
|
52
54
|
/** 凭据重试窗口:宿主的凭据存储晚就绪时用(测试可收短,默认见 connection.ts)。 */
|
|
53
55
|
credentialRetry?: Partial<CredentialRetry>;
|
|
54
56
|
}
|
|
@@ -62,6 +64,7 @@ export declare class Orchestrator implements DomainHost, OrchestratorInternals {
|
|
|
62
64
|
readonly environment: NodeJS.ProcessEnv;
|
|
63
65
|
readonly startupDir: string;
|
|
64
66
|
readonly contractTimeoutMs: number;
|
|
67
|
+
readonly domainAttachTimeoutMs: number;
|
|
65
68
|
readonly credentialRetry: CredentialRetry;
|
|
66
69
|
private runtimeContract;
|
|
67
70
|
private resolved;
|
|
@@ -85,10 +88,7 @@ export declare class Orchestrator implements DomainHost, OrchestratorInternals {
|
|
|
85
88
|
hasConnection(): boolean;
|
|
86
89
|
connection(): ConnectionCredentials | null;
|
|
87
90
|
setConnection(connection: ConnectionCredentials | null): void;
|
|
88
|
-
/**
|
|
89
|
-
* 注册表边界的唯一一次类型收窄:map 按 id 存异构领域,取用方按 `DomainId` 拿回自己的 peer 面。
|
|
90
|
-
* 各领域类 `implements DomainPeers[自己的 id]`,形状对不上在编译期就报错。
|
|
91
|
-
*/
|
|
91
|
+
/** 注册表边界的唯一一次类型收窄:各领域类 implements 自己那份 peer 面,形状不对在编译期就报错。 */
|
|
92
92
|
peer<K extends DomainId>(id: K): DomainPeers[K] | undefined;
|
|
93
93
|
attach(domain: DomainService): void;
|
|
94
94
|
get domains(): ReadonlyMap<DomainId, DomainService & object>;
|
|
@@ -1,30 +1,29 @@
|
|
|
1
1
|
import type { Context } from '@deepseek-ai/cordis';
|
|
2
2
|
import type { CredentialProvider } from '@deepseek-ai/dsh-credentials';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
export interface
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
};
|
|
3
|
+
import type { AgentDefaultModelConfig } from '@deepseek-ai/dsh-agent-default-model';
|
|
4
|
+
import type { SettingsProvider, SettingsSectionHooks } from '@deepseek-ai/dsh-settings';
|
|
5
|
+
import type { ModelSelection } from '../hub/compile.js';
|
|
6
|
+
/** 本插件消费的 host settings 服务面:读 / 写口径照官方 `SettingsProvider`;`replace` / `mutate` 是可缺的降级口。 */
|
|
7
|
+
export type SettingsLike = Pick<SettingsProvider, 'describe' | 'update'> & Partial<Pick<SettingsProvider, 'replace' | 'mutate'>> & {
|
|
8
|
+
/**
|
|
9
|
+
* 命名空间注册:schema 与初始段由插件按运行期注册表构造(形状不在编译期),
|
|
10
|
+
* 故这两项按官方语义放宽成 `unknown` / 对象;`hooks` 与官方 `SettingsSectionHooks` 同义。
|
|
11
|
+
*/
|
|
12
|
+
installSection(owner: unknown, ns: string, schema: unknown, initial: Record<string, unknown>, hooks: SettingsSectionHooks<Record<string, unknown>>): unknown;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* 本插件消费的 dsh agentDefaultModel 服务面(默认模型与无头角色选择)。
|
|
16
|
+
* 读口取自官方 `AgentDefaultModelConfig`;写入口径不变,档位是 hub 下发的普通字符串
|
|
17
|
+
* (官方档位 id 是品牌化字符串,运行时同形)。
|
|
18
|
+
*/
|
|
19
|
+
export type AgentDefaultModelLike = Partial<Pick<AgentDefaultModelConfig, 'currentSelection'>> & {
|
|
20
|
+
saveSelection(next: ModelSelection): Promise<void>;
|
|
21
|
+
};
|
|
22
|
+
export interface PortsOptions {
|
|
23
|
+
/** 等 settings 服务的上限(ms)。 */
|
|
24
|
+
settingsTimeoutMs?: number | undefined;
|
|
25
|
+
/** 等命名空间注册完成再写的上限(ms)。 */
|
|
26
|
+
namespaceWaitMs?: number | undefined;
|
|
28
27
|
}
|
|
29
28
|
export interface Ports {
|
|
30
29
|
/** settings 服务缺失(非 web/headless 宿主)时不阻塞启动,5s 后放弃返回 undefined。 */
|
|
@@ -48,4 +47,4 @@ export interface Ports {
|
|
|
48
47
|
/** 按路径删除本插件写过的键(合并写入删不掉)。 */
|
|
49
48
|
clearKeys(settings: SettingsLike, ns: string, keys: readonly string[]): Promise<void>;
|
|
50
49
|
}
|
|
51
|
-
export declare function createPorts(ctx: Context, warn: (message: string) => void): Ports;
|
|
50
|
+
export declare function createPorts(ctx: Context, warn: (message: string) => void, options?: PortsOptions): Ports;
|
package/lib/types/web/fence.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
+
import type { ConnectionRequestRejection, ConnectionTrustRequest } from '@deepseek-ai/dsh-client-connection';
|
|
2
3
|
export interface RequestFence {
|
|
3
|
-
requestRejection(request:
|
|
4
|
+
requestRejection(request: ConnectionTrustRequest): ConnectionRequestRejection;
|
|
4
5
|
}
|
|
5
|
-
/**
|
|
6
|
+
/** 校验不过就地应答(状态码照官方拒绝码:401 未鉴权 / 403 栅栏拒绝),返回是否已拒绝。 */
|
|
6
7
|
export declare function rejected(req: IncomingMessage, res: ServerResponse, fence: RequestFence): boolean;
|
package/lib/types/web/index.d.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import type { Context } from '@deepseek-ai/cordis';
|
|
2
2
|
import type { RouteRuntime } from './routes.js';
|
|
3
3
|
import type { HealthContributor } from '../service/health.js';
|
|
4
|
-
export
|
|
4
|
+
export interface WebSurfaceOptions {
|
|
5
|
+
/** 校验面迟迟不出现时补一条诊断日志的等待上限(ms)。 */
|
|
6
|
+
fenceTimeoutMs?: number | undefined;
|
|
7
|
+
/** 登录 / 登出 / 健康路由的请求体上限(字节)。 */
|
|
8
|
+
bodyLimitBytes?: number | undefined;
|
|
9
|
+
}
|
|
10
|
+
export declare function installWebSurface(ctx: Context, runtime: RouteRuntime, registerHealth: (contributor: HealthContributor) => void, options?: WebSurfaceOptions): void;
|