@fa-software/semi2-dsh 0.0.14-preview.20260927035651 → 0.0.15

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.
@@ -1,184 +0,0 @@
1
- import { statusFields, workspaceTabs, WORKSPACE_TABS_FIELD, } from "../hub/status.js";
2
- import { SEMI2_SETTINGS_ENTRY, userSection } from "./namespaces.js";
3
- import { HEALTH_FIELDS_SCHEMA_KEYS, STATUS_FIELDS } from "./settings-sections.js";
4
- import { aggregateHealth, healthText } from "./health.js";
5
- import { DOMAINS } from "./registry.js";
6
- import { reportEvent } from "./audit.js";
7
- /** 判成「没有读者」时的重判节奏与次数:热装瞬间服务可见性可能比插件激活晚。 */
8
- const PUBLISH_RECHECK_TRIES = 3;
9
- const PUBLISH_RECHECK_MS = 1_000;
10
- /** 状态行只由本插件写:盘上残留认领按这些键名判定(旧版本落过盘的展示键)。 */
11
- const OWNED_STATUS_KEYS = [
12
- ...STATUS_FIELDS,
13
- ...HEALTH_FIELDS_SCHEMA_KEYS,
14
- WORKSPACE_TABS_FIELD,
15
- 'entries',
16
- ];
17
- /** 发布失败只提示一次:每轮状态变化都喊一遍没有信息量。 */
18
- const warnedPublishFailure = new WeakSet();
19
- /** 盘上残留认领只做一次(每个进程一份引擎)。 */
20
- const claimedLegacy = new WeakSet();
21
- /**
22
- * 状态 + 健康 + 工作区 tab 一次发布:整份写进本插件设置段的运行期 config。
23
- * 盘上组合层由 `applyStatusRow` 原样带上,消费方写在同一行里的调参不会被冲掉。
24
- */
25
- export async function publishStatus(engine) {
26
- // headless 应用(既无网页服务也无 dsh 工作区服务)没有读者:不发,也不留残渣。
27
- if (!engine.ports.hasAudience())
28
- return 'headless';
29
- if (engine.settings() === undefined)
30
- return 'headless';
31
- const facts = {
32
- credentialSource: engine.connection()?.source ?? null,
33
- platform: engine.platform,
34
- startupDir: engine.startupDir,
35
- };
36
- const slices = [];
37
- for (const descriptor of DOMAINS) {
38
- const domain = engine.domains.get(descriptor.id);
39
- if (domain !== undefined)
40
- slices.push(domain.statusFields(engine.state()));
41
- }
42
- const fields = statusFields(engine.state(), facts, slices);
43
- const health = healthSnapshot(engine);
44
- const section = {
45
- ...fields,
46
- ...Object.fromEntries(Object.entries(health).map(([key, item]) => [
47
- key,
48
- `${item.state}:${healthText(item)}`,
49
- ])),
50
- [WORKSPACE_TABS_FIELD]: workspaceTabs(engine.state()),
51
- };
52
- try {
53
- // 到点降级:调用方不再等;内存行本身立即生效,超时只说明这一拍没赶上。
54
- const applied = await within(engine.ports.applyStatusRow(SEMI2_SETTINGS_ENTRY, section), engine.writeTimeoutMs);
55
- return applied === true ? 'applied' : 'failed';
56
- }
57
- catch (error) {
58
- if (!warnedPublishFailure.has(engine)) {
59
- warnedPublishFailure.add(engine);
60
- engine.warn(`状态段发布失败(${error instanceof Error ? error.message : String(error)}),状态不会下发`);
61
- }
62
- return 'failed';
63
- }
64
- }
65
- /**
66
- * 旧版本把状态段写进过 profile patch:启动后按名字认领一次,只撤下本插件写过的展示键,
67
- * 消费方设在同一行里的调参原样留着。这是本插件唯一的落盘写;失败只 warn,下轮再试。
68
- */
69
- export async function dropLegacyStatusRow(engine) {
70
- if (claimedLegacy.has(engine))
71
- return;
72
- const settings = engine.settings();
73
- if (settings === undefined)
74
- return;
75
- const leftover = Object.keys(userSection(settings, SEMI2_SETTINGS_ENTRY))
76
- .filter(key => OWNED_STATUS_KEYS.includes(key));
77
- if (leftover.length === 0) {
78
- claimedLegacy.add(engine);
79
- return;
80
- }
81
- try {
82
- await engine.ports.clearKeys(settings, SEMI2_SETTINGS_ENTRY, leftover);
83
- claimedLegacy.add(engine);
84
- engine.warn('盘上 semi2-dsh 还留着旧版本的状态段,已撤下并改按运行期内存行发布');
85
- }
86
- catch (error) {
87
- engine.warn(`盘上状态段残留清理失败(${error instanceof Error ? error.message : String(error)}),下轮再试`);
88
- }
89
- }
90
- /** 领域在会话期产生新状态时请求一次发布:fire-and-forget,失败只 warn,不影响会话。 */
91
- export function scheduleStatusPublish(engine) {
92
- void publishStatus(engine)
93
- .then((outcome) => {
94
- if (outcome !== 'applied')
95
- schedulePublishRecheck(engine, 1);
96
- })
97
- .catch((error) => {
98
- engine.warn(`状态发布失败(${error instanceof Error ? error.message : String(error)})`);
99
- });
100
- }
101
- /** 有界重判:没发出去(服务晚到场 / 条目还没挂上)时按 PUBLISH_RECHECK_* 给的节奏与次数重判。 */
102
- function schedulePublishRecheck(engine, attempt) {
103
- if (attempt > PUBLISH_RECHECK_TRIES)
104
- return;
105
- const timer = globalThis.setTimeout(() => {
106
- void publishStatus(engine).then((outcome) => {
107
- if (outcome !== 'applied')
108
- schedulePublishRecheck(engine, attempt + 1);
109
- }, () => {
110
- // 重判失败不排下一轮:下一次状态变化会重新发布。
111
- });
112
- }, PUBLISH_RECHECK_MS);
113
- timer.unref();
114
- }
115
- /** 到点返回 `'expired'`,否则原样给出结果(超时不取消:调用方降级,写入照常落定)。 */
116
- async function within(promise, ms) {
117
- let timer;
118
- try {
119
- return await Promise.race([
120
- promise,
121
- new Promise((resolve) => {
122
- timer = globalThis.setTimeout(() => {
123
- resolve('expired');
124
- }, ms);
125
- }),
126
- ]);
127
- }
128
- finally {
129
- if (timer !== undefined)
130
- globalThis.clearTimeout(timer);
131
- }
132
- }
133
- /** 整体读取失败:只落 state.error 与失败态,不注入任何配置(可重载重试)。 */
134
- export async function publishFailed(engine, code) {
135
- engine.patchState({
136
- status: 'failed',
137
- error: code,
138
- workspaces: engine.workspace().tracked(),
139
- });
140
- engine.emitConfigUpdated();
141
- // 失败也必须发布:登录成功但同步失败时,错误码是唯一提示(客户端按 error 渲染「最近失败」)。
142
- await publishStatus(engine);
143
- // 启动期的整体失败落一条运行端事件:判「本端起没起来」不必翻容器日志。
144
- await reportEvent(engine, {
145
- action: 'SESSION_FAILED',
146
- outcome: 'ERROR',
147
- code,
148
- });
149
- }
150
- /** boot 事实 → ready 状态(技能切片取自 skills 领域;项目缓存随装配作废,由工作区同步重取)。 */
151
- export function appliedState(engine, boot, compiled, errorCode) {
152
- const defaultModelId = boot.runtimeDefaultModelId ?? null;
153
- const selection = compiled === null || defaultModelId === null
154
- ? null
155
- : (compiled.selections.get(defaultModelId) ?? null);
156
- const skills = engine.peer('skills')?.skills() ?? null;
157
- const current = engine.state();
158
- return {
159
- ...current,
160
- status: 'ready',
161
- error: errorCode,
162
- consoleUrl: engine.connection()?.url ?? current.consoleUrl,
163
- loadedAt: new Date().toISOString(),
164
- identity: boot.runtimeViewer ?? null,
165
- model: selection === null
166
- ? null
167
- : { id: selection.model, provider: selection.provider },
168
- skills,
169
- projects: [],
170
- };
171
- }
172
- /** 健康快照:领域项按注册表(缺席记 unavailable)+ 能力贡献者按登记。 */
173
- export function healthSnapshot(engine) {
174
- return aggregateHealth(DOMAINS.map(domain => domain.id), (id) => {
175
- // 本端没有 settings 服务:没有任何领域被装配(写不进去就不装),整端记不适用。
176
- if (engine.settingsMissing()) {
177
- return { state: 'unavailable', note: '本端无 settings 服务,不注入配置' };
178
- }
179
- const domain = engine.domains.get(id);
180
- if (domain === undefined)
181
- return { state: 'unavailable', note: '本端不提供该能力(依赖缺席)' };
182
- return domain.health();
183
- }, engine.healthContributorsView());
184
- }
@@ -1,40 +0,0 @@
1
- // @fa-software/semi2-dsh/service/settings-sections —— 插件自身设置段的字段声明(按注册表聚合生成)。
2
- // dsh 的设置段 = Profile 插件条目(id = `SEMI2_SETTINGS_ENTRY`):只有声明为 volatile 的字段才会被
3
- // `describe()` 看见、才能实时写,所以状态 / 健康 / 工作区 tab 全部在这里声明(内容由状态行经内存覆盖
4
- // 写进去,不落盘);数值旋钮不在此列——它们是加载期参数,改动理应重挂载;模型路由也不在此列——它们走内存覆盖行。
5
- import z from '@deepseek-ai/schemastery';
6
- import { STATUS_BASE_FIELDS, WORKSPACE_TABS_FIELD } from "../hub/status.js";
7
- import { DOMAINS } from "./registry.js";
8
- /** 完整状态字段顺序(基座 + 按注册表顺序的领域字段);UI 与健康字段共用这一段。 */
9
- export const STATUS_FIELDS = [
10
- ...STATUS_BASE_FIELDS,
11
- ...DOMAINS.flatMap(domain => [...domain.statusKeys]),
12
- ];
13
- /** 健康字段 schema 键(能力 + 领域,值是 `ok:正常` 形式的编码串;读侧拆码)。 */
14
- export const HEALTH_FIELDS_SCHEMA_KEYS = [
15
- 'healthWeb',
16
- ...DOMAINS.map(domain => `health:${domain.id}`),
17
- ];
18
- /** tab 列表字段:结构化数组(界面按作用域渲染用;扁平字符串继续留给纯文本展示)。 */
19
- const WORKSPACE_TABS_SCHEMA = z
20
- .array(z.object({
21
- slug: z.string().default(''),
22
- title: z.string().default(''),
23
- projectId: z.string().default(''),
24
- project: z.string().default(''),
25
- syncedAt: z.string().default(''),
26
- implicit: z.boolean().default(false),
27
- }))
28
- .default([]);
29
- /** 一个字段表 → 全部置 volatile(整体一个可实时写的节点,子路径共用该性质)。 */
30
- function allVolatile(fields) {
31
- return Object.fromEntries(Object.entries(fields).map(([key, field]) => [key, field.volatile()]));
32
- }
33
- /** 插件 Config 的可写字段表(并入入口的 Config schema);写入只走 volatile 路径。 */
34
- export const STATE_CONFIG_FIELDS = {
35
- ...allVolatile(Object.fromEntries([...STATUS_FIELDS, ...HEALTH_FIELDS_SCHEMA_KEYS].map(key => [
36
- key,
37
- z.string().default(''),
38
- ]))),
39
- [WORKSPACE_TABS_FIELD]: WORKSPACE_TABS_SCHEMA.volatile(),
40
- };
@@ -1,24 +0,0 @@
1
- import type { RuntimeBootFacts } from '../api/index.ts';
2
- import type { LoadRun } from '../domains/types.ts';
3
- import type { Semi2ConfigState } from '../hub/facts.ts';
4
- import type { OrchestratorInternals } from './orchestrator.ts';
5
- /** 发布结果:`headless` = 本端没有读者(不发),供端的有界重判使用。 */
6
- export type PublishOutcome = 'applied' | 'failed' | 'headless';
7
- /**
8
- * 状态 + 健康 + 工作区 tab 一次发布:整份写进本插件设置段的运行期 config。
9
- * 盘上组合层由 `applyStatusRow` 原样带上,消费方写在同一行里的调参不会被冲掉。
10
- */
11
- export declare function publishStatus(engine: OrchestratorInternals): Promise<PublishOutcome>;
12
- /**
13
- * 旧版本把状态段写进过 profile patch:启动后按名字认领一次,只撤下本插件写过的展示键,
14
- * 消费方设在同一行里的调参原样留着。这是本插件唯一的落盘写;失败只 warn,下轮再试。
15
- */
16
- export declare function dropLegacyStatusRow(engine: OrchestratorInternals): Promise<void>;
17
- /** 领域在会话期产生新状态时请求一次发布:fire-and-forget,失败只 warn,不影响会话。 */
18
- export declare function scheduleStatusPublish(engine: OrchestratorInternals): void;
19
- /** 整体读取失败:只落 state.error 与失败态,不注入任何配置(可重载重试)。 */
20
- export declare function publishFailed(engine: OrchestratorInternals, code: string): Promise<void>;
21
- /** boot 事实 → ready 状态(技能切片取自 skills 领域;项目缓存随装配作废,由工作区同步重取)。 */
22
- export declare function appliedState(engine: OrchestratorInternals, boot: RuntimeBootFacts, compiled: LoadRun['compiled'], errorCode: string | null): Semi2ConfigState;
23
- /** 健康快照:领域项按注册表(缺席记 unavailable)+ 能力贡献者按登记。 */
24
- export declare function healthSnapshot(engine: OrchestratorInternals): import("./health.ts").HealthSnapshot;
@@ -1,7 +0,0 @@
1
- import z from '@deepseek-ai/schemastery';
2
- /** 完整状态字段顺序(基座 + 按注册表顺序的领域字段);UI 与健康字段共用这一段。 */
3
- export declare const STATUS_FIELDS: readonly string[];
4
- /** 健康字段 schema 键(能力 + 领域,值是 `ok:正常` 形式的编码串;读侧拆码)。 */
5
- export declare const HEALTH_FIELDS_SCHEMA_KEYS: readonly string[];
6
- /** 插件 Config 的可写字段表(并入入口的 Config schema);写入只走 volatile 路径。 */
7
- export declare const STATE_CONFIG_FIELDS: Record<string, z>;