@lmzhen/dsh-evolution-skill-health 0.30.31 → 0.30.34
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 +38 -3
- package/lib/index.js +29 -5
- package/lib/types/client/Panel.d.ts +32 -1
- package/lib/types/client/api.d.ts +7 -1
- package/lib/types/routes.d.ts +20 -2
- package/package.json +6 -6
package/lib/client.js
CHANGED
|
@@ -257,7 +257,11 @@ window.__ModuleLoader__.load({
|
|
|
257
257
|
ok: true,
|
|
258
258
|
payload: {
|
|
259
259
|
rows: answer.data.rows,
|
|
260
|
-
truncated: answer.data.truncated
|
|
260
|
+
truncated: answer.data.truncated,
|
|
261
|
+
...answer.data.readAt === void 0 ? {} : { readAt: answer.data.readAt },
|
|
262
|
+
...answer.data.scan === void 0 ? {} : { scan: answer.data.scan },
|
|
263
|
+
...answer.data.proposals === void 0 ? {} : { proposals: answer.data.proposals },
|
|
264
|
+
...answer.data.proposalStatus === void 0 ? {} : { proposalStatus: answer.data.proposalStatus }
|
|
261
265
|
}
|
|
262
266
|
};
|
|
263
267
|
}
|
|
@@ -289,8 +293,17 @@ window.__ModuleLoader__.load({
|
|
|
289
293
|
"group.bySkill": "按技能",
|
|
290
294
|
"verdict.ok": "正常",
|
|
291
295
|
"verdict.warn": "警告",
|
|
296
|
+
"verdict.needs-restructure": "需重构",
|
|
292
297
|
"verdict.error": "错误",
|
|
293
298
|
"verdict.unreadable": "不可读",
|
|
299
|
+
"group.proposals": "语义提议",
|
|
300
|
+
"proposal.unavailable": "提议源未挂载;这不是「没有可拆项」。",
|
|
301
|
+
"proposal.outcome.keep": "保持",
|
|
302
|
+
"proposal.outcome.extract": "提取",
|
|
303
|
+
"proposal.outcome.mixed": "混合",
|
|
304
|
+
"proposal.outcome.owner-gap": "owner 缺口",
|
|
305
|
+
"proposal.owner.covered": "有覆盖 owner",
|
|
306
|
+
"proposal.owner.owner-gap": "无覆盖 owner",
|
|
294
307
|
"counts": "{ok} 正常 · {warn} 警告 · {error} 错误 · {unreadable} 不可读",
|
|
295
308
|
"col.rule": "规则",
|
|
296
309
|
"col.skills": "涉及技能",
|
|
@@ -301,6 +314,7 @@ window.__ModuleLoader__.load({
|
|
|
301
314
|
"line": "位置:{path}",
|
|
302
315
|
"line.unknown": "位置:未知",
|
|
303
316
|
"unreadable.note": "读不到内容,因此无法判断(不是「通过」)。",
|
|
317
|
+
"scan.truncated": "本次扫描不完整,计数是下界;请刷新后重试。",
|
|
304
318
|
"loading.failed": "读取失败——现在判断不了;点「刷新」重试。",
|
|
305
319
|
"read.failed": "这次没读到数据(下面一行是原始信息):",
|
|
306
320
|
"failure.unauthorized": "需要登录:重新打开这个页面完成登录后再试。",
|
|
@@ -321,8 +335,17 @@ window.__ModuleLoader__.load({
|
|
|
321
335
|
"group.bySkill": "By skill",
|
|
322
336
|
"verdict.ok": "OK",
|
|
323
337
|
"verdict.warn": "Warn",
|
|
338
|
+
"verdict.needs-restructure": "Needs restructure",
|
|
324
339
|
"verdict.error": "Error",
|
|
325
340
|
"verdict.unreadable": "Unreadable",
|
|
341
|
+
"group.proposals": "Semantic proposals",
|
|
342
|
+
"proposal.unavailable": "No proposal source is mounted; this is not \"nothing to extract\".",
|
|
343
|
+
"proposal.outcome.keep": "keep",
|
|
344
|
+
"proposal.outcome.extract": "extract",
|
|
345
|
+
"proposal.outcome.mixed": "mixed",
|
|
346
|
+
"proposal.outcome.owner-gap": "owner-gap",
|
|
347
|
+
"proposal.owner.covered": "covered owner",
|
|
348
|
+
"proposal.owner.owner-gap": "no covering owner",
|
|
326
349
|
"counts": "{ok} ok · {warn} warn · {error} error · {unreadable} unreadable",
|
|
327
350
|
"col.rule": "Rule",
|
|
328
351
|
"col.skills": "Skills",
|
|
@@ -333,6 +356,7 @@ window.__ModuleLoader__.load({
|
|
|
333
356
|
"line": "line: {path}",
|
|
334
357
|
"line.unknown": "line: UNKNOWN",
|
|
335
358
|
"unreadable.note": "The content could not be read, so this cannot be judged (it is not a pass).",
|
|
359
|
+
"scan.truncated": "This scan is incomplete; the counts are a lower bound. Press Refresh to retry.",
|
|
336
360
|
"loading.failed": "Reading failed — this cannot be judged right now; press Refresh to retry.",
|
|
337
361
|
"failure.unauthorized": "Sign-in is required: reopen this page and sign in, then retry.",
|
|
338
362
|
"failure.forbidden": "The platform refused this request (not the owner principal).",
|
|
@@ -413,6 +437,7 @@ window.__ModuleLoader__.load({
|
|
|
413
437
|
const KINDS = [
|
|
414
438
|
"ok",
|
|
415
439
|
"warn",
|
|
440
|
+
"needs-restructure",
|
|
416
441
|
"error",
|
|
417
442
|
"unreadable"
|
|
418
443
|
];
|
|
@@ -435,6 +460,7 @@ window.__ModuleLoader__.load({
|
|
|
435
460
|
function worstVerdict(face, skills) {
|
|
436
461
|
const order = [
|
|
437
462
|
"error",
|
|
463
|
+
"needs-restructure",
|
|
438
464
|
"warn",
|
|
439
465
|
"unreadable",
|
|
440
466
|
"ok"
|
|
@@ -503,7 +529,10 @@ window.__ModuleLoader__.load({
|
|
|
503
529
|
if (outcome.ok) setView({
|
|
504
530
|
...face,
|
|
505
531
|
rows: outcome.payload.rows,
|
|
506
|
-
truncated: outcome.payload.truncated
|
|
532
|
+
truncated: outcome.payload.truncated,
|
|
533
|
+
proposals: outcome.payload.proposals ?? [],
|
|
534
|
+
loaded: true,
|
|
535
|
+
...outcome.payload.proposalStatus === void 0 ? {} : { proposalStatus: outcome.payload.proposalStatus }
|
|
507
536
|
});
|
|
508
537
|
else setFailed(outcome.failure);
|
|
509
538
|
}).catch(() => {
|
|
@@ -522,7 +551,10 @@ window.__ModuleLoader__.load({
|
|
|
522
551
|
"data-role": "stat",
|
|
523
552
|
"data-tone": kind
|
|
524
553
|
}, (0, react.createElement)("div", { "data-role": "stat-value" }, String(counts.get(kind) ?? 0)), (0, react.createElement)("div", { "data-role": "stat-label" }, t("verdict." + kind)))));
|
|
525
|
-
return (0, react.createElement)("section", { className: CSS_TAG_ID }, (0, react.createElement)("p", { "data-role": "hint" }, t("hint")), stats, view.truncated ? (0, react.createElement)("p", { "data-role": "note" }, t("
|
|
554
|
+
return (0, react.createElement)("section", { className: CSS_TAG_ID }, (0, react.createElement)("p", { "data-role": "hint" }, t("hint")), stats, view.truncated ? (0, react.createElement)("p", { "data-role": "note" }, t("scan.truncated")) : null, failed === void 0 ? null : (0, react.createElement)("div", { "data-role": "note" }, failureBlock(t, failed)), view.loaded === true && view.rows.length === 0 ? (0, react.createElement)("p", { "data-role": "note" }, t("empty.noneReported")) : null, view.proposalStatus === "unavailable" && (view.proposals ?? []).length === 0 ? (0, react.createElement)("p", { "data-role": "note" }, t("proposal.unavailable")) : null, (view.proposals ?? []).length > 0 ? (0, react.createElement)("div", { "data-role": "proposals" }, (0, react.createElement)("h3", { "data-role": "group-title" }, t("group.proposals")), ...(view.proposals ?? []).map((proposal) => (0, react.createElement)("p", {
|
|
555
|
+
key: proposal.id,
|
|
556
|
+
"data-role": "proposal"
|
|
557
|
+
}, proposal.source + " — " + t("proposal.outcome." + proposal.outcome) + " · " + (proposal.owner.mode === "covered" ? t("proposal.owner.covered") + (proposal.owner.name === void 0 ? "" : ": " + proposal.owner.name) : t("proposal.owner.owner-gap") + (proposal.owner.neededOwner === void 0 ? "" : ": " + proposal.owner.neededOwner))))) : null, (0, react.createElement)("h3", { "data-role": "group-title" }, t("group.byRule")), ruleTable(view, t), view.rows.filter((row) => row.verdict === "unreadable").map((row) => (0, react.createElement)("p", {
|
|
526
558
|
key: row.name + ":unreadable",
|
|
527
559
|
"data-role": "note"
|
|
528
560
|
}, row.name + " — " + t("unreadable.note"))));
|
|
@@ -566,7 +598,10 @@ window.__ModuleLoader__.load({
|
|
|
566
598
|
component: SkillHealthPanel,
|
|
567
599
|
body: (t) => ({
|
|
568
600
|
rows: [],
|
|
601
|
+
proposals: [],
|
|
569
602
|
truncated: false,
|
|
603
|
+
loaded: false,
|
|
604
|
+
proposalStatus: "unavailable",
|
|
570
605
|
loadRows: rows,
|
|
571
606
|
t
|
|
572
607
|
})
|
package/lib/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { HOST_ROUTES, json, methodNotAllowed, registerHostRoutes } from "@lmzhen/dsh-evolution-core/http-routes";
|
|
2
|
+
import { readProposalReports } from "@lmzhen/dsh-evolution-core";
|
|
2
3
|
//#region lib/types/routes.js
|
|
3
4
|
/**
|
|
4
5
|
* The panel's ONE read-only route: the health rows the browser half renders.
|
|
@@ -27,8 +28,19 @@ function toRouteRows(rows) {
|
|
|
27
28
|
rule: finding.rule,
|
|
28
29
|
count: finding.count,
|
|
29
30
|
line: finding.line ?? null,
|
|
30
|
-
countIsMeaningful: !UNCALIBRATED_RULES.includes(finding.rule)
|
|
31
|
-
|
|
31
|
+
countIsMeaningful: !UNCALIBRATED_RULES.includes(finding.rule),
|
|
32
|
+
...finding.severity === void 0 ? {} : { severity: finding.severity },
|
|
33
|
+
...finding.message === void 0 ? {} : { message: finding.message }
|
|
34
|
+
})),
|
|
35
|
+
...row.dims === void 0 || row.dims === null ? {} : { dims: {
|
|
36
|
+
bodyChars: row.dims.bodyChars ?? null,
|
|
37
|
+
bodyTokens: row.dims.bodyTokens ?? null,
|
|
38
|
+
supportGroups: row.dims.supportGroups ?? 0,
|
|
39
|
+
supportFiles: row.dims.supportFiles ?? null,
|
|
40
|
+
churnPatches: row.dims.churnPatches ?? null,
|
|
41
|
+
churnReads: row.dims.churnReads ?? null,
|
|
42
|
+
stampDensityPerKb: row.dims.stampDensityPerKb ?? null
|
|
43
|
+
} }
|
|
32
44
|
}));
|
|
33
45
|
}
|
|
34
46
|
/**
|
|
@@ -47,11 +59,17 @@ function makeSkillHealthRoutes(deps, _audit) {
|
|
|
47
59
|
if (request.method !== "GET") return methodNotAllowed(["GET"]);
|
|
48
60
|
const curator = deps.getCurator();
|
|
49
61
|
const view = curator?.healthView === void 0 ? void 0 : await curator.healthView();
|
|
62
|
+
const rows = Array.isArray(view) ? toRouteRows(view) : [];
|
|
63
|
+
const proposals = await deps.getProposals?.() ?? [];
|
|
50
64
|
return json(200, {
|
|
51
65
|
ok: true,
|
|
52
66
|
data: {
|
|
53
|
-
rows
|
|
54
|
-
truncated: view === void 0
|
|
67
|
+
rows,
|
|
68
|
+
truncated: view === void 0,
|
|
69
|
+
readAt: Date.now(),
|
|
70
|
+
scan: { status: view === void 0 ? "unavailable" : "complete" },
|
|
71
|
+
proposals,
|
|
72
|
+
proposalStatus: deps.getProposals === void 0 ? "unavailable" : "available"
|
|
55
73
|
}
|
|
56
74
|
});
|
|
57
75
|
}
|
|
@@ -66,7 +84,13 @@ const inject = [];
|
|
|
66
84
|
* @param ctx - the host context; the connection and curator arrive through it.
|
|
67
85
|
*/
|
|
68
86
|
function apply(ctx) {
|
|
69
|
-
registerHostRoutes(ctx, (audit) => makeSkillHealthRoutes({
|
|
87
|
+
registerHostRoutes(ctx, (audit) => makeSkillHealthRoutes({
|
|
88
|
+
getCurator: () => ctx.get("evolutionCurator"),
|
|
89
|
+
getProposals: async () => {
|
|
90
|
+
const io = ctx.get("evolutionIo")?.provider();
|
|
91
|
+
return io === void 0 ? void 0 : readProposalReports(io);
|
|
92
|
+
}
|
|
93
|
+
}, audit));
|
|
70
94
|
}
|
|
71
95
|
//#endregion
|
|
72
96
|
export { apply, inject };
|
|
@@ -16,22 +16,53 @@ export interface HealthFinding {
|
|
|
16
16
|
line: string | null;
|
|
17
17
|
/** False for a rule whose threshold is not calibrated: the rule is named, the count is not shown. */
|
|
18
18
|
countIsMeaningful: boolean;
|
|
19
|
+
severity?: 'warn' | 'error';
|
|
20
|
+
message?: string;
|
|
21
|
+
}
|
|
22
|
+
/** Measured dimensions behind a row; absent when the scan could not read them. */
|
|
23
|
+
export interface HealthDims {
|
|
24
|
+
bodyChars: number | null;
|
|
25
|
+
bodyTokens: number | null;
|
|
26
|
+
supportGroups: number;
|
|
27
|
+
supportFiles: number | null;
|
|
28
|
+
churnPatches: number | null;
|
|
29
|
+
churnReads: number | null;
|
|
30
|
+
stampDensityPerKb: number | null;
|
|
19
31
|
}
|
|
20
32
|
/** One skill row. `readable: false` is its own verdict — never folded into `ok`. */
|
|
21
33
|
export interface HealthRow {
|
|
22
34
|
name: string;
|
|
23
|
-
verdict: 'ok' | 'warn' | 'error' | 'unreadable';
|
|
35
|
+
verdict: 'ok' | 'warn' | 'error' | 'unreadable' | 'needs-restructure';
|
|
24
36
|
findings: HealthFinding[];
|
|
37
|
+
dims?: HealthDims | null;
|
|
38
|
+
}
|
|
39
|
+
/** One semantic proposal as the health lane projects it; never generated by this panel. */
|
|
40
|
+
export interface HealthProposal {
|
|
41
|
+
id: string;
|
|
42
|
+
source: string;
|
|
43
|
+
outcome: 'keep' | 'extract' | 'mixed' | 'owner-gap';
|
|
44
|
+
owner: {
|
|
45
|
+
mode: 'covered' | 'owner-gap';
|
|
46
|
+
name?: string;
|
|
47
|
+
neededOwner?: string;
|
|
48
|
+
};
|
|
49
|
+
status: 'proposed' | 'previewed' | 'stale' | 'dismissed';
|
|
25
50
|
}
|
|
26
51
|
/** What the sidebar hands the component. */
|
|
27
52
|
export interface HealthFace {
|
|
28
53
|
rows: HealthRow[];
|
|
54
|
+
/** Semantic proposals from the one proposer; empty means none were handed in. */
|
|
55
|
+
proposals?: HealthProposal[];
|
|
29
56
|
/** True when the host could not finish the scan: the counts below are then a lower bound. */
|
|
30
57
|
truncated: boolean;
|
|
31
58
|
/** Opens the skill-history panel for one skill, when that panel is present. */
|
|
32
59
|
openHistory?: (name: string) => void;
|
|
33
60
|
/** Reads the scan result from the host route. Absent faces render their seeded rows unchanged. */
|
|
34
61
|
loadRows?: () => Promise<HealthOutcome>;
|
|
62
|
+
/** Whether the host has answered at least once; the seed is not an empty result. */
|
|
63
|
+
loaded?: boolean;
|
|
64
|
+
/** Semantic proposals are a separate lane; unavailable must not read as "nothing to extract". */
|
|
65
|
+
proposalStatus?: 'available' | 'unavailable' | 'stale';
|
|
35
66
|
/** The seat's translator for this bundle's dictionary — the panel never reaches for a service. */
|
|
36
67
|
t: (key: string) => string;
|
|
37
68
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { RouteFailure } from '@lmzhen/dsh-evolution-client-kit/generated/route-client';
|
|
2
|
-
import type { HealthRow } from './Panel.ts';
|
|
2
|
+
import type { HealthProposal, HealthRow } from './Panel.ts';
|
|
3
3
|
/** Route paths: the generated mirror of the host's HOST_ROUTES (one declaration in core). */
|
|
4
4
|
export declare const HOST_ROUTES: {
|
|
5
5
|
readonly rows: "/api/dsh-evolution/skill-health/rows";
|
|
@@ -9,6 +9,12 @@ export interface HealthPayload {
|
|
|
9
9
|
rows: HealthRow[];
|
|
10
10
|
/** True when the host could not finish the scan: the counts below are then a lower bound. */
|
|
11
11
|
truncated: boolean;
|
|
12
|
+
readAt?: number;
|
|
13
|
+
scan?: {
|
|
14
|
+
status: 'complete' | 'unavailable' | 'truncated';
|
|
15
|
+
};
|
|
16
|
+
proposals?: HealthProposal[];
|
|
17
|
+
proposalStatus?: 'available' | 'unavailable' | 'stale';
|
|
12
18
|
}
|
|
13
19
|
/** Either the payload or the classified four-state failure — never a thrown error the panel must guess at. */
|
|
14
20
|
export type HealthOutcome = {
|
package/lib/types/routes.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ConnectionFetchRouteLike, DecisionAudit } from '@lmzhen/dsh-evolution-core/http-routes';
|
|
2
|
+
import type { ProposalView } from '@lmzhen/dsh-evolution-core';
|
|
2
3
|
/** Where the panel asks for its rows. The ONE declaration lives in core (W1). */
|
|
3
4
|
export declare const ROUTES: {
|
|
4
5
|
readonly rows: "/api/dsh-evolution/skill-health/rows";
|
|
@@ -11,12 +12,25 @@ export interface RouteFinding {
|
|
|
11
12
|
count: number;
|
|
12
13
|
line: string | null;
|
|
13
14
|
countIsMeaningful: boolean;
|
|
15
|
+
severity?: 'warn' | 'error';
|
|
16
|
+
message?: string;
|
|
17
|
+
}
|
|
18
|
+
/** The measured dimensions behind a row, for evidence chips; absent when the scan could not read them. */
|
|
19
|
+
export interface RouteDims {
|
|
20
|
+
bodyChars: number | null;
|
|
21
|
+
bodyTokens: number | null;
|
|
22
|
+
supportGroups: number;
|
|
23
|
+
supportFiles: number | null;
|
|
24
|
+
churnPatches: number | null;
|
|
25
|
+
churnReads: number | null;
|
|
26
|
+
stampDensityPerKb: number | null;
|
|
14
27
|
}
|
|
15
28
|
/** One row as the client sees it. `readable: false` becomes its own verdict, never `ok`. */
|
|
16
29
|
export interface RouteRow {
|
|
17
30
|
name: string;
|
|
18
31
|
verdict: string;
|
|
19
32
|
findings: RouteFinding[];
|
|
33
|
+
dims?: RouteDims | null;
|
|
20
34
|
}
|
|
21
35
|
/**
|
|
22
36
|
* Project the curator's health view into what the panel renders.
|
|
@@ -30,10 +44,12 @@ export declare function toRouteRows(rows: ReadonlyArray<{
|
|
|
30
44
|
rule: string;
|
|
31
45
|
count: number;
|
|
32
46
|
line?: string | null;
|
|
47
|
+
severity?: 'warn' | 'error';
|
|
48
|
+
message?: string;
|
|
33
49
|
}>;
|
|
34
|
-
dims?: {
|
|
50
|
+
dims?: (Partial<RouteDims> & {
|
|
35
51
|
readable?: boolean;
|
|
36
|
-
} | null;
|
|
52
|
+
}) | null;
|
|
37
53
|
}>): RouteRow[];
|
|
38
54
|
/**
|
|
39
55
|
* Build the panel's route in the platform's exact FetchRoute shape.
|
|
@@ -46,5 +62,7 @@ export declare function makeSkillHealthRoutes(deps: {
|
|
|
46
62
|
getCurator: () => {
|
|
47
63
|
healthView?: () => Promise<unknown>;
|
|
48
64
|
} | undefined;
|
|
65
|
+
/** The one proposer's read seam; absent means the panel must say unavailable. */
|
|
66
|
+
getProposals?: () => readonly ProposalView[] | undefined | Promise<readonly ProposalView[] | undefined>;
|
|
49
67
|
}, _audit: DecisionAudit): ConnectionFetchRouteLike[];
|
|
50
68
|
//# sourceMappingURL=routes.d.ts.map
|
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
]
|
|
12
12
|
}
|
|
13
13
|
},
|
|
14
|
-
"version": "0.30.
|
|
14
|
+
"version": "0.30.34",
|
|
15
15
|
"publishConfig": {
|
|
16
16
|
"access": "public"
|
|
17
17
|
},
|
|
@@ -42,18 +42,18 @@
|
|
|
42
42
|
"peerDependencies": {
|
|
43
43
|
"@deepseek-ai/cordis": "^4.0.5",
|
|
44
44
|
"react": "^18.2.0",
|
|
45
|
-
"@lmzhen/dsh-evolution-core": "^0.30.
|
|
46
|
-
"@lmzhen/dsh-evolution-curator": "^0.30.
|
|
45
|
+
"@lmzhen/dsh-evolution-core": "^0.30.34",
|
|
46
|
+
"@lmzhen/dsh-evolution-curator": "^0.30.34"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@deepseek-ai/cordis": "^4.0.5",
|
|
50
50
|
"@types/react": "~18.3.1",
|
|
51
51
|
"react": "^18.2.0",
|
|
52
|
-
"@lmzhen/dsh-evolution-core": "^0.30.
|
|
53
|
-
"@lmzhen/dsh-evolution-curator": "^0.30.
|
|
52
|
+
"@lmzhen/dsh-evolution-core": "^0.30.34",
|
|
53
|
+
"@lmzhen/dsh-evolution-curator": "^0.30.34"
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
|
-
"@lmzhen/dsh-evolution-client-kit": "^0.30.
|
|
56
|
+
"@lmzhen/dsh-evolution-client-kit": "^0.30.34"
|
|
57
57
|
},
|
|
58
58
|
"engines": {
|
|
59
59
|
"dsh": ">=0.2.1-rc.2 <0.2.2-0"
|