@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.
- package/README.md +3 -3
- package/lib/client.js +107 -32
- package/lib/client.js.map +1 -1
- package/lib/contract.js +0 -5
- package/lib/domains/project-profile/domain.js +0 -1
- package/lib/hub/status.js +1 -1
- package/lib/index.js +2 -8
- package/lib/service/config-service.js +2 -22
- package/lib/service/lifecycle.js +6 -28
- package/lib/service/namespaces.js +3 -9
- package/lib/service/orchestrator.js +0 -4
- package/lib/service/ports.js +0 -30
- package/lib/service/registry.js +11 -0
- package/lib/service/status.js +89 -0
- package/lib/types/contract.d.ts +0 -5
- package/lib/types/domains/project-profile/domain.d.ts +1 -1
- package/lib/types/domains/types.d.ts +1 -3
- package/lib/types/index.d.ts +1 -1
- package/lib/types/service/config-service.d.ts +3 -1
- package/lib/types/service/lifecycle.d.ts +1 -1
- package/lib/types/service/namespaces.d.ts +3 -5
- package/lib/types/service/orchestrator.d.ts +0 -2
- package/lib/types/service/ports.d.ts +1 -27
- package/lib/types/service/registry.d.ts +4 -0
- package/lib/types/service/status.d.ts +17 -0
- package/lib/types/web/routes.d.ts +4 -2
- package/lib/web/routes.js +8 -4
- package/package.json +1 -1
- package/lib/service/publish.js +0 -184
- package/lib/service/settings-sections.js +0 -40
- package/lib/types/service/publish.d.ts +0 -24
- package/lib/types/service/settings-sections.d.ts +0 -7
package/README.md
CHANGED
|
@@ -23,9 +23,9 @@ Redmine 走另一条路:地址与密钥都来自 hub,插件按 hub 下发的
|
|
|
23
23
|
| `credentialRetryIntervalMs` | 150 | 凭据重试间隔(ms) |
|
|
24
24
|
| `webFenceTimeoutMs` | 5000 | `webServer` 在而校验接口迟迟不出现时补一条诊断日志的上限 |
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
启动时按 dsh
|
|
28
|
-
|
|
26
|
+
状态与工作区映射都不落盘:状态由 `GET /semi2/status` 按请求现算(浏览器半边拉取),工作区映射只在进程内,
|
|
27
|
+
启动时按 dsh 工作区基线重新登记。本端无网页服务(NPC 一次性容器)时不注册状态路由。
|
|
28
|
+
设置写入只剩带着自铸名字的路由与密钥引用的维护。
|
|
29
29
|
|
|
30
30
|
## 提供的服务与事件
|
|
31
31
|
|
package/lib/client.js
CHANGED
|
@@ -8,43 +8,40 @@ window.__ModuleLoader__.load({
|
|
|
8
8
|
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
9
9
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
10
10
|
//#region src/contract.ts
|
|
11
|
-
/**
|
|
12
|
-
* 插件自身设置段的条目 id:dsh 的设置命名空间就是 Profile 插件条目 id(bundle patch 里那一行的 id),
|
|
13
|
-
* 状态 / 健康 / 工作区映射都写在这一段里。host 与浏览器半边共用这一个常量。
|
|
14
|
-
*/
|
|
15
|
-
const SEMI2_SETTINGS_ENTRY = "semi2-dsh";
|
|
16
11
|
/** 动作通道前缀:登录 / 登出 / 同步都经官方 connection 的逻辑 RPC,一次调用落在 POST <前缀>/<endpoint>。 */
|
|
17
12
|
const SEMI2_ACTION_CHANNEL = "/semi2";
|
|
18
13
|
//#endregion
|
|
19
14
|
//#region src/client/projection.ts
|
|
20
|
-
/**
|
|
21
|
-
function emptySnapshot(
|
|
15
|
+
/** 空快照:还没拿到过任何数据(页面据此停在登录态 / 错误卡)。 */
|
|
16
|
+
function emptySnapshot(frame) {
|
|
22
17
|
return {
|
|
23
18
|
fields: {},
|
|
24
19
|
tabs: [],
|
|
25
|
-
writable:
|
|
26
|
-
revision: snapshot.revision,
|
|
20
|
+
writable: frame.writable === true,
|
|
27
21
|
connected: false
|
|
28
22
|
};
|
|
29
23
|
}
|
|
30
|
-
/**
|
|
31
|
-
function projectSnapshot(
|
|
32
|
-
const value =
|
|
33
|
-
if (value === void 0 || typeof value.status !== "string" || value.status === "") return previous === void 0 ? emptySnapshot(
|
|
24
|
+
/** 投影一帧状态应答;空值沿用上一轮,其余按 `status` 决定是否进控制台。 */
|
|
25
|
+
function projectSnapshot(frame, previous) {
|
|
26
|
+
const value = frame.section;
|
|
27
|
+
if (value === void 0 || typeof value.status !== "string" || value.status === "") return previous === void 0 ? emptySnapshot(frame) : {
|
|
34
28
|
...previous,
|
|
35
|
-
|
|
29
|
+
writable: frame.writable === true
|
|
36
30
|
};
|
|
37
31
|
return {
|
|
38
32
|
fields: value,
|
|
39
33
|
tabs: Array.isArray(value.workspaceTabs) ? value.workspaceTabs : [],
|
|
40
|
-
writable:
|
|
41
|
-
revision: snapshot.revision,
|
|
34
|
+
writable: frame.writable === true,
|
|
42
35
|
connected: value.status === "ready"
|
|
43
36
|
};
|
|
44
37
|
}
|
|
45
38
|
//#endregion
|
|
46
39
|
//#region src/client/controller.ts
|
|
47
40
|
const OVERVIEW_KEY = "overview";
|
|
41
|
+
/** 状态路由:唯一的状态来源。 */
|
|
42
|
+
const STATUS_PATH = "/semi2/status";
|
|
43
|
+
/** 页面可见时的慢心跳:状态页不是仪表盘,30s 足够接住会话期事实(项目画像等)。 */
|
|
44
|
+
const REFRESH_INTERVAL_MS = 3e4;
|
|
48
45
|
/** 运行平台原值(数据层只存 cnb / host)→ 展示标签(本地化)。 */
|
|
49
46
|
function platformText(t, platform) {
|
|
50
47
|
if (typeof platform !== "string" || platform === "") return "—";
|
|
@@ -117,24 +114,42 @@ window.__ModuleLoader__.load({
|
|
|
117
114
|
}
|
|
118
115
|
var Semi2Controller = class {
|
|
119
116
|
ctx;
|
|
120
|
-
scope;
|
|
121
117
|
listeners = /* @__PURE__ */ new Set();
|
|
122
|
-
|
|
118
|
+
disposers = [];
|
|
119
|
+
doFetch;
|
|
123
120
|
projected;
|
|
121
|
+
view = "loading";
|
|
124
122
|
loginBusy = false;
|
|
125
123
|
logoutBusy = false;
|
|
126
124
|
actionError = null;
|
|
127
125
|
constructor(ctx) {
|
|
128
126
|
this.ctx = ctx;
|
|
129
|
-
this.
|
|
130
|
-
this.projected = projectSnapshot(
|
|
131
|
-
|
|
132
|
-
this.
|
|
133
|
-
for (const listener of this.listeners) listener();
|
|
127
|
+
this.doFetch = globalThis.fetch.bind(globalThis);
|
|
128
|
+
this.projected = projectSnapshot({}, void 0);
|
|
129
|
+
const offReset = ctx.on?.("connection/reset", () => {
|
|
130
|
+
this.refresh();
|
|
134
131
|
});
|
|
132
|
+
if (offReset !== void 0) this.disposers.push(() => {
|
|
133
|
+
offReset();
|
|
134
|
+
});
|
|
135
|
+
const state = ctx.connection?.state;
|
|
136
|
+
if (state !== void 0) this.disposers.push(state.subscribe(() => {
|
|
137
|
+
if (state.getSnapshot() === "connected") this.refresh();
|
|
138
|
+
}));
|
|
139
|
+
const browser = globalThis;
|
|
140
|
+
if (browser.document !== void 0) {
|
|
141
|
+
const timer = setInterval(() => {
|
|
142
|
+
if (browser.document?.visibilityState !== "hidden") this.refresh();
|
|
143
|
+
}, REFRESH_INTERVAL_MS);
|
|
144
|
+
this.disposers.push(() => {
|
|
145
|
+
clearInterval(timer);
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
this.refresh();
|
|
135
149
|
}
|
|
136
150
|
dispose() {
|
|
137
|
-
this.
|
|
151
|
+
for (const dispose of this.disposers) dispose();
|
|
152
|
+
this.disposers.length = 0;
|
|
138
153
|
this.listeners.clear();
|
|
139
154
|
}
|
|
140
155
|
subscribe(listener) {
|
|
@@ -146,6 +161,7 @@ window.__ModuleLoader__.load({
|
|
|
146
161
|
getSnapshot() {
|
|
147
162
|
return {
|
|
148
163
|
projected: this.projected,
|
|
164
|
+
view: this.view,
|
|
149
165
|
loginBusy: this.loginBusy,
|
|
150
166
|
logoutBusy: this.logoutBusy,
|
|
151
167
|
actionError: this.actionError
|
|
@@ -154,7 +170,25 @@ window.__ModuleLoader__.load({
|
|
|
154
170
|
notify() {
|
|
155
171
|
for (const listener of this.listeners) listener();
|
|
156
172
|
}
|
|
157
|
-
/**
|
|
173
|
+
/** 拉一帧状态:失败只落 `view`(从未拿到过数据才显示错误卡),拿到过数据就沿用旧值。 */
|
|
174
|
+
async refresh() {
|
|
175
|
+
try {
|
|
176
|
+
const response = await this.doFetch(STATUS_PATH, { headers: { accept: "application/json" } });
|
|
177
|
+
if (!response.ok) throw new Error(`HTTP ${String(response.status)}`);
|
|
178
|
+
const body = await response.json();
|
|
179
|
+
if (body === null || typeof body !== "object" || Array.isArray(body)) throw new Error("invalid payload");
|
|
180
|
+
const section = body;
|
|
181
|
+
this.projected = projectSnapshot({
|
|
182
|
+
section,
|
|
183
|
+
writable: section.writable === true
|
|
184
|
+
}, this.projected);
|
|
185
|
+
this.view = "data";
|
|
186
|
+
} catch {
|
|
187
|
+
if (this.view !== "data") this.view = "error";
|
|
188
|
+
}
|
|
189
|
+
this.notify();
|
|
190
|
+
}
|
|
191
|
+
/** 登录:草稿只在提交时发出;成功清空密码,失败保留草稿并报错;动作后重拉状态。 */
|
|
158
192
|
async login(draft) {
|
|
159
193
|
if (this.loginBusy) return;
|
|
160
194
|
this.loginBusy = true;
|
|
@@ -164,6 +198,7 @@ window.__ModuleLoader__.load({
|
|
|
164
198
|
this.loginBusy = false;
|
|
165
199
|
if (!outcome.ok) this.actionError = outcome.code;
|
|
166
200
|
this.notify();
|
|
201
|
+
await this.refresh();
|
|
167
202
|
return outcome;
|
|
168
203
|
}
|
|
169
204
|
async logout() {
|
|
@@ -175,6 +210,7 @@ window.__ModuleLoader__.load({
|
|
|
175
210
|
this.logoutBusy = false;
|
|
176
211
|
if (!outcome.ok) this.actionError = outcome.code;
|
|
177
212
|
this.notify();
|
|
213
|
+
await this.refresh();
|
|
178
214
|
return outcome;
|
|
179
215
|
}
|
|
180
216
|
/**
|
|
@@ -188,6 +224,7 @@ window.__ModuleLoader__.load({
|
|
|
188
224
|
this.actionError = outcome.code;
|
|
189
225
|
this.notify();
|
|
190
226
|
}
|
|
227
|
+
await this.refresh();
|
|
191
228
|
}
|
|
192
229
|
};
|
|
193
230
|
/** useSyncExternalStore 适配(控制器快照是稳定引用,动作后手动 bump)。 */
|
|
@@ -562,10 +599,40 @@ window.__ModuleLoader__.load({
|
|
|
562
599
|
]
|
|
563
600
|
});
|
|
564
601
|
}
|
|
565
|
-
|
|
602
|
+
/** 从未拿到过数据且拉取失败:错误卡 + 手动重试(不伪装成登录表单)。 */
|
|
603
|
+
function ErrorCard({ t, controller }) {
|
|
604
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
605
|
+
className: "semi2-card",
|
|
606
|
+
children: [
|
|
607
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
608
|
+
className: "semi2-cardTitle",
|
|
609
|
+
children: t("statusUnavailableTitle")
|
|
610
|
+
}),
|
|
611
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
612
|
+
className: "semi2-hint",
|
|
613
|
+
children: t("statusUnavailableHint")
|
|
614
|
+
}),
|
|
615
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
616
|
+
className: "semi2-formActions",
|
|
617
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
618
|
+
variant: "primary",
|
|
619
|
+
onClick: () => {
|
|
620
|
+
controller.refresh();
|
|
621
|
+
},
|
|
622
|
+
children: t("retry")
|
|
623
|
+
})
|
|
624
|
+
})
|
|
625
|
+
]
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
function OverviewTab({ t, projected, controller, actionError, view }) {
|
|
566
629
|
const { fields } = projected;
|
|
567
630
|
const [diagnosticsOpen, setDiagnosticsOpen] = (0, react.useState)(false);
|
|
568
631
|
const { showLogin } = connectionMode(projected);
|
|
632
|
+
if (view === "error") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ErrorCard, {
|
|
633
|
+
t,
|
|
634
|
+
controller
|
|
635
|
+
});
|
|
569
636
|
if (showLogin) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LoginCard, {
|
|
570
637
|
t,
|
|
571
638
|
projected,
|
|
@@ -683,7 +750,8 @@ window.__ModuleLoader__.load({
|
|
|
683
750
|
*/
|
|
684
751
|
function Semi2Page(props) {
|
|
685
752
|
const { t, controller, renderSlot } = props;
|
|
686
|
-
const
|
|
753
|
+
const state = useController(controller);
|
|
754
|
+
const { projected } = state;
|
|
687
755
|
const [active, setActive] = (0, react.useState)(OVERVIEW_KEY);
|
|
688
756
|
const tabs = (0, react.useMemo)(() => projected.tabs.filter((tab) => tab.title !== ""), [projected.tabs]);
|
|
689
757
|
const keys = (0, react.useMemo)(() => [OVERVIEW_KEY, ...tabs.map(tabKey)], [tabs]);
|
|
@@ -741,7 +809,8 @@ window.__ModuleLoader__.load({
|
|
|
741
809
|
t,
|
|
742
810
|
projected,
|
|
743
811
|
controller,
|
|
744
|
-
actionError: showLogin ? actionError : void 0
|
|
812
|
+
actionError: showLogin ? actionError : void 0,
|
|
813
|
+
view: state.view
|
|
745
814
|
}) : renderSlot !== void 0 ? renderSlot("semi2.workspace", { view: "page" }, { entryKey: active }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(WorkspaceTab, {
|
|
746
815
|
t,
|
|
747
816
|
controller,
|
|
@@ -798,6 +867,9 @@ window.__ModuleLoader__.load({
|
|
|
798
867
|
workspaceSyncedAt: "同步时间",
|
|
799
868
|
workspaceUnresolved: "未解析(不归 semi2-hub 管)",
|
|
800
869
|
workspaceNone: "没有已登记的工作区",
|
|
870
|
+
statusUnavailableTitle: "状态暂不可用",
|
|
871
|
+
statusUnavailableHint: "取不到宿主状态(宿主插件未就绪或版本不匹配),可重试",
|
|
872
|
+
retry: "重试",
|
|
801
873
|
domainModels: "模型",
|
|
802
874
|
domainSecrets: "模型密钥",
|
|
803
875
|
domainSkills: "技能",
|
|
@@ -867,6 +939,9 @@ window.__ModuleLoader__.load({
|
|
|
867
939
|
workspaceSyncedAt: "Synced at",
|
|
868
940
|
workspaceUnresolved: "Unresolved (not managed by semi2-hub)",
|
|
869
941
|
workspaceNone: "No registered workspaces",
|
|
942
|
+
statusUnavailableTitle: "Status unavailable",
|
|
943
|
+
statusUnavailableHint: "Cannot read host status (host plugin not ready or version mismatch); retry",
|
|
944
|
+
retry: "Retry",
|
|
870
945
|
domainModels: "Models",
|
|
871
946
|
domainSecrets: "Model keys",
|
|
872
947
|
domainSkills: "Skills",
|
|
@@ -989,7 +1064,6 @@ window.__ModuleLoader__.load({
|
|
|
989
1064
|
const PANEL_ID = "semi2";
|
|
990
1065
|
const inject = [
|
|
991
1066
|
"slots",
|
|
992
|
-
"configForms",
|
|
993
1067
|
"connection",
|
|
994
1068
|
"locale"
|
|
995
1069
|
];
|
|
@@ -1031,9 +1105,9 @@ window.__ModuleLoader__.load({
|
|
|
1031
1105
|
workspaceEntries.set(key, dispose);
|
|
1032
1106
|
}
|
|
1033
1107
|
};
|
|
1034
|
-
ctx.effect(() =>
|
|
1035
|
-
const registered = /* @__PURE__ */ new Map();
|
|
1108
|
+
ctx.effect(() => {
|
|
1036
1109
|
syncWorkspaceEntries();
|
|
1110
|
+
const registered = /* @__PURE__ */ new Map();
|
|
1037
1111
|
registered.set("tabs", controller.subscribe(syncWorkspaceEntries));
|
|
1038
1112
|
registered.set("panel", ctx.slots.inject("main", () => ctx.slots.register({
|
|
1039
1113
|
name: "main",
|
|
@@ -1058,10 +1132,11 @@ window.__ModuleLoader__.load({
|
|
|
1058
1132
|
for (const dispose of workspaceEntries.values()) dispose();
|
|
1059
1133
|
workspaceEntries.clear();
|
|
1060
1134
|
};
|
|
1061
|
-
}
|
|
1135
|
+
}, "semi2: sidebar entry + main page");
|
|
1062
1136
|
}
|
|
1063
1137
|
//#endregion
|
|
1064
1138
|
exports.PANEL_ID = PANEL_ID;
|
|
1139
|
+
exports.Semi2Controller = Semi2Controller;
|
|
1065
1140
|
exports.apply = apply;
|
|
1066
1141
|
exports.inject = inject;
|
|
1067
1142
|
return module.exports;
|