dsh-ensp 0.7.0 → 0.8.0
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.en.md +86 -16
- package/README.md +85 -15
- package/docs/ENSP-FORMAT.md +8 -8
- package/docs/SCENARIOS.md +332 -286
- package/docs/UI-ANCHORS.md +21 -0
- package/docs/plugin-detail.en.md +2 -0
- package/docs/plugin-detail.zh.md +2 -0
- package/package.json +2 -4
- package/scripts/gen-scenarios-doc.mjs +7 -3
- package/scripts/survey-topologies.mjs +15 -6
- package/src/cli/ensp.mjs +252 -28
- package/src/core/acceptance.mjs +258 -53
- package/src/core/build.mjs +15 -1
- package/src/core/compat.mjs +249 -17
- package/src/core/console.mjs +4 -0
- package/src/core/doctor.mjs +9 -12
- package/src/core/docx.mjs +359 -0
- package/src/core/index.mjs +28 -9
- package/src/core/labconfig.mjs +98 -0
- package/src/core/labs.mjs +12 -12
- package/src/core/launcher.mjs +96 -37
- package/src/core/models.mjs +27 -18
- package/src/core/ooxml.mjs +430 -0
- package/src/core/paths.mjs +59 -10
- package/src/core/presets.mjs +9 -5
- package/src/core/report.mjs +411 -0
- package/src/dsh/index.mjs +626 -652
- package/src/mcp/server.mjs +213 -356
- package/src/tools.mjs +271 -0
- package/screenshots.json +0 -3
- package/scripts/gen-knowledge.mjs +0 -196
- package/src/core/knowledge.json +0 -959
- package/src/core/knowledge.mjs +0 -103
package/src/core/knowledge.mjs
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Curriculum knowledge attached to scenarios.
|
|
3
|
-
*
|
|
4
|
-
* The scenario presets say what to build and in what order. They deliberately do
|
|
5
|
-
* not carry commands, because the whole point of the tool is to catch a copied
|
|
6
|
-
* command that does not fit. What they were missing is the vocabulary the lab is
|
|
7
|
-
* teaching: which LSA belongs to which area, which attribute wins a BGP decision,
|
|
8
|
-
* which port role only MSTP has. Short, checkable statements about those terms
|
|
9
|
-
* are what a learner needs before touching a device.
|
|
10
|
-
*
|
|
11
|
-
* This module reads a small generated index: one topic, the scenarios that teach
|
|
12
|
-
* it, and a list of short question/answer pairs. Shipping only the index keeps the
|
|
13
|
-
* package small and keeps third-party source material out of a GPL distribution,
|
|
14
|
-
* while `npm run knowledge` still makes every entry reproducible.
|
|
15
|
-
*
|
|
16
|
-
* @module dsh-ensp/knowledge
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
import { readFileSync } from 'node:fs'
|
|
20
|
-
|
|
21
|
-
let cache = null
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Load the generated index, once.
|
|
25
|
-
*
|
|
26
|
-
* Read through `new URL` so the path resolves from this module rather than from
|
|
27
|
-
* whatever directory the caller happens to run in.
|
|
28
|
-
*
|
|
29
|
-
* @returns {object}
|
|
30
|
-
*/
|
|
31
|
-
function catalogue() {
|
|
32
|
-
if (cache) return cache
|
|
33
|
-
const text = readFileSync(new URL('./knowledge.json', import.meta.url), 'utf8')
|
|
34
|
-
cache = JSON.parse(text)
|
|
35
|
-
return cache
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/** @returns {string[]} the topics the index covers. */
|
|
39
|
-
export function knowledgeTopics() {
|
|
40
|
-
return Object.keys(catalogue().topics).sort()
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Knowledge points for one scenario.
|
|
45
|
-
*
|
|
46
|
-
* A topic can feed several scenarios: `ospf` covers six of them, and each gets the
|
|
47
|
-
* same points. That repetition is intentional. A learner running the multi-area
|
|
48
|
-
* lab should not have to know that the material also sits under another scene.
|
|
49
|
-
*
|
|
50
|
-
* @param {string} scenarioId
|
|
51
|
-
* @returns {Array<{topic: string, n: number, q: string, a: string}>}
|
|
52
|
-
*/
|
|
53
|
-
export function knowledgeFor(scenarioId) {
|
|
54
|
-
const out = []
|
|
55
|
-
for (const [topic, entry] of Object.entries(catalogue().topics)) {
|
|
56
|
-
if (!entry.scenarios?.includes(scenarioId)) continue
|
|
57
|
-
for (const point of entry.points || []) out.push({ topic, ...point })
|
|
58
|
-
}
|
|
59
|
-
return out
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Search the index by question or answer text.
|
|
64
|
-
*
|
|
65
|
-
* Case-insensitive substring match, same contract as {@link searchScenarios} so the
|
|
66
|
-
* two can be chained in a CLI command without surprising the user.
|
|
67
|
-
*
|
|
68
|
-
* @param {string} query
|
|
69
|
-
* @returns {Array<{topic: string, n: number, q: string, a: string}>}
|
|
70
|
-
*/
|
|
71
|
-
export function searchKnowledge(query) {
|
|
72
|
-
const needle = String(query ?? '').trim().toLowerCase()
|
|
73
|
-
if (!needle) return []
|
|
74
|
-
const out = []
|
|
75
|
-
for (const [topic, entry] of Object.entries(catalogue().topics)) {
|
|
76
|
-
for (const point of entry.points || []) {
|
|
77
|
-
if (`${point.q} ${point.a}`.toLowerCase().includes(needle)) out.push({ topic, ...point })
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
return out
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Render the knowledge section for a scenario brief.
|
|
85
|
-
*
|
|
86
|
-
* Returns an empty string when the scenario has none, so callers can append it
|
|
87
|
-
* unconditionally.
|
|
88
|
-
*
|
|
89
|
-
* @param {string} scenarioId
|
|
90
|
-
* @param {object} [options]
|
|
91
|
-
* @param {number} [options.limit=10]
|
|
92
|
-
* @returns {string}
|
|
93
|
-
*/
|
|
94
|
-
export function renderKnowledge(scenarioId, { limit = 10 } = {}) {
|
|
95
|
-
const points = knowledgeFor(scenarioId).slice(0, limit)
|
|
96
|
-
if (!points.length) return ''
|
|
97
|
-
const lines = ['', '## 知识点(按主题归类)']
|
|
98
|
-
for (const p of points) {
|
|
99
|
-
lines.push(`- ${p.q}`)
|
|
100
|
-
lines.push(` 答: ${p.a}`)
|
|
101
|
-
}
|
|
102
|
-
return lines.join('\n')
|
|
103
|
-
}
|