@lmzhen/dsh-evolution-settings-ui 0.26.26 → 0.27.1
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/client.js +355 -76
- package/lib/types/client/DiagnosticsCard.d.ts +26 -0
- package/lib/types/client/ParamCard.d.ts +14 -0
- package/lib/types/client/PendingCard.d.ts +3 -0
- package/lib/types/client/api.d.ts +3 -8
- package/lib/types/client/contracts.generated.d.ts +37 -0
- package/lib/types/client/diagnostics-state.d.ts +28 -0
- package/lib/types/client/fallbacks.generated.d.ts +8 -0
- package/lib/types/client/messages.d.ts +21 -2
- package/lib/types/client/pending-state.d.ts +5 -4
- package/lib/types/client/route-client.generated.d.ts +45 -0
- package/lib/types/client/routes.generated.d.ts +31 -0
- package/lib/types/client/seam.d.ts +3 -4
- package/lib/types/client/states.generated.d.ts +43 -0
- package/package.json +5 -2
- package/lib/types/client/approval-routes.d.ts +0 -17
package/lib/client.js
CHANGED
|
@@ -5,73 +5,138 @@ window.__ModuleLoader__.load({
|
|
|
5
5
|
var exports = module.exports;
|
|
6
6
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
7
|
let react = require("react");
|
|
8
|
-
//#region src/client/
|
|
8
|
+
//#region src/client/routes.generated.ts
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* them against `APPROVAL_ROUTES` in `evolution-approval` — a rename on one side would otherwise surface
|
|
15
|
-
* as a 404 in the GUI instead of in CI.
|
|
16
|
-
* @module @lmzhen/dsh-evolution-settings-ui/client/approval-routes
|
|
10
|
+
* Every family host path, generated from evolution-core's HOST_ROUTES (the ONE declaration).
|
|
11
|
+
* A browser half cannot import the host package at runtime (client bundles keep @deepseek-ai/*
|
|
12
|
+
* external), so the table is mirrored here mechanically; a rename in core regenerates all three
|
|
13
|
+
* client copies, and the generator's --check makes a stale copy red.
|
|
17
14
|
*/
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
15
|
+
const ROUTES = {
|
|
16
|
+
approval: {
|
|
17
|
+
pending: "/api/dsh-evolution/approval/pending",
|
|
18
|
+
preview: "/api/dsh-evolution/approval/preview",
|
|
19
|
+
approve: "/api/dsh-evolution/approval/approve",
|
|
20
|
+
reject: "/api/dsh-evolution/approval/reject"
|
|
21
|
+
},
|
|
22
|
+
"skill-history": {
|
|
23
|
+
skills: "/api/dsh-evolution/skill-history/skills",
|
|
24
|
+
versions: "/api/dsh-evolution/skill-history/versions",
|
|
25
|
+
diff: "/api/dsh-evolution/skill-history/versions/diff",
|
|
26
|
+
body: "/api/dsh-evolution/skill-history/versions/body",
|
|
27
|
+
undo: "/api/dsh-evolution/skill-history/undo",
|
|
28
|
+
health: "/api/dsh-evolution/skill-history/health"
|
|
29
|
+
},
|
|
30
|
+
"skill-health": { rows: "/api/dsh-evolution/skill-health/rows" },
|
|
31
|
+
diagnostics: { view: "/api/dsh-evolution/diagnostics" }
|
|
24
32
|
};
|
|
25
33
|
//#endregion
|
|
26
|
-
//#region src/client/
|
|
34
|
+
//#region src/client/route-client.generated.ts
|
|
35
|
+
/** Classify a non-2xx status: 401/403/404 keep their own kind, everything else is a server answer. */
|
|
36
|
+
function classify(status) {
|
|
37
|
+
if (status === 401) return {
|
|
38
|
+
kind: "unauthenticated",
|
|
39
|
+
status: 401
|
|
40
|
+
};
|
|
41
|
+
if (status === 403) return {
|
|
42
|
+
kind: "forbidden",
|
|
43
|
+
status: 403
|
|
44
|
+
};
|
|
45
|
+
if (status === 404) return {
|
|
46
|
+
kind: "unmounted",
|
|
47
|
+
status: 404
|
|
48
|
+
};
|
|
49
|
+
return {
|
|
50
|
+
kind: "server",
|
|
51
|
+
status
|
|
52
|
+
};
|
|
53
|
+
}
|
|
27
54
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* @module @lmzhen/dsh-evolution-settings-ui/client/api
|
|
55
|
+
* One request, no retries and no body parsing for the platform's admission answers.
|
|
56
|
+
* @param doFetch - the fetch in use (the browser's, or a spec's stub).
|
|
57
|
+
* @param path - the exact route path from the generated ROUTES table.
|
|
58
|
+
* @param init - standard RequestInit.
|
|
59
|
+
* @returns the data, or the classified failure (network/parse failures read as 'server').
|
|
34
60
|
*/
|
|
35
|
-
|
|
36
|
-
|
|
61
|
+
async function requestJson(doFetch, path, init) {
|
|
62
|
+
let response;
|
|
37
63
|
try {
|
|
38
|
-
|
|
39
|
-
|
|
64
|
+
response = await doFetch(path, init);
|
|
65
|
+
} catch (error) {
|
|
66
|
+
return {
|
|
40
67
|
ok: false,
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
data: body.data
|
|
68
|
+
failure: {
|
|
69
|
+
kind: "server",
|
|
70
|
+
status: 0,
|
|
71
|
+
detail: error instanceof Error ? error.message : String(error)
|
|
72
|
+
}
|
|
47
73
|
};
|
|
74
|
+
}
|
|
75
|
+
if (!response.ok) return {
|
|
76
|
+
ok: false,
|
|
77
|
+
failure: classify(response.status)
|
|
78
|
+
};
|
|
79
|
+
let body;
|
|
80
|
+
try {
|
|
81
|
+
body = await response.json();
|
|
82
|
+
} catch {
|
|
48
83
|
return {
|
|
49
84
|
ok: false,
|
|
50
|
-
|
|
85
|
+
failure: {
|
|
86
|
+
kind: "server",
|
|
87
|
+
status: response.status
|
|
88
|
+
}
|
|
51
89
|
};
|
|
52
|
-
}
|
|
53
|
-
|
|
90
|
+
}
|
|
91
|
+
if (typeof body === "object" && body !== null) {
|
|
92
|
+
const record = body;
|
|
93
|
+
if (record.ok === true) return {
|
|
94
|
+
ok: true,
|
|
95
|
+
data: record.data
|
|
96
|
+
};
|
|
97
|
+
if (record.ok === false && typeof record.message === "string" && record.message !== "") return {
|
|
54
98
|
ok: false,
|
|
55
|
-
|
|
99
|
+
failure: {
|
|
100
|
+
kind: "refused",
|
|
101
|
+
code: typeof record.code === "string" && record.code !== "" ? record.code : "refused",
|
|
102
|
+
message: record.message
|
|
103
|
+
}
|
|
56
104
|
};
|
|
57
105
|
}
|
|
106
|
+
return {
|
|
107
|
+
ok: false,
|
|
108
|
+
failure: {
|
|
109
|
+
kind: "server",
|
|
110
|
+
status: response.status
|
|
111
|
+
}
|
|
112
|
+
};
|
|
58
113
|
}
|
|
114
|
+
//#endregion
|
|
115
|
+
//#region src/client/api.ts
|
|
116
|
+
/**
|
|
117
|
+
* The approval routes this bundle calls, and the ONE place a request is made.
|
|
118
|
+
*
|
|
119
|
+
* Structural types on purpose: the browser half cannot import the host package at runtime, so the row
|
|
120
|
+
* shape is declared by what the route hands over. Every answer — including a refusal — is parsed the
|
|
121
|
+
* same way, because a refusal is a business answer the card shows as a sentence, not a transport error.
|
|
122
|
+
* @module @lmzhen/dsh-evolution-settings-ui/client/api
|
|
123
|
+
*/
|
|
59
124
|
/**
|
|
60
125
|
* Build the approval face over one fetch implementation.
|
|
61
126
|
* @param doFetch - the fetch to use (the platform's global in the browser, a fake in a spec).
|
|
62
127
|
* @returns the three calls the pending card makes.
|
|
63
128
|
*/
|
|
64
129
|
function createApprovalApi(doFetch) {
|
|
65
|
-
const post = (path, id) =>
|
|
130
|
+
const post = async (path, id) => await requestJson(doFetch, path, {
|
|
66
131
|
method: "POST",
|
|
67
132
|
headers: { "content-type": "application/json" },
|
|
68
133
|
body: JSON.stringify({ id })
|
|
69
134
|
});
|
|
70
135
|
return {
|
|
71
|
-
pending: async (status) => await
|
|
72
|
-
approve: async (id) => await post(
|
|
73
|
-
reject: async (id) => await post(
|
|
74
|
-
preview: async (id) => await
|
|
136
|
+
pending: async (status) => await requestJson(doFetch, status === void 0 ? ROUTES.approval.pending : ROUTES.approval.pending + "?status=" + status),
|
|
137
|
+
approve: async (id) => await post(ROUTES.approval.approve, id),
|
|
138
|
+
reject: async (id) => await post(ROUTES.approval.reject, id),
|
|
139
|
+
preview: async (id) => await requestJson(doFetch, ROUTES.approval.preview + "?id=" + encodeURIComponent(id))
|
|
75
140
|
};
|
|
76
141
|
}
|
|
77
142
|
//#endregion
|
|
@@ -607,6 +672,35 @@ window.__ModuleLoader__.load({
|
|
|
607
672
|
}
|
|
608
673
|
];
|
|
609
674
|
//#endregion
|
|
675
|
+
//#region src/client/states.generated.ts
|
|
676
|
+
/** A neutral/ warn/ fail block; a fail block is role=alert so assistive tech announces it. */
|
|
677
|
+
function Block(props) {
|
|
678
|
+
const tone = props.tone ?? "neutral";
|
|
679
|
+
return (0, react.createElement)("div", {
|
|
680
|
+
"data-tone": tone,
|
|
681
|
+
...tone === "fail" ? { role: "alert" } : {}
|
|
682
|
+
}, props.title === void 0 ? null : (0, react.createElement)("div", { "data-tone-title": tone }, props.title), props.children);
|
|
683
|
+
}
|
|
684
|
+
/** One classified failure as a reader-facing sentence. */
|
|
685
|
+
function failureText(failure, t) {
|
|
686
|
+
switch (failure.kind) {
|
|
687
|
+
case "unauthenticated": return t("failureUnauthorized");
|
|
688
|
+
case "forbidden": return t("failureForbidden");
|
|
689
|
+
case "unmounted": return t("failureUnmounted");
|
|
690
|
+
case "server": return failure.status === 0 ? t("failureServer") : t("failureServer") + " (HTTP " + String(failure.status) + ")";
|
|
691
|
+
case "refused": return failure.message;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
/** The failed-state block: sentence + (when the surface exists but is unreachable) the command fallback. */
|
|
695
|
+
function FailureBlock(props) {
|
|
696
|
+
const hint = props.fallback === void 0 ? null : (0, react.createElement)("div", { "data-tone-hint": "true" }, props.t("fallbackHint", { command: props.fallback.command }));
|
|
697
|
+
return Block({
|
|
698
|
+
tone: "fail",
|
|
699
|
+
title: failureText(props.failure, props.t),
|
|
700
|
+
children: hint
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
//#endregion
|
|
610
704
|
//#region src/client/pending-state.ts
|
|
611
705
|
/** The state a card mounts in: still loading, nothing in flight, nothing said yet. */
|
|
612
706
|
const INITIAL_PENDING_STATE = {
|
|
@@ -662,13 +756,13 @@ window.__ModuleLoader__.load({
|
|
|
662
756
|
}
|
|
663
757
|
};
|
|
664
758
|
}
|
|
665
|
-
/** The list could not be read: the
|
|
666
|
-
function failed(state,
|
|
759
|
+
/** The list could not be read: the classified failure stays on screen next to a retry control. */
|
|
760
|
+
function failed(state, failure) {
|
|
667
761
|
return {
|
|
668
762
|
...state,
|
|
669
763
|
view: {
|
|
670
764
|
kind: "failed",
|
|
671
|
-
|
|
765
|
+
failure
|
|
672
766
|
}
|
|
673
767
|
};
|
|
674
768
|
}
|
|
@@ -741,6 +835,7 @@ window.__ModuleLoader__.load({
|
|
|
741
835
|
*/
|
|
742
836
|
function PendingCard(props) {
|
|
743
837
|
const { t, api } = props;
|
|
838
|
+
const translate = (key, values) => t(key, values);
|
|
744
839
|
const confirmMode = props.confirmMode?.() ?? "auto";
|
|
745
840
|
const [armed, setArmed] = (0, react.useState)(null);
|
|
746
841
|
const [state, setState] = (0, react.useState)(INITIAL_PENDING_STATE);
|
|
@@ -768,11 +863,11 @@ window.__ModuleLoader__.load({
|
|
|
768
863
|
const [waiting, running] = await Promise.all([api.pending(), api.pending("executing")]);
|
|
769
864
|
if (readTicket.current !== ticket) return;
|
|
770
865
|
if (!waiting.ok) {
|
|
771
|
-
apply((current) => failed(current, waiting.
|
|
866
|
+
apply((current) => failed(current, waiting.failure));
|
|
772
867
|
return;
|
|
773
868
|
}
|
|
774
869
|
if (!running.ok) {
|
|
775
|
-
apply((current) => failed(current, running.
|
|
870
|
+
apply((current) => failed(current, running.failure));
|
|
776
871
|
return;
|
|
777
872
|
}
|
|
778
873
|
apply((current) => loaded(current, [...waiting.data, ...running.data]));
|
|
@@ -792,7 +887,7 @@ window.__ModuleLoader__.load({
|
|
|
792
887
|
(async () => {
|
|
793
888
|
const answer = action === "approve" ? await api.approve(id) : await api.reject(id);
|
|
794
889
|
if (!answer.ok) {
|
|
795
|
-
apply((current) => noticed(endAction(current, id), answer.
|
|
890
|
+
apply((current) => noticed(endAction(current, id), failureText(answer.failure, translate)));
|
|
796
891
|
return;
|
|
797
892
|
}
|
|
798
893
|
apply((current) => noticed(endAction(current, id), null));
|
|
@@ -815,7 +910,7 @@ window.__ModuleLoader__.load({
|
|
|
815
910
|
reason: answer.data.reason
|
|
816
911
|
} : {
|
|
817
912
|
kind: "failed",
|
|
818
|
-
|
|
913
|
+
failure: answer.failure
|
|
819
914
|
};
|
|
820
915
|
apply((current) => previewArrived(current, id, view));
|
|
821
916
|
};
|
|
@@ -836,7 +931,7 @@ window.__ModuleLoader__.load({
|
|
|
836
931
|
if (view.kind === "failed") return (0, react.createElement)("p", {
|
|
837
932
|
className: "evolution-param-error",
|
|
838
933
|
role: "status"
|
|
839
|
-
}, view.
|
|
934
|
+
}, failureText(view.failure, translate));
|
|
840
935
|
if (view.kind === "unavailable") return (0, react.createElement)("p", {
|
|
841
936
|
className: "evolution-param-error",
|
|
842
937
|
role: "status"
|
|
@@ -937,10 +1032,11 @@ window.__ModuleLoader__.load({
|
|
|
937
1032
|
className: "evolution-param-note",
|
|
938
1033
|
role: "status"
|
|
939
1034
|
}, t("approvalEmpty"));
|
|
940
|
-
if (state.view.kind === "failed") return (0, react.createElement)("div", { className: "evolution-param-actions" }, (
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
1035
|
+
if (state.view.kind === "failed") return (0, react.createElement)("div", { className: "evolution-param-actions" }, FailureBlock({
|
|
1036
|
+
failure: state.view.failure,
|
|
1037
|
+
t: translate,
|
|
1038
|
+
fallback: props.fallback
|
|
1039
|
+
}), (0, react.createElement)("button", {
|
|
944
1040
|
className: "evolution-param-button",
|
|
945
1041
|
type: "button",
|
|
946
1042
|
onClick: () => {
|
|
@@ -971,6 +1067,120 @@ window.__ModuleLoader__.load({
|
|
|
971
1067
|
}, t("approvalRefresh"))), body()) : null);
|
|
972
1068
|
}
|
|
973
1069
|
//#endregion
|
|
1070
|
+
//#region src/client/DiagnosticsCard.ts
|
|
1071
|
+
/**
|
|
1072
|
+
* The diagnostics card (W7 S7): the browser face of the SAME DiagnosticsView the CLI and the
|
|
1073
|
+
* `/api/dsh-evolution/diagnostics` route answer.
|
|
1074
|
+
*
|
|
1075
|
+
* Read-only by construction: it fetches once on mount and only refetches on an explicit Refresh; a
|
|
1076
|
+
* failed read renders the shared FailureBlock with the command-face fallback. Facts and runtime rows
|
|
1077
|
+
* are rendered in the view's own order — the client owns no key list and no status vocabulary.
|
|
1078
|
+
* @module @lmzhen/dsh-evolution-settings-ui/client/DiagnosticsCard
|
|
1079
|
+
*/
|
|
1080
|
+
/** Which word one status gets (the frozen W1 status set). */
|
|
1081
|
+
const STATUS_KEYS = Object.freeze({
|
|
1082
|
+
ok: "diagStatusOk",
|
|
1083
|
+
warn: "diagStatusWarn",
|
|
1084
|
+
fail: "diagStatusFail",
|
|
1085
|
+
unknown: "diagStatusUnknown"
|
|
1086
|
+
});
|
|
1087
|
+
/**
|
|
1088
|
+
* Render the card.
|
|
1089
|
+
* @param props - the copy seat, the host call and the command fallback.
|
|
1090
|
+
* @returns the card.
|
|
1091
|
+
*/
|
|
1092
|
+
function DiagnosticsCard(props) {
|
|
1093
|
+
const { t, api } = props;
|
|
1094
|
+
const translate = (key, values) => t(key, values);
|
|
1095
|
+
const [state, setState] = (0, react.useState)({ phase: "loading" });
|
|
1096
|
+
const ticket = (0, react.useRef)(0);
|
|
1097
|
+
const read = (keepView) => {
|
|
1098
|
+
const current = ticket.current + 1;
|
|
1099
|
+
ticket.current = current;
|
|
1100
|
+
if (!keepView) setState({ phase: "loading" });
|
|
1101
|
+
api.view().then((answer) => {
|
|
1102
|
+
if (ticket.current !== current) return;
|
|
1103
|
+
setState(answer.ok ? {
|
|
1104
|
+
phase: "ready",
|
|
1105
|
+
view: answer.data
|
|
1106
|
+
} : {
|
|
1107
|
+
phase: "failed",
|
|
1108
|
+
failure: answer.failure
|
|
1109
|
+
});
|
|
1110
|
+
});
|
|
1111
|
+
};
|
|
1112
|
+
(0, react.useEffect)(() => {
|
|
1113
|
+
read(false);
|
|
1114
|
+
}, []);
|
|
1115
|
+
const refresh = () => {
|
|
1116
|
+
read(true);
|
|
1117
|
+
};
|
|
1118
|
+
const header = (0, react.createElement)("div", { className: "evolution-param-card-head" }, (0, react.createElement)("span", { className: "evolution-param-card-title" }, t("diagTitle")), (0, react.createElement)("button", {
|
|
1119
|
+
type: "button",
|
|
1120
|
+
className: "evolution-param-button",
|
|
1121
|
+
onClick: refresh,
|
|
1122
|
+
"data-action": "diag-refresh"
|
|
1123
|
+
}, state.phase === "loading" ? t("diagRefreshing") : t("diagRefresh")));
|
|
1124
|
+
const body = () => {
|
|
1125
|
+
if (state.phase === "loading") return (0, react.createElement)("p", { className: "evolution-param-loading" }, t("stateLoading"));
|
|
1126
|
+
if (state.phase === "failed") return FailureBlock({
|
|
1127
|
+
failure: state.failure,
|
|
1128
|
+
t: translate,
|
|
1129
|
+
fallback: props.fallback
|
|
1130
|
+
});
|
|
1131
|
+
const view = state.view;
|
|
1132
|
+
const factRows = view.facts.map((fact) => (0, react.createElement)("div", {
|
|
1133
|
+
key: "fact-" + fact.key,
|
|
1134
|
+
className: "evo-diag-row",
|
|
1135
|
+
"data-kind": "fact"
|
|
1136
|
+
}, (0, react.createElement)("span", { className: "evo-diag-key" }, fact.label), (0, react.createElement)("span", { className: "evo-diag-value" }, fact.value), (0, react.createElement)("span", { className: "evo-diag-home" }, t("diagHomeCite", { home: fact.home }))));
|
|
1137
|
+
const runtimeRows = view.runtime.map((row) => (0, react.createElement)("div", {
|
|
1138
|
+
key: "runtime-" + row.key,
|
|
1139
|
+
className: "evo-diag-row",
|
|
1140
|
+
"data-kind": "runtime",
|
|
1141
|
+
"data-status": row.status
|
|
1142
|
+
}, (0, react.createElement)("span", { className: "evo-diag-key" }, row.label), (0, react.createElement)("span", { className: "evo-diag-status" }, t(STATUS_KEYS[row.status])), row.detail === void 0 ? null : (0, react.createElement)("span", { className: "evo-diag-detail" }, row.detail), row.remedy === void 0 ? null : (0, react.createElement)("span", { className: "evo-diag-remedy" }, t("diagRemedy") + row.remedy)));
|
|
1143
|
+
return (0, react.createElement)("div", { className: "evo-diag-body" }, (0, react.createElement)("p", { className: "evolution-params-subtitle" }, t("diagSubtitle")), (0, react.createElement)("p", { className: "evo-diag-meta" }, t("diagSchema", { n: view.schemaVersion }) + " · " + t("diagGeneratedAt", { at: view.generatedAt })), (0, react.createElement)("h3", { className: "evo-diag-section" }, t("diagFactsTitle")), ...factRows, (0, react.createElement)("h3", { className: "evo-diag-section" }, t("diagRuntimeTitle")), ...runtimeRows);
|
|
1144
|
+
};
|
|
1145
|
+
return (0, react.createElement)("div", {
|
|
1146
|
+
className: "evolution-param-card",
|
|
1147
|
+
"data-card": "diagnostics"
|
|
1148
|
+
}, header, body());
|
|
1149
|
+
}
|
|
1150
|
+
//#endregion
|
|
1151
|
+
//#region src/client/diagnostics-state.ts
|
|
1152
|
+
/**
|
|
1153
|
+
* The diagnostics card's data face (W7 S7): ONE request to `HOST_ROUTES.diagnostics.view` through the
|
|
1154
|
+
* GENERATED route client, and the four-state answer the card renders.
|
|
1155
|
+
*
|
|
1156
|
+
* The Host route is the SAME `buildDoctorView` the CLI prints (W6/W8), so this client owns no facts
|
|
1157
|
+
* and no computation — it only renders.
|
|
1158
|
+
* @module @lmzhen/dsh-evolution-settings-ui/client/diagnostics-state
|
|
1159
|
+
*/
|
|
1160
|
+
/**
|
|
1161
|
+
* Build the diagnostics face over one fetch implementation.
|
|
1162
|
+
* @param doFetch - the browser fetch, or a spec stub.
|
|
1163
|
+
*/
|
|
1164
|
+
function createDiagnosticsApi(doFetch = fetch) {
|
|
1165
|
+
return { view: async () => await requestJson(doFetch, ROUTES.diagnostics.view, { headers: { accept: "application/json" } }) };
|
|
1166
|
+
}
|
|
1167
|
+
//#endregion
|
|
1168
|
+
//#region src/client/fallbacks.generated.ts
|
|
1169
|
+
/**
|
|
1170
|
+
* The command-face fallback for every Host surface, generated from evolution-commands'
|
|
1171
|
+
* COMMAND_FALLBACKS (the ONE home). When the platform's connection carrier is absent (E-325) the
|
|
1172
|
+
* panels render this hint instead of a bare HTTP failure — same use case, command face.
|
|
1173
|
+
*/
|
|
1174
|
+
const FALLBACKS = {
|
|
1175
|
+
approval: { command: "/evolution pending … /evolution approve <id> … /evolution reject <id> … /evolution release <id>" },
|
|
1176
|
+
"skill-history": { command: "/evolution restore <name> [v](查看:/evolution skills history <name>)" },
|
|
1177
|
+
"skill-health": { command: "/evolution skills refresh(健康行:/evolution doctor)" },
|
|
1178
|
+
diagnostics: {
|
|
1179
|
+
command: "/evolution doctor",
|
|
1180
|
+
platformCli: "dsh --json /evolution doctor"
|
|
1181
|
+
}
|
|
1182
|
+
};
|
|
1183
|
+
//#endregion
|
|
974
1184
|
//#region src/client/messages.ts
|
|
975
1185
|
/**
|
|
976
1186
|
* Copy for the evolution settings section and its parameter cards.
|
|
@@ -1002,14 +1212,13 @@ window.__ModuleLoader__.load({
|
|
|
1002
1212
|
discard: "放弃修改",
|
|
1003
1213
|
saving: "保存中…",
|
|
1004
1214
|
resetting: "恢复中…",
|
|
1005
|
-
|
|
1215
|
+
writeTargetHost: "保存去向:宿主配置(对所有会话生效)",
|
|
1006
1216
|
invalidNumber: "需要一个数字",
|
|
1007
1217
|
invalidSelect: "不在允许的取值里",
|
|
1008
1218
|
invalidAtLeast: "不能小于同组的 {n}",
|
|
1009
1219
|
approvalConfirmNamed: "即将批准:{n}",
|
|
1010
1220
|
approvalConfirmYes: "确认批准",
|
|
1011
1221
|
approvalConfirmNo: "取消",
|
|
1012
|
-
destMemory: "保存去向:仅本次会话",
|
|
1013
1222
|
resetTo: "复位为 {value}",
|
|
1014
1223
|
refused: "这个值没有被接受(界面上保持原样)。想看原因:在对话里让它说明。",
|
|
1015
1224
|
resetRefused: "恢复默认值没有生效(这一行还是你的值)。想看原因:在对话里让它说明。",
|
|
@@ -1045,7 +1254,27 @@ window.__ModuleLoader__.load({
|
|
|
1045
1254
|
approvalPreviewNone: "看不了预览:",
|
|
1046
1255
|
approvalDiffAdded: "{n} 行新增",
|
|
1047
1256
|
approvalDiffRemoved: "{n} 行删除",
|
|
1048
|
-
approvalDiffCut: "改动区域已截断"
|
|
1257
|
+
approvalDiffCut: "改动区域已截断",
|
|
1258
|
+
diagTitle: "诊断",
|
|
1259
|
+
diagSubtitle: "安装形态、平台线与运行面的当前读数;只读,不改任何东西。",
|
|
1260
|
+
diagFactsTitle: "事实",
|
|
1261
|
+
diagRuntimeTitle: "运行时",
|
|
1262
|
+
diagSchema: "视图 schema v{n}",
|
|
1263
|
+
diagGeneratedAt: "生成于 {at}",
|
|
1264
|
+
diagRefresh: "刷新",
|
|
1265
|
+
diagRefreshing: "刷新中…",
|
|
1266
|
+
diagStatusOk: "正常",
|
|
1267
|
+
diagStatusWarn: "注意",
|
|
1268
|
+
diagStatusFail: "异常",
|
|
1269
|
+
diagStatusUnknown: "未知",
|
|
1270
|
+
diagRemedy: "下一步:",
|
|
1271
|
+
diagHomeCite: "出处:{home}",
|
|
1272
|
+
stateLoading: "正在读取…",
|
|
1273
|
+
failureUnauthorized: "需要登录:重新打开这个页面完成登录后再试。",
|
|
1274
|
+
failureForbidden: "平台拒绝了这个请求(身份不是 owner)。",
|
|
1275
|
+
failureUnmounted: "该功能没有挂载。",
|
|
1276
|
+
failureServer: "读取失败,可以手动重试。",
|
|
1277
|
+
fallbackHint: "可以改用命令:{command}"
|
|
1049
1278
|
};
|
|
1050
1279
|
/** English copy, key for key with {@link zh}. */
|
|
1051
1280
|
const en = {
|
|
@@ -1065,14 +1294,13 @@ window.__ModuleLoader__.load({
|
|
|
1065
1294
|
discard: "Discard",
|
|
1066
1295
|
saving: "Saving…",
|
|
1067
1296
|
resetting: "Resetting…",
|
|
1068
|
-
|
|
1297
|
+
writeTargetHost: "Saved to: host configuration (applies to every session)",
|
|
1069
1298
|
invalidNumber: "Needs a number",
|
|
1070
1299
|
invalidSelect: "Not one of the allowed values",
|
|
1071
1300
|
invalidAtLeast: "must not be below {n}",
|
|
1072
1301
|
approvalConfirmNamed: "About to approve: {n}",
|
|
1073
1302
|
approvalConfirmYes: "Approve",
|
|
1074
1303
|
approvalConfirmNo: "Cancel",
|
|
1075
|
-
destMemory: "Saved to: this session only",
|
|
1076
1304
|
resetTo: "Restores {value}",
|
|
1077
1305
|
refused: "The value was not accepted (the page keeps what you see). For the reason, ask in the conversation.",
|
|
1078
1306
|
resetRefused: "The reset did not take (this row still holds your value). For the reason, ask in the conversation.",
|
|
@@ -1108,7 +1336,27 @@ window.__ModuleLoader__.load({
|
|
|
1108
1336
|
approvalPreviewNone: "no preview: ",
|
|
1109
1337
|
approvalDiffAdded: "{n} lines added",
|
|
1110
1338
|
approvalDiffRemoved: "{n} lines removed",
|
|
1111
|
-
approvalDiffCut: "the changed region is truncated"
|
|
1339
|
+
approvalDiffCut: "the changed region is truncated",
|
|
1340
|
+
diagTitle: "Diagnostics",
|
|
1341
|
+
diagSubtitle: "Current readings for the install form, platform line and runtime surfaces; read-only.",
|
|
1342
|
+
diagFactsTitle: "Facts",
|
|
1343
|
+
diagRuntimeTitle: "Runtime",
|
|
1344
|
+
diagSchema: "view schema v{n}",
|
|
1345
|
+
diagGeneratedAt: "generated at {at}",
|
|
1346
|
+
diagRefresh: "Refresh",
|
|
1347
|
+
diagRefreshing: "refreshing…",
|
|
1348
|
+
diagStatusOk: "ok",
|
|
1349
|
+
diagStatusWarn: "warn",
|
|
1350
|
+
diagStatusFail: "fail",
|
|
1351
|
+
diagStatusUnknown: "unknown",
|
|
1352
|
+
diagRemedy: "Next: ",
|
|
1353
|
+
diagHomeCite: "source: {home}",
|
|
1354
|
+
stateLoading: "reading…",
|
|
1355
|
+
failureUnauthorized: "Sign-in is required: reopen this page and sign in, then retry.",
|
|
1356
|
+
failureForbidden: "The platform refused this request (not the owner principal).",
|
|
1357
|
+
failureUnmounted: "This feature is not mounted.",
|
|
1358
|
+
failureServer: "The read failed; you can retry manually.",
|
|
1359
|
+
fallbackHint: "Command fallback: {command}"
|
|
1112
1360
|
};
|
|
1113
1361
|
/**
|
|
1114
1362
|
* Card titles per settings namespace. A namespace without an entry falls back to
|
|
@@ -1175,6 +1423,23 @@ window.__ModuleLoader__.load({
|
|
|
1175
1423
|
* typed from the registry (\`control\`), so booleans are switches, enums are
|
|
1176
1424
|
* selects and numbers are numeric inputs with the unit shown beside the label.
|
|
1177
1425
|
*/
|
|
1426
|
+
/**
|
|
1427
|
+
* The NAMESPACE-RELATIVE key of one registry field.
|
|
1428
|
+
*
|
|
1429
|
+
* The E3 registry writes family ids in canonical form (`evolution-approval.pendingTtlMs`),
|
|
1430
|
+
* while the platform settings seat speaks a namespace's own field keys (`pendingTtlMs`) —
|
|
1431
|
+
* its snapshot user layer, its base and its op paths are all relative to the namespace. Sending
|
|
1432
|
+
* the canonical id as an op path makes the Host refuse the write
|
|
1433
|
+
* ("Config field evolution-approval.pendingTtlMs is not volatile", measured 2026-10-19), so the
|
|
1434
|
+
* card translates once, here, and every read/write goes through it.
|
|
1435
|
+
* @param namespace - the settings namespace (Loader row id) the card writes.
|
|
1436
|
+
* @param field - one generated field.
|
|
1437
|
+
* @returns the field's key inside that namespace.
|
|
1438
|
+
*/
|
|
1439
|
+
function fieldKeyOf(namespace, field) {
|
|
1440
|
+
const prefix = namespace + ".";
|
|
1441
|
+
return field.id.startsWith(prefix) ? field.id.slice(prefix.length) : field.id;
|
|
1442
|
+
}
|
|
1178
1443
|
/** One cell's text: absent reads as an empty control, containers as JSON. */
|
|
1179
1444
|
function format(value) {
|
|
1180
1445
|
if (value === void 0) return "";
|
|
@@ -1358,16 +1623,17 @@ window.__ModuleLoader__.load({
|
|
|
1358
1623
|
if (snapshot.status === "unavailable") return (0, react.createElement)("p", {
|
|
1359
1624
|
className: "evolution-param-note",
|
|
1360
1625
|
role: "status"
|
|
1361
|
-
}, t(snapshot.reason === "seat-missing" ? "seatMissing" : snapshot.reason === "projection-failed" ? "projectionFailed" :
|
|
1626
|
+
}, t(snapshot.reason === "seat-missing" ? "seatMissing" : snapshot.reason === "projection-failed" ? "projectionFailed" : "unavailable"));
|
|
1362
1627
|
const user = isRecord(snapshot.user) ? snapshot.user : {};
|
|
1363
1628
|
const value = isRecord(snapshot.value) ? snapshot.value : {};
|
|
1364
1629
|
const disabled = !snapshot.writable;
|
|
1365
1630
|
const locked = disabled || busy || clearing !== null;
|
|
1366
1631
|
const titleKey = NAMESPACE_TITLES[namespace];
|
|
1367
1632
|
const title = titleKey === void 0 ? namespace : t(titleKey);
|
|
1368
|
-
const
|
|
1369
|
-
const
|
|
1370
|
-
const
|
|
1633
|
+
const keyOf = (field) => fieldKeyOf(namespace, field);
|
|
1634
|
+
const changed = fields.filter((field) => Object.hasOwn(user, keyOf(field))).length;
|
|
1635
|
+
const textOf = (field) => draft[field.id] ?? format(value[keyOf(field)]);
|
|
1636
|
+
const dirty = fields.filter((field) => draft[field.id] !== void 0 && draft[field.id] !== format(value[keyOf(field)]));
|
|
1371
1637
|
const peerTextOf = (field) => {
|
|
1372
1638
|
if (field.atLeast === "") return void 0;
|
|
1373
1639
|
const related = fields.find((candidate) => candidate.id === field.atLeast);
|
|
@@ -1394,7 +1660,7 @@ window.__ModuleLoader__.load({
|
|
|
1394
1660
|
(async () => {
|
|
1395
1661
|
let accepted;
|
|
1396
1662
|
try {
|
|
1397
|
-
accepted = await clear(field);
|
|
1663
|
+
accepted = await clear(fieldKeyOf(namespace, { id: field }));
|
|
1398
1664
|
} catch (caught) {
|
|
1399
1665
|
setClearing(null);
|
|
1400
1666
|
setError(caught instanceof Error && caught.message !== "" ? caught.message : t("resetRefused"));
|
|
@@ -1414,7 +1680,7 @@ window.__ModuleLoader__.load({
|
|
|
1414
1680
|
setPending({
|
|
1415
1681
|
accepted: await mutateOnce(dirty.map((field) => ({
|
|
1416
1682
|
op: "set",
|
|
1417
|
-
path: [field
|
|
1683
|
+
path: [keyOf(field)],
|
|
1418
1684
|
value: parseFor(field.control, textOf(field))
|
|
1419
1685
|
}))),
|
|
1420
1686
|
resets: false
|
|
@@ -1432,7 +1698,7 @@ window.__ModuleLoader__.load({
|
|
|
1432
1698
|
onClick: () => {
|
|
1433
1699
|
setOpen(!open);
|
|
1434
1700
|
}
|
|
1435
|
-
}, (0, react.createElement)("span", { className: "evolution-param-card-title" }, title), changed === 0 ? null : (0, react.createElement)("span", { className: "evolution-param-count" }, t("changed", { n: changed })), (0, react.createElement)("span", { className: "evolution-param-dest" },
|
|
1701
|
+
}, (0, react.createElement)("span", { className: "evolution-param-card-title" }, title), changed === 0 ? null : (0, react.createElement)("span", { className: "evolution-param-count" }, t("changed", { n: changed })), (0, react.createElement)("span", { className: "evolution-param-dest" }, t("writeTargetHost")), (0, react.createElement)("span", { className: "evolution-param-chevron" }, open ? "▲" : "▼")), open ? (0, react.createElement)("div", { className: "evolution-param-body" }, disabled ? (0, react.createElement)("p", {
|
|
1436
1702
|
className: "evolution-param-note",
|
|
1437
1703
|
role: "status"
|
|
1438
1704
|
}, t("readonly")) : null, fields.length === 0 ? (0, react.createElement)("p", {
|
|
@@ -1445,10 +1711,10 @@ window.__ModuleLoader__.load({
|
|
|
1445
1711
|
key: field.id,
|
|
1446
1712
|
field,
|
|
1447
1713
|
text: textOf(field),
|
|
1448
|
-
overridden: Object.hasOwn(user, field
|
|
1714
|
+
overridden: Object.hasOwn(user, keyOf(field)),
|
|
1449
1715
|
disabled: locked,
|
|
1450
1716
|
clearing: clearing === field.id,
|
|
1451
|
-
resetTo: resetToOf(snapshot.base?.[field
|
|
1717
|
+
resetTo: resetToOf(snapshot.base?.[keyOf(field)]),
|
|
1452
1718
|
invalid: invalidReason(field, textOf(field), peerTextOf(field)),
|
|
1453
1719
|
t,
|
|
1454
1720
|
onChange: change,
|
|
@@ -1498,7 +1764,6 @@ window.__ModuleLoader__.load({
|
|
|
1498
1764
|
user: void 0,
|
|
1499
1765
|
writable: false,
|
|
1500
1766
|
base: void 0,
|
|
1501
|
-
mode: "host",
|
|
1502
1767
|
reason: "seat-missing"
|
|
1503
1768
|
});
|
|
1504
1769
|
/** What a card shows when the seat served the row but projecting it failed. A frozen constant
|
|
@@ -1510,7 +1775,6 @@ window.__ModuleLoader__.load({
|
|
|
1510
1775
|
user: void 0,
|
|
1511
1776
|
writable: false,
|
|
1512
1777
|
base: void 0,
|
|
1513
|
-
mode: "host",
|
|
1514
1778
|
reason: "projection-failed"
|
|
1515
1779
|
});
|
|
1516
1780
|
/**
|
|
@@ -1525,8 +1789,7 @@ window.__ModuleLoader__.load({
|
|
|
1525
1789
|
user: snapshot.user,
|
|
1526
1790
|
writable: snapshot.writable,
|
|
1527
1791
|
base: isRecord(snapshot.base) ? snapshot.base : void 0,
|
|
1528
|
-
|
|
1529
|
-
...snapshot.status === "unavailable" ? { reason: snapshot.mode === "memory" ? "session-memory" : "not-served" } : {}
|
|
1792
|
+
...snapshot.status === "unavailable" ? { reason: "not-served" } : {}
|
|
1530
1793
|
};
|
|
1531
1794
|
}
|
|
1532
1795
|
/**
|
|
@@ -1687,9 +1950,9 @@ window.__ModuleLoader__.load({
|
|
|
1687
1950
|
* @param fields - the row's fields.
|
|
1688
1951
|
* @returns how many of them the user overrode.
|
|
1689
1952
|
*/
|
|
1690
|
-
function overridden(user, fields) {
|
|
1953
|
+
function overridden(user, fields, namespace) {
|
|
1691
1954
|
const layer = isRecord(user) ? user : {};
|
|
1692
|
-
return fields.filter((field) => Object.hasOwn(layer, field
|
|
1955
|
+
return fields.filter((field) => Object.hasOwn(layer, fieldKeyOf(namespace, field))).length;
|
|
1693
1956
|
}
|
|
1694
1957
|
/**
|
|
1695
1958
|
* Render one row's configuration entry.
|
|
@@ -1702,7 +1965,7 @@ window.__ModuleLoader__.load({
|
|
|
1702
1965
|
className: "evolution-param-note",
|
|
1703
1966
|
role: "status"
|
|
1704
1967
|
}, t("seatMissing"));
|
|
1705
|
-
const changed = overridden(form.state.user, fields);
|
|
1968
|
+
const changed = overridden(form.state.user, fields, namespace);
|
|
1706
1969
|
if (view === "summary") return changed === 0 ? null : (0, react.createElement)("span", null, t("changed", { n: changed }));
|
|
1707
1970
|
return ParamCardView({
|
|
1708
1971
|
namespace,
|
|
@@ -1744,6 +2007,8 @@ window.__ModuleLoader__.load({
|
|
|
1744
2007
|
* the section — one definition in the module both sides already share is what keeps that pair acyclic.
|
|
1745
2008
|
*/
|
|
1746
2009
|
const PENDING_CARD_KEY = "approval-pending";
|
|
2010
|
+
/** The diagnostics card's slot key: read-only, no settings namespace (its data comes from HOST_ROUTES.diagnostics). */
|
|
2011
|
+
const DIAGNOSTICS_CARD_KEY = "diagnostics";
|
|
1747
2012
|
//#endregion
|
|
1748
2013
|
//#region src/client/SettingsSection.ts
|
|
1749
2014
|
/**
|
|
@@ -1765,7 +2030,10 @@ window.__ModuleLoader__.load({
|
|
|
1765
2030
|
return (0, react.createElement)("div", { className: "evolution-params" }, (0, react.createElement)("h2", { className: "evolution-params-title" }, face.t("title")), (0, react.createElement)("p", { className: "evolution-params-subtitle" }, face.t("subtitle")), (0, react.createElement)("p", { className: "evolution-params-about" }, "0.24.0 · Web settings cards for the evolution family's parameter namespaces"), (0, react.createElement)("div", {
|
|
1766
2031
|
key: PENDING_CARD_KEY,
|
|
1767
2032
|
className: "evolution-params-group"
|
|
1768
|
-
}, face.renderSlot(CARD_SLOT, {}, { entryKey: PENDING_CARD_KEY })),
|
|
2033
|
+
}, face.renderSlot(CARD_SLOT, {}, { entryKey: PENDING_CARD_KEY })), (0, react.createElement)("div", {
|
|
2034
|
+
key: DIAGNOSTICS_CARD_KEY,
|
|
2035
|
+
className: "evolution-params-group"
|
|
2036
|
+
}, face.renderSlot(CARD_SLOT, {}, { entryKey: DIAGNOSTICS_CARD_KEY })), ...CLIENT_PARAM_SECTIONS.map((section_) => (0, react.createElement)("div", {
|
|
1769
2037
|
key: section_.namespace,
|
|
1770
2038
|
className: "evolution-params-group"
|
|
1771
2039
|
}, face.renderSlot(CARD_SLOT, {}, { entryKey: section_.namespace }))));
|
|
@@ -1970,9 +2238,20 @@ window.__ModuleLoader__.load({
|
|
|
1970
2238
|
inject: () => ({
|
|
1971
2239
|
t,
|
|
1972
2240
|
api,
|
|
1973
|
-
confirmMode
|
|
2241
|
+
confirmMode,
|
|
2242
|
+
fallback: FALLBACKS.approval
|
|
1974
2243
|
})
|
|
1975
2244
|
}, PendingCard);
|
|
2245
|
+
const diagApi = createDiagnosticsApi((input, init) => fetch(input, init));
|
|
2246
|
+
yield seam.slots.register({
|
|
2247
|
+
name: CARD_SLOT,
|
|
2248
|
+
key: DIAGNOSTICS_CARD_KEY,
|
|
2249
|
+
inject: () => ({
|
|
2250
|
+
t,
|
|
2251
|
+
api: diagApi,
|
|
2252
|
+
fallback: FALLBACKS.diagnostics
|
|
2253
|
+
})
|
|
2254
|
+
}, DiagnosticsCard);
|
|
1976
2255
|
});
|
|
1977
2256
|
seam.slots.inject(ROW_CONFIG_SLOT, function* () {
|
|
1978
2257
|
for (const seat of CLIENT_ROW_SEATS) for (const rowId of seat.rows) {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The diagnostics card (W7 S7): the browser face of the SAME DiagnosticsView the CLI and the
|
|
3
|
+
* `/api/dsh-evolution/diagnostics` route answer.
|
|
4
|
+
*
|
|
5
|
+
* Read-only by construction: it fetches once on mount and only refetches on an explicit Refresh; a
|
|
6
|
+
* failed read renders the shared FailureBlock with the command-face fallback. Facts and runtime rows
|
|
7
|
+
* are rendered in the view's own order — the client owns no key list and no status vocabulary.
|
|
8
|
+
* @module @lmzhen/dsh-evolution-settings-ui/client/DiagnosticsCard
|
|
9
|
+
*/
|
|
10
|
+
import { type ReactNode } from 'react';
|
|
11
|
+
import type { DiagnosticsApi } from './diagnostics-state.ts';
|
|
12
|
+
import type { FallbackHint } from './contracts.generated.ts';
|
|
13
|
+
import type { MessageKey } from './messages.ts';
|
|
14
|
+
/** Injected face: the copy seat, the one host call, and this surface's command fallback. */
|
|
15
|
+
export interface DiagnosticsCardFace {
|
|
16
|
+
t: (key: MessageKey, values?: Readonly<Record<string, string | number>>) => string;
|
|
17
|
+
api: DiagnosticsApi;
|
|
18
|
+
fallback?: FallbackHint | undefined;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Render the card.
|
|
22
|
+
* @param props - the copy seat, the host call and the command fallback.
|
|
23
|
+
* @returns the card.
|
|
24
|
+
*/
|
|
25
|
+
export declare function DiagnosticsCard(props: DiagnosticsCardFace): ReactNode;
|
|
26
|
+
//# sourceMappingURL=DiagnosticsCard.d.ts.map
|
|
@@ -48,6 +48,20 @@ export type ParamCardProps = Omit<ParamCardFace, 'hooks'> & {
|
|
|
48
48
|
};
|
|
49
49
|
/** The op list a save hands to the seat — the seam's own shape, never a second vocabulary. */
|
|
50
50
|
export type SettingsParamOp = SettingsPathOpLike;
|
|
51
|
+
/**
|
|
52
|
+
* The NAMESPACE-RELATIVE key of one registry field.
|
|
53
|
+
*
|
|
54
|
+
* The E3 registry writes family ids in canonical form (`evolution-approval.pendingTtlMs`),
|
|
55
|
+
* while the platform settings seat speaks a namespace's own field keys (`pendingTtlMs`) —
|
|
56
|
+
* its snapshot user layer, its base and its op paths are all relative to the namespace. Sending
|
|
57
|
+
* the canonical id as an op path makes the Host refuse the write
|
|
58
|
+
* ("Config field evolution-approval.pendingTtlMs is not volatile", measured 2026-10-19), so the
|
|
59
|
+
* card translates once, here, and every read/write goes through it.
|
|
60
|
+
* @param namespace - the settings namespace (Loader row id) the card writes.
|
|
61
|
+
* @param field - one generated field.
|
|
62
|
+
* @returns the field's key inside that namespace.
|
|
63
|
+
*/
|
|
64
|
+
export declare function fieldKeyOf(namespace: string, field: ClientParamField): string;
|
|
51
65
|
/**
|
|
52
66
|
* U-4: whether one field's CURRENT text is a value the Host can take. The controls constrain their own
|
|
53
67
|
* domains (a number input sanitizes, a select only offers listed values), so the reachable case is a
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* @module @lmzhen/dsh-evolution-settings-ui/client/PendingCard
|
|
14
14
|
*/
|
|
15
15
|
import { type ReactNode } from 'react';
|
|
16
|
+
import type { FallbackHint } from './contracts.generated.ts';
|
|
16
17
|
import type { ApprovalApi } from './api.ts';
|
|
17
18
|
import type { MessageKey } from './messages.ts';
|
|
18
19
|
/** Injected face: the copy seat and the host calls. No hook compartment — this card keeps its own state. */
|
|
@@ -24,6 +25,8 @@ export interface PendingCardFace {
|
|
|
24
25
|
* captured once would pin the gate for the life of the fiber. Absent ⇒ the shipped default `auto`.
|
|
25
26
|
*/
|
|
26
27
|
confirmMode?: () => 'auto' | 'ask' | 'timeout';
|
|
28
|
+
/** G5/W7 S4: the command-face fallback rendered with an unreachable/unauthenticated failure. */
|
|
29
|
+
fallback?: FallbackHint | undefined;
|
|
27
30
|
}
|
|
28
31
|
/**
|
|
29
32
|
* U-6: the registry's gate word, read out of a section's user layer. ONE definition, exported so the
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type RouteAnswer } from './route-client.generated.ts';
|
|
1
2
|
/** One staged write, as the pending route hands it over (never the staged args). */
|
|
2
3
|
export interface PendingRow {
|
|
3
4
|
readonly id: string;
|
|
@@ -33,14 +34,8 @@ export type PreviewAnswer = {
|
|
|
33
34
|
readonly available: false;
|
|
34
35
|
readonly reason: string;
|
|
35
36
|
};
|
|
36
|
-
/** What one call answered: the
|
|
37
|
-
export type ApprovalAnswer<T> =
|
|
38
|
-
readonly ok: true;
|
|
39
|
-
readonly data: T;
|
|
40
|
-
} | {
|
|
41
|
-
readonly ok: false;
|
|
42
|
-
readonly message: string;
|
|
43
|
-
};
|
|
37
|
+
/** What one call answered: the generated four-state RouteAnswer (no local failure vocabulary). */
|
|
38
|
+
export type ApprovalAnswer<T> = RouteAnswer<T>;
|
|
44
39
|
/**
|
|
45
40
|
* Which window the pending route is asked for. The host route has always taken `?status=` (the CLI
|
|
46
41
|
* asks for both); this half asked for neither, so a record stuck in `executing` — an approve that
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Literal mirror of the frozen Host diagnostics contract (W1 §2.8 / core diagnostics.ts). The browser
|
|
3
|
+
* build keeps every @deepseek-ai/* specifier external, so the client CANNOT import the host type — the
|
|
4
|
+
* shape is mirrored mechanically here instead. The key unions are rendered from core diagnostics.ts by
|
|
5
|
+
* the generator, so a key change regenerates all three copies (a stale copy fails --check).
|
|
6
|
+
*/
|
|
7
|
+
export type DiagnosticsSchemaVersion = 2;
|
|
8
|
+
export type DiagnosticsFactKey = 'platform-line' | 'family-version' | 'install-form' | 'preset-ids' | 'readonly-contract' | 'upgrade-order' | 'fallback-map';
|
|
9
|
+
export type DiagnosticsRuntimeKey = 'subagent-channel' | 'restricted-preset' | 'host-surface' | 'session-fold' | 'platform-services' | 'runs' | 'platform-pairing' | 'install-form-detected';
|
|
10
|
+
export type DiagnosticStatus = 'ok' | 'warn' | 'fail' | 'unknown';
|
|
11
|
+
export interface DocFactRow {
|
|
12
|
+
readonly key: DiagnosticsFactKey;
|
|
13
|
+
readonly label: string;
|
|
14
|
+
readonly value: string;
|
|
15
|
+
readonly home: string;
|
|
16
|
+
}
|
|
17
|
+
export interface DiagnosticRow {
|
|
18
|
+
readonly key: DiagnosticsRuntimeKey;
|
|
19
|
+
readonly label: string;
|
|
20
|
+
readonly status: DiagnosticStatus;
|
|
21
|
+
readonly detail?: string;
|
|
22
|
+
readonly remedy?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface DiagnosticsView {
|
|
25
|
+
readonly schemaVersion: DiagnosticsSchemaVersion;
|
|
26
|
+
readonly generatedAt: string;
|
|
27
|
+
readonly facts: readonly DocFactRow[];
|
|
28
|
+
readonly runtime: readonly DiagnosticRow[];
|
|
29
|
+
}
|
|
30
|
+
/** The installation form the doctor reports (W8 install-form-detected row). */
|
|
31
|
+
export type InstallFormId = 'M1' | 'M2' | 'M3' | 'M4';
|
|
32
|
+
/** One surface's command-face path(s) — the client `fallbacks.generated.ts` table's row type. */
|
|
33
|
+
export interface FallbackHint {
|
|
34
|
+
readonly command: string;
|
|
35
|
+
readonly platformCli?: string;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=contracts.generated.d.ts.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type RouteAnswer } from './route-client.generated.ts';
|
|
2
|
+
import type { DiagnosticsView } from './contracts.generated.ts';
|
|
3
|
+
/** What the card renders: the view, or the classified failure that kept it from arriving. */
|
|
4
|
+
export type DiagnosticsState = {
|
|
5
|
+
readonly phase: 'loading';
|
|
6
|
+
} | {
|
|
7
|
+
readonly phase: 'ready';
|
|
8
|
+
readonly view: DiagnosticsView;
|
|
9
|
+
} | {
|
|
10
|
+
readonly phase: 'failed';
|
|
11
|
+
readonly failure: import('./route-client.generated.ts').RouteFailure;
|
|
12
|
+
};
|
|
13
|
+
/** The one call the card makes. */
|
|
14
|
+
export interface DiagnosticsApi {
|
|
15
|
+
view(): Promise<RouteAnswer<DiagnosticsView>>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Build the diagnostics face over one fetch implementation.
|
|
19
|
+
* @param doFetch - the browser fetch, or a spec stub.
|
|
20
|
+
*/
|
|
21
|
+
export declare function createDiagnosticsApi(doFetch?: typeof fetch): DiagnosticsApi;
|
|
22
|
+
/**
|
|
23
|
+
* Read the view once and map it onto the card's four states (loading is the caller's initial value).
|
|
24
|
+
* @param doFetch - the fetch to use.
|
|
25
|
+
* @returns ready with the view, or failed with the classified failure.
|
|
26
|
+
*/
|
|
27
|
+
export declare function loadDiagnostics(doFetch?: typeof fetch): Promise<DiagnosticsState>;
|
|
28
|
+
//# sourceMappingURL=diagnostics-state.d.ts.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { FallbackHint } from './contracts.generated.ts';
|
|
2
|
+
/**
|
|
3
|
+
* The command-face fallback for every Host surface, generated from evolution-commands'
|
|
4
|
+
* COMMAND_FALLBACKS (the ONE home). When the platform's connection carrier is absent (E-325) the
|
|
5
|
+
* panels render this hint instead of a bare HTTP failure — same use case, command face.
|
|
6
|
+
*/
|
|
7
|
+
export declare const FALLBACKS: Readonly<Record<'approval' | 'skill-history' | 'skill-health' | 'diagnostics', FallbackHint>>;
|
|
8
|
+
//# sourceMappingURL=fallbacks.generated.d.ts.map
|
|
@@ -28,14 +28,13 @@ export declare const zh: {
|
|
|
28
28
|
readonly discard: "放弃修改";
|
|
29
29
|
readonly saving: "保存中…";
|
|
30
30
|
readonly resetting: "恢复中…";
|
|
31
|
-
readonly
|
|
31
|
+
readonly writeTargetHost: "保存去向:宿主配置(对所有会话生效)";
|
|
32
32
|
readonly invalidNumber: "需要一个数字";
|
|
33
33
|
readonly invalidSelect: "不在允许的取值里";
|
|
34
34
|
readonly invalidAtLeast: "不能小于同组的 {n}";
|
|
35
35
|
readonly approvalConfirmNamed: "即将批准:{n}";
|
|
36
36
|
readonly approvalConfirmYes: "确认批准";
|
|
37
37
|
readonly approvalConfirmNo: "取消";
|
|
38
|
-
readonly destMemory: "保存去向:仅本次会话";
|
|
39
38
|
readonly resetTo: "复位为 {value}";
|
|
40
39
|
readonly refused: "这个值没有被接受(界面上保持原样)。想看原因:在对话里让它说明。";
|
|
41
40
|
readonly resetRefused: "恢复默认值没有生效(这一行还是你的值)。想看原因:在对话里让它说明。";
|
|
@@ -72,6 +71,26 @@ export declare const zh: {
|
|
|
72
71
|
readonly approvalDiffAdded: "{n} 行新增";
|
|
73
72
|
readonly approvalDiffRemoved: "{n} 行删除";
|
|
74
73
|
readonly approvalDiffCut: "改动区域已截断";
|
|
74
|
+
readonly diagTitle: "诊断";
|
|
75
|
+
readonly diagSubtitle: "安装形态、平台线与运行面的当前读数;只读,不改任何东西。";
|
|
76
|
+
readonly diagFactsTitle: "事实";
|
|
77
|
+
readonly diagRuntimeTitle: "运行时";
|
|
78
|
+
readonly diagSchema: "视图 schema v{n}";
|
|
79
|
+
readonly diagGeneratedAt: "生成于 {at}";
|
|
80
|
+
readonly diagRefresh: "刷新";
|
|
81
|
+
readonly diagRefreshing: "刷新中…";
|
|
82
|
+
readonly diagStatusOk: "正常";
|
|
83
|
+
readonly diagStatusWarn: "注意";
|
|
84
|
+
readonly diagStatusFail: "异常";
|
|
85
|
+
readonly diagStatusUnknown: "未知";
|
|
86
|
+
readonly diagRemedy: "下一步:";
|
|
87
|
+
readonly diagHomeCite: "出处:{home}";
|
|
88
|
+
readonly stateLoading: "正在读取…";
|
|
89
|
+
readonly failureUnauthorized: "需要登录:重新打开这个页面完成登录后再试。";
|
|
90
|
+
readonly failureForbidden: "平台拒绝了这个请求(身份不是 owner)。";
|
|
91
|
+
readonly failureUnmounted: "该功能没有挂载。";
|
|
92
|
+
readonly failureServer: "读取失败,可以手动重试。";
|
|
93
|
+
readonly fallbackHint: "可以改用命令:{command}";
|
|
75
94
|
};
|
|
76
95
|
/** English copy, key for key with {@link zh}. */
|
|
77
96
|
export declare const en: Record<keyof typeof zh, string>;
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* @module @lmzhen/dsh-evolution-settings-ui/client/pending-state
|
|
9
9
|
*/
|
|
10
10
|
import type { PendingRow, PreviewAnswer } from './api.ts';
|
|
11
|
+
import type { RouteFailure } from './route-client.generated.ts';
|
|
11
12
|
/** What one row's preview is showing while it is open. */
|
|
12
13
|
export type PreviewView = {
|
|
13
14
|
readonly kind: 'loading';
|
|
@@ -21,7 +22,7 @@ export type PreviewView = {
|
|
|
21
22
|
readonly reason: string;
|
|
22
23
|
} | {
|
|
23
24
|
readonly kind: 'failed';
|
|
24
|
-
readonly
|
|
25
|
+
readonly failure: RouteFailure;
|
|
25
26
|
};
|
|
26
27
|
/** What the card is showing right now. */
|
|
27
28
|
export type PendingView = {
|
|
@@ -33,7 +34,7 @@ export type PendingView = {
|
|
|
33
34
|
readonly kind: 'empty';
|
|
34
35
|
} | {
|
|
35
36
|
readonly kind: 'failed';
|
|
36
|
-
readonly
|
|
37
|
+
readonly failure: RouteFailure;
|
|
37
38
|
};
|
|
38
39
|
/** The whole card state: the view, the ids with an action in flight, and the last refusal line. */
|
|
39
40
|
export interface PendingState {
|
|
@@ -62,8 +63,8 @@ export declare function loading(state: PendingState): PendingState;
|
|
|
62
63
|
* being shown the same line a slow answer shows.
|
|
63
64
|
*/
|
|
64
65
|
export declare function loaded(state: PendingState, rows: readonly PendingRow[]): PendingState;
|
|
65
|
-
/** The list could not be read: the
|
|
66
|
-
export declare function failed(state: PendingState,
|
|
66
|
+
/** The list could not be read: the classified failure stays on screen next to a retry control. */
|
|
67
|
+
export declare function failed(state: PendingState, failure: RouteFailure): PendingState;
|
|
67
68
|
/** Mark one record as having an action in flight (its buttons are disabled until it ends). */
|
|
68
69
|
export declare function beginAction(state: PendingState, id: string): PendingState;
|
|
69
70
|
/** The action ended, whatever it answered. */
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The four-state transport classifier every family client shares (W7 §2). The platform owns
|
|
3
|
+
* admission (401/403/404 are its answers, not ours), so the client reads the STATUS only for those
|
|
4
|
+
* three and never tries to interpret a platform body. Everything the family route itself answers
|
|
5
|
+
* keeps its business envelope: 2xx + {ok:true,data} or 2xx + {ok:false,code,message}.
|
|
6
|
+
*/
|
|
7
|
+
export type RouteFailure = {
|
|
8
|
+
readonly kind: 'unauthenticated';
|
|
9
|
+
readonly status: 401;
|
|
10
|
+
} | {
|
|
11
|
+
readonly kind: 'forbidden';
|
|
12
|
+
readonly status: 403;
|
|
13
|
+
} | {
|
|
14
|
+
readonly kind: 'unmounted';
|
|
15
|
+
readonly status: 404;
|
|
16
|
+
} | {
|
|
17
|
+
readonly kind: 'server';
|
|
18
|
+
readonly status: number;
|
|
19
|
+
readonly detail?: string;
|
|
20
|
+
} | {
|
|
21
|
+
readonly kind: 'refused';
|
|
22
|
+
readonly code: string;
|
|
23
|
+
readonly message: string;
|
|
24
|
+
};
|
|
25
|
+
/** One route answer: the data, or the classified failure. */
|
|
26
|
+
export type RouteAnswer<T> = {
|
|
27
|
+
readonly ok: true;
|
|
28
|
+
readonly data: T;
|
|
29
|
+
} | {
|
|
30
|
+
readonly ok: false;
|
|
31
|
+
readonly failure: RouteFailure;
|
|
32
|
+
};
|
|
33
|
+
/** Classify a non-2xx status: 401/403/404 keep their own kind, everything else is a server answer. */
|
|
34
|
+
export declare function classify(status: number): RouteFailure;
|
|
35
|
+
/**
|
|
36
|
+
* One request, no retries and no body parsing for the platform's admission answers.
|
|
37
|
+
* @param doFetch - the fetch in use (the browser's, or a spec's stub).
|
|
38
|
+
* @param path - the exact route path from the generated ROUTES table.
|
|
39
|
+
* @param init - standard RequestInit.
|
|
40
|
+
* @returns the data, or the classified failure (network/parse failures read as 'server').
|
|
41
|
+
*/
|
|
42
|
+
export declare function requestJson<T>(doFetch: typeof fetch, path: string, init?: RequestInit): Promise<RouteAnswer<T>>;
|
|
43
|
+
/** The one-sentence rendering legacy callers attach to a classified failure. */
|
|
44
|
+
export declare function failureMessage(failure: RouteFailure): string;
|
|
45
|
+
//# sourceMappingURL=route-client.generated.d.ts.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** One host data-plane surface the browser half can call. */
|
|
2
|
+
export type SurfaceId = 'approval' | 'skill-history' | 'skill-health' | 'diagnostics';
|
|
3
|
+
/**
|
|
4
|
+
* Every family host path, generated from evolution-core's HOST_ROUTES (the ONE declaration).
|
|
5
|
+
* A browser half cannot import the host package at runtime (client bundles keep @deepseek-ai/*
|
|
6
|
+
* external), so the table is mirrored here mechanically; a rename in core regenerates all three
|
|
7
|
+
* client copies, and the generator's --check makes a stale copy red.
|
|
8
|
+
*/
|
|
9
|
+
export declare const ROUTES: {
|
|
10
|
+
readonly approval: {
|
|
11
|
+
readonly pending: "/api/dsh-evolution/approval/pending";
|
|
12
|
+
readonly preview: "/api/dsh-evolution/approval/preview";
|
|
13
|
+
readonly approve: "/api/dsh-evolution/approval/approve";
|
|
14
|
+
readonly reject: "/api/dsh-evolution/approval/reject";
|
|
15
|
+
};
|
|
16
|
+
readonly 'skill-history': {
|
|
17
|
+
readonly skills: "/api/dsh-evolution/skill-history/skills";
|
|
18
|
+
readonly versions: "/api/dsh-evolution/skill-history/versions";
|
|
19
|
+
readonly diff: "/api/dsh-evolution/skill-history/versions/diff";
|
|
20
|
+
readonly body: "/api/dsh-evolution/skill-history/versions/body";
|
|
21
|
+
readonly undo: "/api/dsh-evolution/skill-history/undo";
|
|
22
|
+
readonly health: "/api/dsh-evolution/skill-history/health";
|
|
23
|
+
};
|
|
24
|
+
readonly 'skill-health': {
|
|
25
|
+
readonly rows: "/api/dsh-evolution/skill-health/rows";
|
|
26
|
+
};
|
|
27
|
+
readonly diagnostics: {
|
|
28
|
+
readonly view: "/api/dsh-evolution/diagnostics";
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=routes.generated.d.ts.map
|
|
@@ -35,6 +35,8 @@ export declare function rowConfigKey(bundle: string, rowId: string): string;
|
|
|
35
35
|
* the section — one definition in the module both sides already share is what keeps that pair acyclic.
|
|
36
36
|
*/
|
|
37
37
|
export declare const PENDING_CARD_KEY = "approval-pending";
|
|
38
|
+
/** The diagnostics card's slot key: read-only, no settings namespace (its data comes from HOST_ROUTES.diagnostics). */
|
|
39
|
+
export declare const DIAGNOSTICS_CARD_KEY = "diagnostics";
|
|
38
40
|
/** Snapshot of one settings row, as the client seat reports it. */
|
|
39
41
|
export interface ParamSectionSnapshot {
|
|
40
42
|
status: 'loading' | 'ready' | 'unavailable';
|
|
@@ -44,14 +46,12 @@ export interface ParamSectionSnapshot {
|
|
|
44
46
|
writable: boolean;
|
|
45
47
|
/** U-5: the composition layer a cleared field reverts to (platform `base`, previously dropped). */
|
|
46
48
|
base: Record<string, unknown> | undefined;
|
|
47
|
-
/** U-5: where a save lands — `memory` means this session only (platform `mode`, previously dropped). */
|
|
48
|
-
mode: 'host' | 'memory';
|
|
49
49
|
/**
|
|
50
50
|
* Why an `unavailable` snapshot is unavailable: this bundle found no settings seat at
|
|
51
51
|
* all, or the seat is there and serves no such row. The two read differently to the
|
|
52
52
|
* operator (one is the UI's own gap, the other is a row that is not composed).
|
|
53
53
|
*/
|
|
54
|
-
reason?: 'seat-missing' | 'not-served' | 'projection-failed'
|
|
54
|
+
reason?: 'seat-missing' | 'not-served' | 'projection-failed';
|
|
55
55
|
}
|
|
56
56
|
/** The reactive source the renderer binds to a `use<Name>` seat. */
|
|
57
57
|
export interface ParamSectionSource {
|
|
@@ -84,7 +84,6 @@ export interface ConfigFormSnapshotLike<T = Record<string, unknown>> {
|
|
|
84
84
|
user: unknown;
|
|
85
85
|
revision: number | undefined;
|
|
86
86
|
writable: boolean;
|
|
87
|
-
mode: 'host' | 'memory';
|
|
88
87
|
}
|
|
89
88
|
/** One row's client write face (0.2.x `configForms`). */
|
|
90
89
|
export interface ConfigFormLike<T = Record<string, unknown>> {
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import type { RouteAnswer, RouteFailure } from './route-client.generated.ts';
|
|
3
|
+
import type { FallbackHint } from './contracts.generated.ts';
|
|
4
|
+
/**
|
|
5
|
+
* The four-state base of every family card (W7 §2). Load states are read from the generated
|
|
6
|
+
* route-client's answer — a card owns no HTTP vocabulary and no second state machine.
|
|
7
|
+
*/
|
|
8
|
+
export type LoadState<T> = {
|
|
9
|
+
readonly phase: 'loading';
|
|
10
|
+
} | {
|
|
11
|
+
readonly phase: 'ready';
|
|
12
|
+
readonly data: T;
|
|
13
|
+
} | {
|
|
14
|
+
readonly phase: 'empty';
|
|
15
|
+
} | {
|
|
16
|
+
readonly phase: 'failed';
|
|
17
|
+
readonly failure: RouteFailure;
|
|
18
|
+
};
|
|
19
|
+
/** Map one classified answer onto the card's four states. */
|
|
20
|
+
export declare function loadStateOf<T>(answer: RouteAnswer<T>, isEmpty: (data: T) => boolean): LoadState<T>;
|
|
21
|
+
/** The locale lookup every block calls (the platform's own `t`). */
|
|
22
|
+
export interface Translate {
|
|
23
|
+
(key: string, values?: Readonly<Record<string, string | number>>): string;
|
|
24
|
+
}
|
|
25
|
+
export type BlockTone = 'neutral' | 'warn' | 'fail';
|
|
26
|
+
export interface BlockProps {
|
|
27
|
+
readonly tone?: BlockTone;
|
|
28
|
+
readonly title?: string;
|
|
29
|
+
readonly children?: ReactNode;
|
|
30
|
+
}
|
|
31
|
+
/** A neutral/ warn/ fail block; a fail block is role=alert so assistive tech announces it. */
|
|
32
|
+
export declare function Block(props: BlockProps): ReactNode;
|
|
33
|
+
export interface FailureBlockProps {
|
|
34
|
+
readonly failure: RouteFailure;
|
|
35
|
+
readonly t: Translate;
|
|
36
|
+
/** The command-face fallback for this surface (rendered on unmounted/failed answers). */
|
|
37
|
+
readonly fallback?: FallbackHint | undefined;
|
|
38
|
+
}
|
|
39
|
+
/** One classified failure as a reader-facing sentence. */
|
|
40
|
+
export declare function failureText(failure: RouteFailure, t: Translate): string;
|
|
41
|
+
/** The failed-state block: sentence + (when the surface exists but is unreachable) the command fallback. */
|
|
42
|
+
export declare function FailureBlock(props: FailureBlockProps): ReactNode;
|
|
43
|
+
//# sourceMappingURL=states.generated.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lmzhen/dsh-evolution-settings-ui",
|
|
3
3
|
"description": "Web settings cards for the evolution family's parameter namespaces (community build)",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.27.1",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -39,7 +39,10 @@
|
|
|
39
39
|
}
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
|
-
"@deepseek-ai/cordis": "^4.0.
|
|
42
|
+
"@deepseek-ai/cordis": "^4.0.5",
|
|
43
43
|
"react": "^18.2.0"
|
|
44
|
+
},
|
|
45
|
+
"engines": {
|
|
46
|
+
"dsh": ">=0.2.1-rc.1 <0.2.2-0"
|
|
44
47
|
}
|
|
45
48
|
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The approval paths this browser half calls.
|
|
3
|
-
*
|
|
4
|
-
* A browser half cannot import the host package's table at runtime (the family's client bundles treat
|
|
5
|
-
* every `@deepseek-ai/*` specifier as external), so the literals are declared here and a spec compares
|
|
6
|
-
* them against `APPROVAL_ROUTES` in `evolution-approval` — a rename on one side would otherwise surface
|
|
7
|
-
* as a 404 in the GUI instead of in CI.
|
|
8
|
-
* @module @lmzhen/dsh-evolution-settings-ui/client/approval-routes
|
|
9
|
-
*/
|
|
10
|
-
/** One path per call the pending card makes. */
|
|
11
|
-
export declare const APPROVAL_CLIENT_ROUTES: {
|
|
12
|
-
readonly pending: "/api/dsh-evolution/approval/pending";
|
|
13
|
-
readonly approve: "/api/dsh-evolution/approval/approve";
|
|
14
|
-
readonly reject: "/api/dsh-evolution/approval/reject";
|
|
15
|
-
readonly preview: "/api/dsh-evolution/approval/preview";
|
|
16
|
-
};
|
|
17
|
-
//# sourceMappingURL=approval-routes.d.ts.map
|