dsh-plugin-upgrade 2.0.0 → 2.0.2
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/AGENTS.md +74 -43
- package/CHANGELOG.md +58 -0
- package/README-es.md +71 -55
- package/README-hi.md +66 -50
- package/README-pt.md +71 -55
- package/README-zh.md +66 -50
- package/README.md +66 -50
- package/THIRD_PARTY_NOTICES.md +2 -2
- package/docs/EVIDENCE.md +69 -0
- package/lib/route.mjs +20 -4
- package/lib/scan-0.1.6.mjs +39 -4
- package/package.json +11 -8
- package/scripts/verify-artifacts.mjs +3 -2
- package/skills/plugin-upgrade/SKILL.md +12 -12
- package/skills/plugin-upgrade/references/v0.1.5-rc.2-to-v0.1.6-alpha.2.md +2 -2
package/lib/route.mjs
CHANGED
|
@@ -39,7 +39,11 @@ export const CORRIDORS = [
|
|
|
39
39
|
},
|
|
40
40
|
]
|
|
41
41
|
|
|
42
|
-
/**
|
|
42
|
+
/**
|
|
43
|
+
* Accepts a corridor id ('legC'), a full span, or a bare mention of a line.
|
|
44
|
+
* @param {string} id - the `--span` value, a corridor id, or a bare line mention.
|
|
45
|
+
* @returns {Corridor | undefined} the matching corridor, or undefined when nothing matches.
|
|
46
|
+
*/
|
|
43
47
|
export function corridorById(id) {
|
|
44
48
|
if (typeof id !== 'string' || id.length === 0) return undefined
|
|
45
49
|
const wanted = id.trim().toLowerCase()
|
|
@@ -90,7 +94,11 @@ export function resolveCorridor(input = {}) {
|
|
|
90
94
|
return CORRIDORS.find(c => c.id === 'legAB')
|
|
91
95
|
}
|
|
92
96
|
|
|
93
|
-
/**
|
|
97
|
+
/**
|
|
98
|
+
* Reads `--span <value>` out of an argv array without touching the other flags.
|
|
99
|
+
* @param {string[]} argv - the raw CLI argument list (no `node`/script prefix).
|
|
100
|
+
* @returns {string | undefined} the span value, or undefined when the flag is absent.
|
|
101
|
+
*/
|
|
94
102
|
export function spanFromArgv(argv) {
|
|
95
103
|
const index = argv.findIndex(a => a === '--span')
|
|
96
104
|
if (index >= 0 && typeof argv[index + 1] === 'string' && !argv[index + 1].startsWith('--')) return argv[index + 1]
|
|
@@ -98,7 +106,11 @@ export function spanFromArgv(argv) {
|
|
|
98
106
|
return inline ? inline.slice('--span='.length) : undefined
|
|
99
107
|
}
|
|
100
108
|
|
|
101
|
-
/**
|
|
109
|
+
/**
|
|
110
|
+
* Reads `--repo <value>` (or `--repo=<value>`) out of an argv array.
|
|
111
|
+
* @param {string[]} argv - the raw CLI argument list (no `node`/script prefix).
|
|
112
|
+
* @returns {string | undefined} the repo path, or undefined when the flag is absent.
|
|
113
|
+
*/
|
|
102
114
|
export function repoFromArgv(argv) {
|
|
103
115
|
const index = argv.findIndex(a => a === '--repo')
|
|
104
116
|
if (index >= 0 && typeof argv[index + 1] === 'string') return argv[index + 1]
|
|
@@ -106,7 +118,11 @@ export function repoFromArgv(argv) {
|
|
|
106
118
|
return inline ? inline.slice('--repo='.length) : undefined
|
|
107
119
|
}
|
|
108
120
|
|
|
109
|
-
/**
|
|
121
|
+
/**
|
|
122
|
+
* Loads the catalog module the corridor points at.
|
|
123
|
+
* @param {Corridor} corridor - the corridor whose `catalog` path is imported.
|
|
124
|
+
* @returns {Promise<any>} the catalog module namespace (`main`, `SEAMS`, `render`, …). `any` is the honest type here: the specifier is a runtime string, so the namespace shape is not knowable statically.
|
|
125
|
+
*/
|
|
110
126
|
export async function loadCatalog(corridor) {
|
|
111
127
|
return import(new URL(corridor.catalog, import.meta.url).href)
|
|
112
128
|
}
|
package/lib/scan-0.1.6.mjs
CHANGED
|
@@ -75,7 +75,17 @@ const SCAN_EXT = /\.(ts|tsx|mts|cts|mjs|cjs|js|jsx|json|yml|yaml)$/
|
|
|
75
75
|
* @property {(line: string) => boolean} [lineFilter]
|
|
76
76
|
* @property {(lines: string[], i: number) => boolean} [windowFilter]
|
|
77
77
|
* @property {(text: string, file: string) => boolean} [fileCheck]
|
|
78
|
-
* @property {(text: string
|
|
78
|
+
* @property {(text: string) => boolean} [downgradeIf] file-level guard recognition
|
|
79
|
+
*/
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* @typedef {object} Hit
|
|
83
|
+
* @property {string} seam
|
|
84
|
+
* @property {string} severity
|
|
85
|
+
* @property {string} file
|
|
86
|
+
* @property {number} line
|
|
87
|
+
* @property {string} snippet
|
|
88
|
+
* @property {string} detail
|
|
79
89
|
*/
|
|
80
90
|
|
|
81
91
|
/** @type {Seam[]} */
|
|
@@ -145,6 +155,12 @@ const STRUCTURED = new Set(['E2'])
|
|
|
145
155
|
*/
|
|
146
156
|
export const CARD_ONLY = SEAMS.filter(s => s.test === null && !STRUCTURED.has(s.id)).map(s => s.id)
|
|
147
157
|
|
|
158
|
+
/**
|
|
159
|
+
* Yield every scannable file under `dir`, depth-limited and read-only.
|
|
160
|
+
* @param {string} dir
|
|
161
|
+
* @param {number} [depth]
|
|
162
|
+
* @returns {Generator<string, void, void>}
|
|
163
|
+
*/
|
|
148
164
|
function* walk(dir, depth = 0) {
|
|
149
165
|
if (depth > 8) return
|
|
150
166
|
let ents
|
|
@@ -159,7 +175,12 @@ function* walk(dir, depth = 0) {
|
|
|
159
175
|
}
|
|
160
176
|
}
|
|
161
177
|
|
|
162
|
-
/**
|
|
178
|
+
/**
|
|
179
|
+
* 1-based line number of the first line containing `needle`, or 1.
|
|
180
|
+
* @param {string} text
|
|
181
|
+
* @param {string} needle
|
|
182
|
+
* @returns {number}
|
|
183
|
+
*/
|
|
163
184
|
function lineOf(text, needle) {
|
|
164
185
|
const lines = text.split(/\r?\n/)
|
|
165
186
|
const i = lines.findIndex(l => l.includes(needle))
|
|
@@ -172,6 +193,9 @@ function lineOf(text, needle) {
|
|
|
172
193
|
* registration call after it. `register()` return values are flagged regardless
|
|
173
194
|
* of whether they reach `ctx.effect()`: the card instructs the author to hand
|
|
174
195
|
* them to the effect.
|
|
196
|
+
* @param {string} text
|
|
197
|
+
* @param {string} file
|
|
198
|
+
* @returns {Hit[]}
|
|
175
199
|
*/
|
|
176
200
|
function checkAsyncApply(text, file) {
|
|
177
201
|
const lines = text.split(/\r?\n/)
|
|
@@ -224,7 +248,7 @@ function checkAsyncApply(text, file) {
|
|
|
224
248
|
* Scan one repo.
|
|
225
249
|
* @param {string} repoDir
|
|
226
250
|
* @param {{ seams?: string[] }} [options]
|
|
227
|
-
* @returns {{ repo: string, scannedAt: string, files: number, hits:
|
|
251
|
+
* @returns {{ repo: string, scannedAt: string, files: number, hits: Hit[], bySeam: Record<string, number> }}
|
|
228
252
|
*/
|
|
229
253
|
export function scanRepo(repoDir, options = {}) {
|
|
230
254
|
const wanted = options.seams && options.seams.length ? new Set(options.seams) : null
|
|
@@ -257,12 +281,17 @@ export function scanRepo(repoDir, options = {}) {
|
|
|
257
281
|
}
|
|
258
282
|
if (!wanted || wanted.has('E2')) hits.push(...checkAsyncApply(text, file))
|
|
259
283
|
}
|
|
284
|
+
/** @type {Record<string, number>} */
|
|
260
285
|
const bySeam = {}
|
|
261
286
|
for (const h of hits) bySeam[h.seam] = (bySeam[h.seam] || 0) + 1
|
|
262
287
|
return { repo: repoDir, scannedAt: new Date().toISOString(), files, hits, bySeam }
|
|
263
288
|
}
|
|
264
289
|
|
|
265
|
-
/**
|
|
290
|
+
/**
|
|
291
|
+
* Human-readable rendering. Order derives from the catalog, never a copy.
|
|
292
|
+
* @param {{ repo: string, files: number, hits: Hit[] }} report
|
|
293
|
+
* @returns {string}
|
|
294
|
+
*/
|
|
266
295
|
export function render(report) {
|
|
267
296
|
const L = []
|
|
268
297
|
L.push(`# scan-0.1.6 · ${report.repo}`)
|
|
@@ -286,7 +315,13 @@ export function render(report) {
|
|
|
286
315
|
return L.join('\n')
|
|
287
316
|
}
|
|
288
317
|
|
|
318
|
+
/**
|
|
319
|
+
* CLI entry point.
|
|
320
|
+
* @param {string[]} argv
|
|
321
|
+
* @returns {number} the process exit code.
|
|
322
|
+
*/
|
|
289
323
|
export function main(argv) {
|
|
324
|
+
/** @type {{ repo: string, json: string | null, seams: string[] | null, quiet: boolean }} */
|
|
290
325
|
const args = { repo: process.cwd(), json: null, seams: null, quiet: false }
|
|
291
326
|
for (let i = 0; i < argv.length; i++) {
|
|
292
327
|
const a = argv[i]
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-plugin-upgrade",
|
|
3
|
-
"version": "2.0.
|
|
4
|
-
"description": "Plugin-author upgrade skill for DeepSeek Harness:
|
|
3
|
+
"version": "2.0.2",
|
|
4
|
+
"description": "Plugin-author upgrade skill for DeepSeek Harness: one package, one corridor index - the scanner detects the caller peer band and routes to the matching closed corridor card (0.1.3-alpha.1 -> 0.1.5-rc.1 as legs A+B, and 0.1.5-rc.2 -> 0.1.6-alpha.2 as leg C), with a zero-dependency seam scanner shipped as a bundle skill and an npx CLI.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/PerryLink/dsh-plugin-upgrade.git"
|
|
@@ -77,14 +77,16 @@
|
|
|
77
77
|
"compatibility": {
|
|
78
78
|
"dshVersions": [
|
|
79
79
|
"0.1.2-rc.1",
|
|
80
|
-
"0.1.5-rc.2"
|
|
80
|
+
"0.1.5-rc.2",
|
|
81
|
+
"0.1.6-alpha.2",
|
|
82
|
+
"0.1.7-alpha.2"
|
|
81
83
|
]
|
|
82
84
|
},
|
|
83
85
|
"capability": {
|
|
84
86
|
"id": "plugin-upgrade",
|
|
85
87
|
"kind": "service",
|
|
86
88
|
"invocation": "ctx.skills.get('plugin-upgrade') resolves a complete SkillDefinition",
|
|
87
|
-
"expected": "the session skill catalog lists plugin-upgrade and the loaded body carries the
|
|
89
|
+
"expected": "the session skill catalog lists plugin-upgrade and the loaded body carries the corridor index (lib/route.mjs) with both closed corridor cards - legAB 0.1.3-alpha.1 -> 0.1.5-rc.1 and legC 0.1.5-rc.2 -> 0.1.6-alpha.2 - over a directory resourceBase"
|
|
88
90
|
},
|
|
89
91
|
"evidence": {
|
|
90
92
|
"install": null,
|
|
@@ -112,13 +114,13 @@
|
|
|
112
114
|
"packageManager": "pnpm@11.7.0",
|
|
113
115
|
"peerDependencies": {
|
|
114
116
|
"@deepseek-ai/cordis": "^4.0.2",
|
|
115
|
-
"@deepseek-ai/dsh-skill": ">=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0 || >=0.1.6-0 <0.2.0",
|
|
117
|
+
"@deepseek-ai/dsh-skill": ">=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0 || >=0.1.6-0 <0.2.0 || >=0.1.7-0 <0.2.0",
|
|
116
118
|
"@deepseek-ai/schemastery": "^3.18.2"
|
|
117
119
|
},
|
|
118
120
|
"devDependencies": {
|
|
119
|
-
"@deepseek-ai/cordis": "^4.0.
|
|
120
|
-
"@deepseek-ai/dsh-skill": "0.1.
|
|
121
|
-
"@deepseek-ai/schemastery": "^3.18.
|
|
121
|
+
"@deepseek-ai/cordis": "^4.0.4",
|
|
122
|
+
"@deepseek-ai/dsh-skill": "0.1.7-alpha.2",
|
|
123
|
+
"@deepseek-ai/schemastery": "^3.18.4",
|
|
122
124
|
"@types/node": "^22.0.0",
|
|
123
125
|
"typescript": "^5.9.0"
|
|
124
126
|
},
|
|
@@ -126,6 +128,7 @@
|
|
|
126
128
|
"test": "node --test \"test/*.test.mjs\"",
|
|
127
129
|
"check": "tsc -p tsconfig.check.json",
|
|
128
130
|
"typecheck:ci": "tsc -p tsconfig.check.ci.json",
|
|
131
|
+
"typecheck:checkout": "tsc -p tsconfig.checkout.json --noEmit",
|
|
129
132
|
"scan": "node scripts/scan-0.1.5.mjs",
|
|
130
133
|
"check:readmes": "node scripts/check-readme-sync.mjs",
|
|
131
134
|
"verify:self-contained": "node scripts/verify-self-contained.mjs",
|
|
@@ -61,10 +61,11 @@ try {
|
|
|
61
61
|
failures.push(`packaged entry failed to import: ${detail.slice(0, 200)}`)
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
// The packaged SKILL.md must keep its
|
|
64
|
+
// The packaged SKILL.md must keep its corridor-index frontmatter (both corridors).
|
|
65
65
|
const skill = readFileSync(join(pkgRoot, 'skills/plugin-upgrade/SKILL.md'), 'utf8')
|
|
66
66
|
if (!/^name:\s*plugin-upgrade\s*$/m.test(skill)) failures.push('packaged SKILL.md lost its frontmatter name')
|
|
67
|
-
if (!/^
|
|
67
|
+
if (!/^ corridors:\s*"legAB `0\.1\.3-alpha\.1 -> 0\.1\.5-rc\.1`/m.test(skill)) failures.push('packaged SKILL.md lost its legAB corridor frontmatter')
|
|
68
|
+
if (!/legC `0\.1\.5-rc\.2 -> 0\.1\.6-alpha\.2`/m.test(skill)) failures.push('packaged SKILL.md lost its legC corridor frontmatter')
|
|
68
69
|
|
|
69
70
|
// cordis.patch.yml must stay a top-level YAML ARRAY of loader patch entries:
|
|
70
71
|
// a mapping (`insert:` at column 0) mounts nothing and dsh reports
|
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: plugin-upgrade
|
|
3
|
-
description: Migrate a DeepSeek Harness plugin repo across the
|
|
4
|
-
whenToUse: Use when a DSH plugin must reach the DeepSeek Harness 0.1.5-rc.1 line (or any host line in >=0.1.2-rc.1 <0.2.0) while keeping the older peer band working. Read leg A
|
|
3
|
+
description: Migrate a DeepSeek Harness plugin repo across one of the two closed corridors this package carries. A zero-dependency seam scanner reads the target repo's declared band and routes to the matching corridor, then you walk the fix-and-verify loop. Corridor `legAB` (`0.1.3-alpha.1 -> 0.1.5-rc.1`, 20 seams) holds leg A (V3 session format, assistant/message.stream, SessionHandleReadResult, ctx.agent, Inbox, SubprocessHandle.pid, SystemPrompt persona, PTC rename, EpochHeader.system, and the tsconfig stale-path false green) plus leg B (the removed bare `conversation` client slot, the sidebar textpreview -> documentpreview package rename, the stale-type-line false green, the new global main-panel model and usePanelInfo standard prop, the new `present` tool key, two new session event types, and the peer-range trap). Corridor `legC` (`0.1.5-rc.2 -> 0.1.6-alpha.2`, 5 seams `E1`-`E5`) holds the `agent/created` listener-throw chain, the post-`await` apply race, the removed `settings.plugin.item` slot and `SessionListState.current`, the removed `sessions.open/openSubagent/clear` client API, and the removed model literals.
|
|
4
|
+
whenToUse: Use when a DSH plugin must reach the DeepSeek Harness 0.1.5-rc.1 line (or any host line in >=0.1.2-rc.1 <0.2.0), or the 0.1.6-alpha.2 line, while keeping the older peer band working. Read leg A if the peer band is below 0.1.5-alpha.1 or the target is 0.1.5-alpha.1; read leg B if the target is 0.1.5-rc.1 or the plugin has a client/browser half; read leg C if the target band is 0.1.6-alpha.2 (or the repo already sits on 0.1.5-rc.2). Not for 0.1.1 -> 0.1.2 migrations (use the community convergence skill) and not for the DSH user-facing upgrade/repair path. A hop past every corridor carried here is a new corridor, not a wider card.
|
|
5
5
|
metadata:
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
evidence: "leg A: 2026-09-09 wave over 40 plugin repos · leg B: tag-range diff + public slot/service catalogs re-read 2026-09-10; family workspace sweep (15 repos with a client half, 8 slot keys). Both recorded in docs/EVIDENCE.md"
|
|
6
|
+
corridors: "legAB `0.1.3-alpha.1 -> 0.1.5-rc.1` (leg A S1-S10 + M1 · leg B C1, C2, C4, C5, H1-H4, P1; leg B's C3 is leg A's M1) · legC `0.1.5-rc.2 -> 0.1.6-alpha.2` (E1-E5, all error)"
|
|
7
|
+
host-baseline: "0.1.5-rc.1 (tag dsh-v0.1.5-rc.1 = 183f08e9c6dde7e36cd2318eaee70b0da08fb35e) for legAB; 0.1.6-alpha.2 (tag dsh-v0.1.6-alpha.2) for legC. leg A baseline 0.1.5-alpha.1 (tag dsh-v0.1.5-alpha.1 = 5dda764ed3aa172535a7967b06ff95d9cbfe536a, checkout 19d2e38480)"
|
|
8
|
+
evidence: "leg A: 2026-09-09 wave over 40 plugin repos · leg B: tag-range diff + public slot/service catalogs re-read 2026-09-10; family workspace sweep (15 repos with a client half, 8 slot keys) · leg C: 2026-09-19 sweep, dsh-v0.1.6-alpha.2. All recorded in docs/EVIDENCE.md (sections A, 1-10 and 11)"
|
|
10
9
|
status: "published"
|
|
11
|
-
supersedes: "the version-locked packages dsh-plugin-upgrade (leg A) and dsh-plugin-upgrade-rc1 (leg B)"
|
|
10
|
+
supersedes: "the retired version-locked packages dsh-plugin-upgrade (leg A) and dsh-plugin-upgrade-rc1 (leg B), plus the never-published dsh-plugin-upgrade-016 corridor now folded in as leg C; the two published retired names stay on the registry, deprecated"
|
|
12
11
|
user-invocable: true
|
|
13
12
|
---
|
|
14
13
|
|
|
15
|
-
# Plugin upgrade ·
|
|
14
|
+
# Plugin upgrade · one package, one corridor index (two closed corridors)
|
|
16
15
|
|
|
17
16
|
You are migrating **one plugin repository** across the merged DSH span `0.1.3-alpha.1 → 0.1.5-rc.1`. The goal is not "make typecheck pass" — it is "prove the plugin still works on the target host". Local gates are necessary but not sufficient, and two classes of failure survive a green gate: **stale-type false green** (the local gate compiles an old type line) and **silent non-mount** (the host drops a contribution with no error, no log line and no failed build). Read the leg that matches your peer band; both legs share one scanner and one catalog.
|
|
18
17
|
|
|
@@ -82,7 +81,8 @@ Both legs' frontmatter `whenToUse` were routing hints; they cannot both live in
|
|
|
82
81
|
|
|
83
82
|
## 5. Reference
|
|
84
83
|
|
|
85
|
-
- `./references/v0.1.3-alpha.1-to-v0.1.5-rc.1.md` — the
|
|
86
|
-
- `./
|
|
87
|
-
-
|
|
88
|
-
- `
|
|
84
|
+
- `./references/v0.1.3-alpha.1-to-v0.1.5-rc.1.md` — the `legAB` version card: preamble, **§1 Leg A** (10 seams with host path + commit + minimal fix + regression, and the 4 that no community PR covers yet), **§2 Leg B** (the from→to mapping, the `conversation` → `main.conversation` rewrite recipe, the boundary list), **§3 the 20-seam merged index**.
|
|
85
|
+
- `./references/v0.1.5-rc.2-to-v0.1.6-alpha.2.md` — the `legC` version card: what breaks across `0.1.5-rc.2 → 0.1.6-alpha.2` (all silent or runtime-only), the five-seam index `E1`–`E5` with evidence and fix/verify recipes, and the family-side facts. `CARD_ONLY = []` there: every `legC` seam has a detector.
|
|
86
|
+
- `./scripts/scan-0.1.5.mjs` — the router plus detector: it resolves the corridor through `lib/route.mjs` (declared band, or `--span legAB|legC|span`) and hands the same argv to that corridor's own catalog `main()` — `lib/scan.mjs` for `legAB`, `lib/scan-0.1.6.mjs` for `legC`. Zero dependency, `file:line` output, exit `1` on error-severity hits, `--seams` to filter. The file name is historical; it ships inside the skill directory so relative paths resolve.
|
|
87
|
+
- The package's own test suite (`node --test`) — synthetic bad/good fixtures **for both `legAB` legs** (`fixtures/leg-a-*` for leg A, `fixtures/bad-repo` / `fixtures/good-repo` for leg B), a card↔catalog id-and-severity parity gate **for each corridor**, and a live negative on a family repo.
|
|
88
|
+
- `docs/EVIDENCE.md` — the command→output record behind every card claim: §A is leg A's provenance, §1–§10 the leg-B records, §11 the `legC` records.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# 版本卡 · `0.1.5-rc.2` → `0.1.6-alpha.2`
|
|
2
2
|
|
|
3
|
-
> 本卡只对 **`0.1.5-rc.2 → 0.1.6-alpha.2`**
|
|
4
|
-
>
|
|
3
|
+
> 本卡只对 **`0.1.5-rc.2 → 0.1.6-alpha.2`** 这一个封闭走廊负责。走廊方法论不变:**走廊永不加宽**——一条走廊一张卡、一套证据、一套 fixture 与一个回滚点;绝不把卡撑成「所有版本」。
|
|
4
|
+
> **归属(2026-09-19 业主决定,已取代旧规则)**:本走廊与上一个走廊(`0.1.3-alpha.1 → 0.1.5-rc.1`,`legAB`)现在由**同一个包** `dsh-plugin-upgrade` 通过走廊索引(`lib/route.mjs`)承担,本卡是它的第二条索引行(`legC`)。旧措辞「一个 hop 加一条接缝就是一个新包」与 `dsh-plugin-upgrade-016` 这个名字都已作废,`-016` 从未发布到 registry。两条走廊**不共享 SEAMS 清单**——本卡五条接缝与旧目录零交集,各自有独立的目录模块与 parity 门禁。
|
|
5
5
|
|
|
6
6
|
## 1. 这个跨度里什么会坏(全部是**静默**或**运行时**失效)
|
|
7
7
|
|