@fa-software/semi2-dsh 0.0.18 → 0.0.19-preview.20261001161030
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/api/bootstrap.js +45 -0
- package/lib/api/generated/http.js +1 -0
- package/lib/api/http-assets.js +89 -0
- package/lib/api/http-models.js +101 -0
- package/lib/api/http.js +99 -0
- package/lib/api/index.js +1 -72
- package/lib/api/login.js +3 -10
- package/lib/api/operations.js +9 -0
- package/lib/api/rest.js +57 -0
- package/lib/client.js.map +1 -1
- package/lib/cnb-token.js +37 -0
- package/lib/domains/default-model/domain.js +1 -3
- package/lib/domains/models/domain.js +3 -5
- package/lib/domains/models/web-search.js +2 -4
- package/lib/domains/project-profile/domain.js +1 -3
- package/lib/domains/project-profile/index.js +2 -2
- package/lib/domains/redmine/domain.js +1 -4
- package/lib/domains/redmine/index.js +2 -2
- package/lib/domains/secrets/domain.js +2 -6
- package/lib/domains/self-portrait/domain.js +1 -3
- package/lib/domains/self-portrait/index.js +1 -1
- package/lib/domains/skills/index.js +1 -1
- package/lib/domains/skills/mcp-resources.js +8 -10
- package/lib/domains/skills/provider.js +4 -4
- package/lib/domains/workspace/domain.js +3 -3
- package/lib/domains/workspace/feed.js +1 -1
- package/lib/domains/workspace/index.js +1 -1
- package/lib/domains/workspace/resolve.js +2 -4
- package/lib/hub/compile.js +5 -17
- package/lib/hub/credentials.js +3 -8
- package/lib/hub/errors.js +1 -3
- package/lib/hub/status.js +1 -1
- package/lib/hub/workspace-book.js +2 -6
- package/lib/index.js +3 -2
- package/lib/service/config-service.js +3 -4
- package/lib/service/connection.js +1 -1
- package/lib/service/lifecycle.js +3 -3
- package/lib/service/orchestrator.js +2 -4
- package/lib/service/ports.js +4 -4
- package/lib/service/registry.js +2 -8
- package/lib/service/sessions.js +5 -10
- package/lib/service/settings-tx.js +3 -6
- package/lib/service/status.js +4 -8
- package/lib/types/api/bootstrap.d.ts +23 -0
- package/lib/types/api/facts.d.ts +41 -0
- package/lib/types/api/generated/http.d.ts +3924 -0
- package/lib/types/api/http-assets.d.ts +65 -0
- package/lib/types/api/http-models.d.ts +68 -0
- package/lib/types/api/http.d.ts +16 -0
- package/lib/types/api/index.d.ts +10 -31
- package/lib/types/api/operations.d.ts +27 -0
- package/lib/types/api/rest.d.ts +3 -0
- package/lib/types/cnb-token.d.ts +24 -0
- package/lib/types/domains/project-profile/domain.d.ts +1 -1
- package/lib/types/hub/compile.d.ts +5 -5
- package/lib/types/index.d.ts +7 -5
- package/lib/types/service/config-service.d.ts +1 -1
- package/lib/web/action-routes.js +1 -3
- package/lib/web/index.js +2 -2
- package/lib/web/welcome-ack.js +6 -5
- package/package.json +3 -5
- package/lib/api/client.js +0 -119
- package/lib/api/generated/documents.js +0 -125
- package/lib/types/api/client.d.ts +0 -29
- package/lib/types/api/generated/documents.d.ts +0 -15
- package/lib/types/api/generated/graphql.d.ts +0 -184
- /package/lib/api/{generated/graphql.js → facts.js} +0 -0
|
@@ -64,10 +64,7 @@ export function createSkillResourceProvider(deps) {
|
|
|
64
64
|
const project = await deps.projectForCwd(cwd);
|
|
65
65
|
if (project === null)
|
|
66
66
|
return [];
|
|
67
|
-
return [
|
|
68
|
-
...skills.filter(skill => skill.projectId === project.id),
|
|
69
|
-
...skills.filter(skill => skill.projectId === null),
|
|
70
|
-
];
|
|
67
|
+
return [...skills.filter(skill => skill.projectId === project.id), ...skills.filter(skill => skill.projectId === null)];
|
|
71
68
|
};
|
|
72
69
|
const listResources = async (exec) => {
|
|
73
70
|
const seen = new Set();
|
|
@@ -116,18 +113,19 @@ export function createSkillResourceProvider(deps) {
|
|
|
116
113
|
};
|
|
117
114
|
const dispatch = async (request, exec) => {
|
|
118
115
|
switch (request.method) {
|
|
119
|
-
case 'resources/templates/list':
|
|
120
|
-
|
|
121
|
-
case 'resources/
|
|
116
|
+
case 'resources/templates/list':
|
|
117
|
+
return templateList();
|
|
118
|
+
case 'resources/list':
|
|
119
|
+
return await listResources(exec);
|
|
120
|
+
case 'resources/read':
|
|
121
|
+
return await readResource(request.uri, exec);
|
|
122
122
|
}
|
|
123
123
|
};
|
|
124
124
|
return {
|
|
125
125
|
request(request, exec) {
|
|
126
126
|
return dispatch(request, exec).catch((error) => {
|
|
127
127
|
deps.warn(`技能资源请求失败(${message(error)})`);
|
|
128
|
-
return request.method === 'resources/read'
|
|
129
|
-
? errorResult(-32603, '技能资源读取失败')
|
|
130
|
-
: { resources: [] };
|
|
128
|
+
return request.method === 'resources/read' ? errorResult(-32603, '技能资源读取失败') : { resources: [] };
|
|
131
129
|
});
|
|
132
130
|
},
|
|
133
131
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { installSkillResources, skillResourceHint
|
|
1
|
+
import { installSkillResources, skillResourceHint } from "./mcp-resources.js";
|
|
2
2
|
import { candidateOf, definitionOf } from "./candidates.js";
|
|
3
3
|
function errorMessage(error) {
|
|
4
4
|
return error instanceof Error ? error.message : String(error);
|
|
@@ -13,7 +13,7 @@ export function installHubSkills(ctx, deps) {
|
|
|
13
13
|
state: () => deps.state(),
|
|
14
14
|
projectForCwd: cwd => deps.projectForCwd(cwd),
|
|
15
15
|
readFiles: skillId => deps.readFiles(skillId),
|
|
16
|
-
warn:
|
|
16
|
+
warn: message => {
|
|
17
17
|
deps.warn(message);
|
|
18
18
|
},
|
|
19
19
|
};
|
|
@@ -39,7 +39,7 @@ export function installHubSkills(ctx, deps) {
|
|
|
39
39
|
const push = (skill) => {
|
|
40
40
|
if (seen.has(skill.name))
|
|
41
41
|
return;
|
|
42
|
-
const candidate = candidateOf(skill,
|
|
42
|
+
const candidate = candidateOf(skill, message => {
|
|
43
43
|
deps.warn(message);
|
|
44
44
|
});
|
|
45
45
|
if (candidate === null)
|
|
@@ -115,7 +115,7 @@ export function installHubSkills(ctx, deps) {
|
|
|
115
115
|
};
|
|
116
116
|
const disposers = [];
|
|
117
117
|
try {
|
|
118
|
-
disposers.push(ctx.skills.registerProvider(
|
|
118
|
+
disposers.push(ctx.skills.registerProvider(control => {
|
|
119
119
|
invalidate = () => {
|
|
120
120
|
control.invalidate();
|
|
121
121
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { trackedWorkspaces, WorkspaceBook
|
|
1
|
+
import { trackedWorkspaces, WorkspaceBook } from "../../hub/workspace-book.js";
|
|
2
2
|
import { renderWorkspaces } from "../../hub/status.js";
|
|
3
|
-
import { projectForCwd, syncAll, syncBySlug, syncOne
|
|
3
|
+
import { projectForCwd, syncAll, syncBySlug, syncOne } from "./resolve.js";
|
|
4
4
|
export class WorkspaceDomain {
|
|
5
5
|
host;
|
|
6
6
|
id = 'workspace';
|
|
@@ -23,7 +23,7 @@ export class WorkspaceDomain {
|
|
|
23
23
|
missingProjects: this.missingProjects,
|
|
24
24
|
gitSlugs: this.gitSlugs,
|
|
25
25
|
tracked: () => this.tracked(),
|
|
26
|
-
publish:
|
|
26
|
+
publish: notify => {
|
|
27
27
|
this.host.patchState({ workspaces: this.tracked() });
|
|
28
28
|
if (notify)
|
|
29
29
|
this.host.emitWorkspacesUpdated();
|
|
@@ -2,7 +2,7 @@ function describe(error) {
|
|
|
2
2
|
return error instanceof Error ? error.message : String(error);
|
|
3
3
|
}
|
|
4
4
|
export function installWorkspaceFeed(ctx, handlers, warn) {
|
|
5
|
-
ctx.inject(['workspaceController'],
|
|
5
|
+
ctx.inject(['workspaceController'], scoped => {
|
|
6
6
|
const scope = scoped;
|
|
7
7
|
const controller = scope.get('workspaceController');
|
|
8
8
|
if (controller === undefined)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { canonicalPath, pendingEntry
|
|
1
|
+
import { canonicalPath, pendingEntry } from "../../hub/workspace-book.js";
|
|
2
2
|
import { matchProject, workspaceSlug } from "../../hub/git-slug.js";
|
|
3
3
|
import { codeOf } from "../../hub/errors.js";
|
|
4
4
|
/**
|
|
@@ -101,9 +101,7 @@ function resolveEntry(bag, entry, slug) {
|
|
|
101
101
|
* 结果空 = 该 slug 确实不在可读范围内,记进负缓存免得每次会话都重问。
|
|
102
102
|
*/
|
|
103
103
|
async function fetchProjects(bag, slugs) {
|
|
104
|
-
const wanted = [
|
|
105
|
-
...new Set(slugs.map(slug => slug.trim().toLowerCase())),
|
|
106
|
-
].filter(slug => slug !== '' && !bag.missingProjects.has(slug));
|
|
104
|
+
const wanted = [...new Set(slugs.map(slug => slug.trim().toLowerCase()))].filter(slug => slug !== '' && !bag.missingProjects.has(slug));
|
|
107
105
|
if (wanted.length === 0 || !bag.host.hasConnection())
|
|
108
106
|
return;
|
|
109
107
|
try {
|
package/lib/hub/compile.js
CHANGED
|
@@ -23,11 +23,7 @@ export function connectionApiKeyEnv(connectionId) {
|
|
|
23
23
|
return `SEMI2_MODEL_KEY_${connectionId.replaceAll('-', '').toUpperCase()}`;
|
|
24
24
|
}
|
|
25
25
|
export function protocolApi(protocol) {
|
|
26
|
-
return protocol === 'ANTHROPIC'
|
|
27
|
-
? 'anthropic-messages'
|
|
28
|
-
: protocol === 'OPENAI_RESPONSES'
|
|
29
|
-
? 'openai-responses'
|
|
30
|
-
: 'openai-completions';
|
|
26
|
+
return protocol === 'ANTHROPIC' ? 'anthropic-messages' : protocol === 'OPENAI_RESPONSES' ? 'openai-responses' : 'openai-completions';
|
|
31
27
|
}
|
|
32
28
|
export function modelName(model) {
|
|
33
29
|
return model.displayName ?? model.upstreamId;
|
|
@@ -48,9 +44,7 @@ export function providerProfile(compiled) {
|
|
|
48
44
|
export function remapSelections(compiled, routes) {
|
|
49
45
|
const rewrite = (selection) => {
|
|
50
46
|
const route = routes.get(selection.provider);
|
|
51
|
-
return route === undefined || route === selection.provider
|
|
52
|
-
? selection
|
|
53
|
-
: { ...selection, provider: route };
|
|
47
|
+
return route === undefined || route === selection.provider ? selection : { ...selection, provider: route };
|
|
54
48
|
};
|
|
55
49
|
return {
|
|
56
50
|
...compiled,
|
|
@@ -103,15 +97,9 @@ function presetProvider(route, connection, models) {
|
|
|
103
97
|
models: models.map(model => ({
|
|
104
98
|
id: model.upstreamId,
|
|
105
99
|
name: modelName(model),
|
|
106
|
-
...(model.metadata.contextWindow === null
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
...(model.metadata.maxTokens === null
|
|
110
|
-
? {}
|
|
111
|
-
: { maxTokens: model.metadata.maxTokens }),
|
|
112
|
-
...(model.metadata.input === null
|
|
113
|
-
? {}
|
|
114
|
-
: { input: model.metadata.input.map(modality) }),
|
|
100
|
+
...(model.metadata.contextWindow === null ? {} : { contextWindow: model.metadata.contextWindow }),
|
|
101
|
+
...(model.metadata.maxTokens === null ? {} : { maxTokens: model.metadata.maxTokens }),
|
|
102
|
+
...(model.metadata.input === null ? {} : { input: model.metadata.input.map(modality) }),
|
|
115
103
|
})),
|
|
116
104
|
};
|
|
117
105
|
}
|
package/lib/hub/credentials.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// 解析优先级:运行端契约 > dsh credentials(启动环境层 → provider 存储 → .env 文件层)> 进程 env 快照。
|
|
3
3
|
// 启动环境层(`source: env`)的令牌只属 CNB 端(secret imports);宿主 / 容器形态靠设置页登录(见 resolveConnectionFields 的筛选)。
|
|
4
4
|
// 令牌不是一次性凭据:`SEMI2_ACCESS_TOKEN` 首次读取成功后从进程 env 删除,内存副本必须活满整个进程。
|
|
5
|
-
import { credentialRef
|
|
5
|
+
import { credentialRef } from '@deepseek-ai/dsh-credentials';
|
|
6
6
|
import { ConfigurationError } from "./errors.js";
|
|
7
7
|
export const CONFIG_URL_REF = 'SEMI2_CONFIG_URL';
|
|
8
8
|
export const ACCESS_TOKEN_REF = 'SEMI2_ACCESS_TOKEN';
|
|
@@ -19,9 +19,7 @@ async function resolveField(credentials, ref, explicit, fallback) {
|
|
|
19
19
|
if (credentials !== undefined) {
|
|
20
20
|
const resolved = await credentials.resolve(ref);
|
|
21
21
|
const value = text(resolved?.value);
|
|
22
|
-
return value === undefined
|
|
23
|
-
? undefined
|
|
24
|
-
: { value, source: resolved?.source ?? 'credentials' };
|
|
22
|
+
return value === undefined ? undefined : { value, source: resolved?.source ?? 'credentials' };
|
|
25
23
|
}
|
|
26
24
|
const value = text(fallback);
|
|
27
25
|
return value === undefined ? undefined : { value, source: 'env' };
|
|
@@ -47,10 +45,7 @@ export async function resolveConnectionFields(credentials, runtimeContract, envi
|
|
|
47
45
|
// 宿主 / 容器形态认这一层等于自锁——该层只读(dsh 拒绝 set/unset)又排在临时存储之前,设置页会只剩只读状态
|
|
48
46
|
// (没有登录入口),登录写进去的令牌也读不回来(编排层另在启动时把它从进程 env 摘掉)。
|
|
49
47
|
// 地址不是密钥、还是登录表单的预填来源,任何形态都保留;契约与登录存储的令牌一律照收。
|
|
50
|
-
const usable = token !== undefined &&
|
|
51
|
-
(token.source !== 'env' || runtimeContract?.platform === 'cnb')
|
|
52
|
-
? token
|
|
53
|
-
: undefined;
|
|
48
|
+
const usable = token !== undefined && (token.source !== 'env' || runtimeContract?.platform === 'cnb') ? token : undefined;
|
|
54
49
|
return {
|
|
55
50
|
...(url === undefined ? {} : { url }),
|
|
56
51
|
...(usable === undefined ? {} : { token: usable }),
|
package/lib/hub/errors.js
CHANGED
|
@@ -10,7 +10,5 @@ export class ConfigurationError extends Error {
|
|
|
10
10
|
}
|
|
11
11
|
/** 领域失败码归一:非 ConfigurationError 的异常一律按通用码处理。 */
|
|
12
12
|
export function codeOf(error) {
|
|
13
|
-
return error instanceof ConfigurationError
|
|
14
|
-
? error.code
|
|
15
|
-
: 'CONFIG_APPLY_FAILED';
|
|
13
|
+
return error instanceof ConfigurationError ? error.code : 'CONFIG_APPLY_FAILED';
|
|
16
14
|
}
|
package/lib/hub/status.js
CHANGED
|
@@ -13,7 +13,7 @@ export const STATUS_BASE_FIELDS = [
|
|
|
13
13
|
const MAX_LISTED_WORKSPACES = 20;
|
|
14
14
|
/** `标题 → slug(项目名)`:只有命中 hub 项目的工作区才进这里,读一行就知道同步到了什么。 */
|
|
15
15
|
export function renderWorkspaces(workspaces) {
|
|
16
|
-
const listed = workspaces.slice(0, MAX_LISTED_WORKSPACES).map(
|
|
16
|
+
const listed = workspaces.slice(0, MAX_LISTED_WORKSPACES).map(entry => {
|
|
17
17
|
const slug = entry.slug ?? '未解析';
|
|
18
18
|
const project = entry.name ?? '未匹配';
|
|
19
19
|
return `${entry.title} → ${slug}(${project})`;
|
|
@@ -60,9 +60,7 @@ export class WorkspaceBook {
|
|
|
60
60
|
seen.add(item.path);
|
|
61
61
|
this.pathById.set(item.workspaceId, item.path);
|
|
62
62
|
const previous = this.byPath.get(item.path);
|
|
63
|
-
this.byPath.set(item.path, previous === undefined
|
|
64
|
-
? pendingEntry(item.path, item.title)
|
|
65
|
-
: { ...previous, title: item.title, implicit: false });
|
|
63
|
+
this.byPath.set(item.path, previous === undefined ? pendingEntry(item.path, item.title) : { ...previous, title: item.title, implicit: false });
|
|
66
64
|
}
|
|
67
65
|
for (const [path, entry] of [...this.byPath]) {
|
|
68
66
|
if (entry.implicit || seen.has(path))
|
|
@@ -78,9 +76,7 @@ export class WorkspaceBook {
|
|
|
78
76
|
upsertWorkspace(view) {
|
|
79
77
|
this.pathById.set(view.workspaceId, view.path);
|
|
80
78
|
const previous = this.byPath.get(view.path);
|
|
81
|
-
const next = previous === undefined
|
|
82
|
-
? pendingEntry(view.path, view.title)
|
|
83
|
-
: { ...previous, title: view.title, implicit: false };
|
|
79
|
+
const next = previous === undefined ? pendingEntry(view.path, view.title) : { ...previous, title: view.title, implicit: false };
|
|
84
80
|
this.byPath.set(view.path, next);
|
|
85
81
|
return next;
|
|
86
82
|
}
|
package/lib/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import z from '@deepseek-ai/schemastery';
|
|
2
2
|
import { installConfiguration } from "./service/config-service.js";
|
|
3
3
|
import { installWebClient } from "./web/index.js";
|
|
4
|
-
export { SEMI2_RUNTIME_SERVICE, SEMI2_SECTION_ORDERS, runtimeFromCnbEnv, semi2SectionOrder
|
|
4
|
+
export { SEMI2_RUNTIME_SERVICE, SEMI2_SECTION_ORDERS, runtimeFromCnbEnv, semi2SectionOrder } from "./contract.js";
|
|
5
|
+
export { cnbTokenPath, writeCnbSessionToken } from "./cnb-token.js";
|
|
5
6
|
// 事件签名声明(semi2/config-updated / semi2/workspaces-updated)唯一出处。
|
|
6
7
|
import "./events.js";
|
|
7
8
|
export const name = 'semi2-dsh';
|
|
@@ -60,7 +61,7 @@ export function apply(ctx, config) {
|
|
|
60
61
|
sync: scope => configuration.session.sync(scope),
|
|
61
62
|
health: () => configuration.health.snapshot(),
|
|
62
63
|
status: () => configuration.status(),
|
|
63
|
-
},
|
|
64
|
+
}, contributor => {
|
|
64
65
|
configuration.registerHealth(contributor);
|
|
65
66
|
}, { fenceTimeoutMs: options.webFenceTimeoutMs });
|
|
66
67
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createRestApi } from "../api/rest.js";
|
|
2
2
|
import { createPorts } from "./ports.js";
|
|
3
3
|
import { Orchestrator } from "./orchestrator.js";
|
|
4
4
|
import { reportEventResult } from "./audit.js";
|
|
@@ -30,12 +30,11 @@ export function installConfiguration(ctx, options = {}) {
|
|
|
30
30
|
// api 门面读连接凭据的桥:闭包延迟求值(编排层构造完成后才可能被调用)。
|
|
31
31
|
const engineRef = { current: undefined };
|
|
32
32
|
const orchestrator = new Orchestrator(ctx, options.api ??
|
|
33
|
-
|
|
33
|
+
createRestApi({
|
|
34
34
|
readConnection: () => engineRef.current?.connection() ?? null,
|
|
35
35
|
environment: { ...process.env },
|
|
36
36
|
// 契约缺席即宿主直跑:本端标识回落 `host`,别让会话落成「未标注」。
|
|
37
|
-
readChannel: () => engineRef.current?.contract()?.channel ??
|
|
38
|
-
(engineRef.current?.platform === 'host' ? 'host' : undefined),
|
|
37
|
+
readChannel: () => engineRef.current?.contract()?.channel ?? (engineRef.current?.platform === 'host' ? 'host' : undefined),
|
|
39
38
|
}), ports, { ...process.env }, options);
|
|
40
39
|
engineRef.current = orchestrator;
|
|
41
40
|
// 客户端会话日志的事实来源:旁听 dsh 的会话创建 / 销毁(服务缺席时自动跳过)。
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// 契约只解析一次(适配器在 apply 里同步 provide);凭据三层解析在 hub/credentials(纯逻辑)。
|
|
3
3
|
import { setTimeout as delay } from 'node:timers/promises';
|
|
4
4
|
import { SEMI2_RUNTIME_SERVICE } from "../contract.js";
|
|
5
|
-
import { connectionOf, resolveConnectionFields
|
|
5
|
+
import { connectionOf, resolveConnectionFields } from "../hub/credentials.js";
|
|
6
6
|
import { codeOf } from "../hub/errors.js";
|
|
7
7
|
/** 等运行端契约出现的上限;超时按宿主直跑(无适配器)处理。 */
|
|
8
8
|
export const CONTRACT_TIMEOUT_MS = 5_000;
|
package/lib/service/lifecycle.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { clearConnection, connectionOf, storeConnection
|
|
1
|
+
import { clearConnection, connectionOf, storeConnection } from "../hub/credentials.js";
|
|
2
2
|
import { codeOf, ConfigurationError } from "../hub/errors.js";
|
|
3
3
|
import { initialState } from "../hub/facts.js";
|
|
4
4
|
import { appliedState, credentialsWritable, failedState } from "./status.js";
|
|
5
5
|
import { DOMAINS } from "./registry.js";
|
|
6
|
-
import { connectionSnapshot, exchangeCnbCredential, resolveConnectionWithRetry, resolveContract
|
|
6
|
+
import { connectionSnapshot, exchangeCnbCredential, resolveConnectionWithRetry, resolveContract } from "./connection.js";
|
|
7
7
|
/** 首次加载;未配凭据或失败只落到 state.error,绝不 throw,也不注入任何配置。 */
|
|
8
8
|
export async function start(engine) {
|
|
9
9
|
try {
|
|
@@ -191,7 +191,7 @@ async function ensureSnapshot(engine) {
|
|
|
191
191
|
return connectionSnapshot(engine.ports, engine.environment, engine.contract());
|
|
192
192
|
}
|
|
193
193
|
async function exchange(engine, connection) {
|
|
194
|
-
return exchangeCnbCredential(engine.api,
|
|
194
|
+
return exchangeCnbCredential(engine.api, message => {
|
|
195
195
|
engine.warn(message);
|
|
196
196
|
}, connection, engine.contract()?.platform);
|
|
197
197
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { setTimeout as delay } from 'node:timers/promises';
|
|
2
|
-
import { initialState
|
|
2
|
+
import { initialState } from "../hub/facts.js";
|
|
3
3
|
import { DOMAINS } from "./registry.js";
|
|
4
4
|
import { NOOP_WORKSPACE } from "./workspace-ops.js";
|
|
5
5
|
import { CONTRACT_TIMEOUT_MS, CREDENTIAL_RETRY } from "./connection.js";
|
|
@@ -121,9 +121,7 @@ export class Orchestrator {
|
|
|
121
121
|
domain.invalidate?.();
|
|
122
122
|
}
|
|
123
123
|
noteFailure(area, error) {
|
|
124
|
-
const code = error instanceof Error && 'code' in error
|
|
125
|
-
? String(error.code)
|
|
126
|
-
: 'CONFIG_APPLY_FAILED';
|
|
124
|
+
const code = error instanceof Error && 'code' in error ? String(error.code) : 'CONFIG_APPLY_FAILED';
|
|
127
125
|
this.warn(`${area}应用失败(${code}),该领域降级,其余领域照常`);
|
|
128
126
|
return code;
|
|
129
127
|
}
|
package/lib/service/ports.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { transact
|
|
1
|
+
import { transact } from "./settings-tx.js";
|
|
2
2
|
const SETTINGS_TIMEOUT_MS = 5_000;
|
|
3
3
|
const NAMESPACE_WAIT_MS = 2_000;
|
|
4
4
|
const WRITE_TIMEOUT_MS = 3_000;
|
|
@@ -7,8 +7,8 @@ export function createPorts(ctx, warn, options = {}) {
|
|
|
7
7
|
const namespaceWaitMs = options.namespaceWaitMs ?? NAMESPACE_WAIT_MS;
|
|
8
8
|
const writeTimeoutMs = options.writeTimeoutMs ?? WRITE_TIMEOUT_MS;
|
|
9
9
|
const tx = { warn, namespaceWaitMs, writeTimeoutMs };
|
|
10
|
-
const service = (key) => new Promise(
|
|
11
|
-
ctx.inject([key],
|
|
10
|
+
const service = (key) => new Promise(resolve => {
|
|
11
|
+
ctx.inject([key], injected => {
|
|
12
12
|
resolve(injected.get(key));
|
|
13
13
|
});
|
|
14
14
|
});
|
|
@@ -19,7 +19,7 @@ export function createPorts(ctx, warn, options = {}) {
|
|
|
19
19
|
const timer = globalThis.setTimeout(() => {
|
|
20
20
|
resolve(undefined);
|
|
21
21
|
}, settingsTimeoutMs);
|
|
22
|
-
ctx.inject(['settings'],
|
|
22
|
+
ctx.inject(['settings'], injected => {
|
|
23
23
|
globalThis.clearTimeout(timer);
|
|
24
24
|
resolve(injected.get('settings'));
|
|
25
25
|
});
|
package/lib/service/registry.js
CHANGED
|
@@ -19,12 +19,6 @@ export const DOMAINS = [
|
|
|
19
19
|
redmineDomain,
|
|
20
20
|
];
|
|
21
21
|
/** 完整状态字段顺序(基座 + 按注册表顺序的领域字段);状态视图与健康字段共用这一段。 */
|
|
22
|
-
export const STATUS_FIELDS = [
|
|
23
|
-
...STATUS_BASE_FIELDS,
|
|
24
|
-
...DOMAINS.flatMap(domain => [...domain.statusKeys]),
|
|
25
|
-
];
|
|
22
|
+
export const STATUS_FIELDS = [...STATUS_BASE_FIELDS, ...DOMAINS.flatMap(domain => [...domain.statusKeys])];
|
|
26
23
|
/** 健康字段键(能力 + 领域,值是 `状态:说明` 形式的编码串;读侧拆码)。 */
|
|
27
|
-
export const HEALTH_FIELDS = [
|
|
28
|
-
'healthWeb',
|
|
29
|
-
...DOMAINS.map(domain => `health:${domain.id}`),
|
|
30
|
-
];
|
|
24
|
+
export const HEALTH_FIELDS = ['healthWeb', ...DOMAINS.map(domain => `health:${domain.id}`)];
|
package/lib/service/sessions.js
CHANGED
|
@@ -46,13 +46,8 @@ async function reportCreated(engine, session) {
|
|
|
46
46
|
...(scope.workspacePath === null ? {} : { workspacePath: scope.workspacePath }),
|
|
47
47
|
...(scope.workspaceSlug === null ? {} : { workspaceSlug: scope.workspaceSlug }),
|
|
48
48
|
...(scope.projectSlug === null ? {} : { projectSlug: scope.projectSlug }),
|
|
49
|
-
...(selection?.model === undefined || selection.model === ''
|
|
50
|
-
|
|
51
|
-
: { modelId: selection.model }),
|
|
52
|
-
...(selection?.reasoningEffort === undefined ||
|
|
53
|
-
selection.reasoningEffort === ''
|
|
54
|
-
? {}
|
|
55
|
-
: { reasoningLevel: selection.reasoningEffort }),
|
|
49
|
+
...(selection?.model === undefined || selection.model === '' ? {} : { modelId: selection.model }),
|
|
50
|
+
...(selection?.reasoningEffort === undefined || selection.reasoningEffort === '' ? {} : { reasoningLevel: selection.reasoningEffort }),
|
|
56
51
|
detail: { cwd: scope.workspacePath },
|
|
57
52
|
});
|
|
58
53
|
}
|
|
@@ -72,7 +67,7 @@ async function reportDisposed(engine, session) {
|
|
|
72
67
|
* 返回订阅光纤(可 await 到就绪),测试据它确认监听已挂上。
|
|
73
68
|
*/
|
|
74
69
|
export function installSessionReporting(engine, ctx = engine.ctx) {
|
|
75
|
-
return ctx.inject(['sessions'],
|
|
70
|
+
return ctx.inject(['sessions'], scoped => {
|
|
76
71
|
const track = (session, report) => {
|
|
77
72
|
if (!isUserSession(session))
|
|
78
73
|
return;
|
|
@@ -80,10 +75,10 @@ export function installSessionReporting(engine, ctx = engine.ctx) {
|
|
|
80
75
|
engine.warn(`会话上报失败:${error instanceof Error ? error.message : String(error)}`);
|
|
81
76
|
});
|
|
82
77
|
};
|
|
83
|
-
scoped.on('session/created',
|
|
78
|
+
scoped.on('session/created', session => {
|
|
84
79
|
track(session, reportCreated);
|
|
85
80
|
});
|
|
86
|
-
scoped.on('session/disposed',
|
|
81
|
+
scoped.on('session/disposed', session => {
|
|
87
82
|
track(session, reportDisposed);
|
|
88
83
|
});
|
|
89
84
|
});
|
|
@@ -52,9 +52,7 @@ function viewOf(settings, ns) {
|
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
54
|
function recordOf(value) {
|
|
55
|
-
return value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
56
|
-
? value
|
|
57
|
-
: {};
|
|
55
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
58
56
|
}
|
|
59
57
|
/** 落一笔写:section 走 replace(缺则回退 update 的合并写入)、ops 走 mutate;都带上 CAS。 */
|
|
60
58
|
function applyWrite(settings, ns, write, revision, options) {
|
|
@@ -75,8 +73,7 @@ function applyWrite(settings, ns, write, revision, options) {
|
|
|
75
73
|
}
|
|
76
74
|
/** 官方冲突(读与写之间有人改了段):按结构判 code,防闭包内模块实例分裂时漏认。 */
|
|
77
75
|
function isConflict(error) {
|
|
78
|
-
return error instanceof SettingsConflictError ||
|
|
79
|
-
(error instanceof Error && error.code === 'SETTINGS_CONFLICT');
|
|
76
|
+
return error instanceof SettingsConflictError || (error instanceof Error && error.code === 'SETTINGS_CONFLICT');
|
|
80
77
|
}
|
|
81
78
|
/** 到点返回 {@link EXPIRED},否则原样给出结果;定时器总在返回前清掉。 */
|
|
82
79
|
async function within(promise, ms) {
|
|
@@ -84,7 +81,7 @@ async function within(promise, ms) {
|
|
|
84
81
|
try {
|
|
85
82
|
return await Promise.race([
|
|
86
83
|
promise,
|
|
87
|
-
new Promise(
|
|
84
|
+
new Promise(resolve => {
|
|
88
85
|
timer = globalThis.setTimeout(() => {
|
|
89
86
|
resolve(EXPIRED);
|
|
90
87
|
}, ms);
|
package/lib/service/status.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { statusFields, workspaceTabs, WORKSPACE_TABS_FIELD
|
|
1
|
+
import { statusFields, workspaceTabs, WORKSPACE_TABS_FIELD } from "../hub/status.js";
|
|
2
2
|
import { aggregateHealth, healthText } from "./health.js";
|
|
3
3
|
import { DOMAINS } from "./registry.js";
|
|
4
4
|
import { reportEvent } from "./audit.js";
|
|
@@ -55,9 +55,7 @@ export async function failedState(engine, code) {
|
|
|
55
55
|
/** boot 事实 → ready 状态(技能切片取自 skills 领域;项目缓存随装配作废,由工作区同步重取)。 */
|
|
56
56
|
export function appliedState(engine, boot, compiled, errorCode) {
|
|
57
57
|
const defaultModelId = boot.runtimeDefaultModelId ?? null;
|
|
58
|
-
const selection = compiled === null || defaultModelId === null
|
|
59
|
-
? null
|
|
60
|
-
: (compiled.selections.get(defaultModelId) ?? null);
|
|
58
|
+
const selection = compiled === null || defaultModelId === null ? null : (compiled.selections.get(defaultModelId) ?? null);
|
|
61
59
|
const skills = engine.peer('skills')?.skills() ?? null;
|
|
62
60
|
const current = engine.state();
|
|
63
61
|
return {
|
|
@@ -67,16 +65,14 @@ export function appliedState(engine, boot, compiled, errorCode) {
|
|
|
67
65
|
consoleUrl: engine.connection()?.url ?? current.consoleUrl,
|
|
68
66
|
loadedAt: new Date().toISOString(),
|
|
69
67
|
identity: boot.runtimeViewer ?? null,
|
|
70
|
-
model: selection === null
|
|
71
|
-
? null
|
|
72
|
-
: { id: selection.model, provider: selection.provider },
|
|
68
|
+
model: selection === null ? null : { id: selection.model, provider: selection.provider },
|
|
73
69
|
skills,
|
|
74
70
|
projects: [],
|
|
75
71
|
};
|
|
76
72
|
}
|
|
77
73
|
/** 健康快照:领域项按注册表(缺席记 unavailable)+ 能力贡献者按登记。 */
|
|
78
74
|
export function healthSnapshot(engine) {
|
|
79
|
-
return aggregateHealth(DOMAINS.map(domain => domain.id),
|
|
75
|
+
return aggregateHealth(DOMAINS.map(domain => domain.id), id => {
|
|
80
76
|
// 本端没有 settings 服务:没有任何领域被装配(写不进去就不装),整端记不适用。
|
|
81
77
|
if (engine.settingsMissing()) {
|
|
82
78
|
return { state: 'unavailable', note: '本端无 settings 服务,不注入配置' };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { decodeModelConfiguration } from './http-models.ts';
|
|
2
|
+
import { decodeSkill } from './http-assets.ts';
|
|
3
|
+
import type { operations } from './generated/http.ts';
|
|
4
|
+
type WireBootstrap = operations['runtimeBootstrap']['responses'][200]['content']['application/json'];
|
|
5
|
+
export type RuntimeViewer = WireBootstrap['viewer'];
|
|
6
|
+
export type RuntimeBlock<T> = {
|
|
7
|
+
status: 'ok';
|
|
8
|
+
data: T;
|
|
9
|
+
} | {
|
|
10
|
+
status: 'error';
|
|
11
|
+
code: string;
|
|
12
|
+
};
|
|
13
|
+
export interface BootstrapEnvelope {
|
|
14
|
+
viewer: RuntimeViewer;
|
|
15
|
+
models: RuntimeBlock<ReturnType<typeof decodeModelConfiguration>>;
|
|
16
|
+
skills: RuntimeBlock<ReturnType<typeof decodeSkill>[]>;
|
|
17
|
+
selfPortrait: RuntimeBlock<string>;
|
|
18
|
+
redmine: WireBootstrap['redmine'];
|
|
19
|
+
}
|
|
20
|
+
/** 失败块不得伪装成空配置;缺块或未知状态说明协议不完整。 */
|
|
21
|
+
export declare function decodeBlock<T>(value: unknown, decode: (data: unknown) => T): RuntimeBlock<T>;
|
|
22
|
+
export declare function decodeBootstrap(value: unknown): BootstrapEnvelope;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { decodeCredential, decodeModel, protocol, reasoning } from './http-models.ts';
|
|
2
|
+
import type { decodeEvent, decodeFileContent, decodeProfile, decodeProject, decodeSkill } from './http-assets.ts';
|
|
3
|
+
import type { RuntimeViewer } from './bootstrap.ts';
|
|
4
|
+
/** 解码后的领域事实;HTTP 空值在这里保留,不与读取失败合并。 */
|
|
5
|
+
export type RuntimeModel = ReturnType<typeof decodeModel>;
|
|
6
|
+
export type RuntimeProject = ReturnType<typeof decodeProject>;
|
|
7
|
+
export type RuntimeCredential = ReturnType<typeof decodeCredential>;
|
|
8
|
+
export type ModelProtocol = ReturnType<typeof protocol>;
|
|
9
|
+
export type ReasoningLevel = NonNullable<ReturnType<typeof reasoning>>;
|
|
10
|
+
export type InputModality = NonNullable<RuntimeModel['metadata']['input']>[number];
|
|
11
|
+
export interface BootFacts {
|
|
12
|
+
runtimeViewer: RuntimeViewer | null;
|
|
13
|
+
runtimeModels: RuntimeModel[] | null;
|
|
14
|
+
runtimeModelCredentials: RuntimeCredential[] | null;
|
|
15
|
+
runtimeDefaultModelId: string | null;
|
|
16
|
+
runtimeHeadlessModels: {
|
|
17
|
+
planModelId: string | null;
|
|
18
|
+
executeModelId: string | null;
|
|
19
|
+
} | null;
|
|
20
|
+
runtimeSkillCatalog: ReturnType<typeof decodeSkill>[] | null;
|
|
21
|
+
runtimeSelfPortrait: string | null;
|
|
22
|
+
runtimeRedmine: {
|
|
23
|
+
baseUrl: string | null;
|
|
24
|
+
apiKey: string | null;
|
|
25
|
+
} | null;
|
|
26
|
+
}
|
|
27
|
+
export interface WorkspaceFacts {
|
|
28
|
+
runtimeProjects: RuntimeProject[] | null;
|
|
29
|
+
}
|
|
30
|
+
export interface CredentialFacts {
|
|
31
|
+
runtimeModelCredentials: RuntimeCredential[] | null;
|
|
32
|
+
}
|
|
33
|
+
export interface SkillFileFacts {
|
|
34
|
+
runtimeSkillFiles: ReturnType<typeof decodeFileContent>[] | null;
|
|
35
|
+
}
|
|
36
|
+
export interface ProjectProfileFacts {
|
|
37
|
+
runtimeProjectProfile: ReturnType<typeof decodeProfile> | null;
|
|
38
|
+
}
|
|
39
|
+
export interface ReportedEventFacts {
|
|
40
|
+
runtimeReportEvent: ReturnType<typeof decodeEvent>;
|
|
41
|
+
}
|