dsh-win-pwsh7 0.2.5 → 0.2.6
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/CHANGELOG.md +25 -0
- package/README.en.md +5 -2
- package/README.md +4 -2
- package/package.json +3 -3
- package/tools/check-host.mjs +21 -0
- package/tools/check-native-contracts.mjs +48 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.6 — loads on DSH 0.2.2-rc.1 again
|
|
4
|
+
|
|
5
|
+
- **The plugin had gone silently inert on the 0.2.2 desktop build.** 0.2.5 declared
|
|
6
|
+
`engines.dsh: ">=0.2.1-rc.3 <0.2.2-0"`. The loader skips a bundle whose range the running build
|
|
7
|
+
does not satisfy, prints nothing, and reports it only as an entry in `skippedBundles`, so on
|
|
8
|
+
DSH 0.2.2-rc.1 both tools, the guard, the page and the UTF-8 console bridge were absent. The
|
|
9
|
+
only visible trace was `patch: entry "win-pwsh7" not found` on every recomposition, because the
|
|
10
|
+
bundle patch that inserts that row never applied.
|
|
11
|
+
- `engines.dsh` is now `>=0.2.1-rc.3 <0.3.0-0` and `dsh.verifiedAgainst` is `0.2.2-rc.1`. The
|
|
12
|
+
upper bound follows the **minor** line: DSH ships fixes as prereleases of the next patch, so a
|
|
13
|
+
bound on the next patch's prerelease is a bound on "the next DSH update".
|
|
14
|
+
- `tools/check-host.mjs` now fails when the range ends at a patch prerelease
|
|
15
|
+
(`<X.Y.Z-0`) instead of `<X.<minor+1>.0-0`; the check was mutation-tested by restoring the old
|
|
16
|
+
range (red) and putting the new one back (green).
|
|
17
|
+
- `tools/check-native-contracts.mjs` follows the 0.2.2 split of `dsh-tools`: it accepts
|
|
18
|
+
`checkGuards` (0.2.2) or `guardReason` (0.2.1), reads the `Error: <reason>` materialization from
|
|
19
|
+
`lib/pipeline.js` when that file exists, asserts the pre-execute waterfall still runs **before**
|
|
20
|
+
the guards, and drops `ToolRuntime.prepareExecution` (removed upstream; this plugin never used it).
|
|
21
|
+
Green on 0.2.2-rc.1 in 37 checks.
|
|
22
|
+
- `docs/native-contracts.md` records the new baseline, the per-package delta from 0.2.1-rc.4 and
|
|
23
|
+
the new guard symbols. `dsh-tool-pwsh` is byte-identical; `dsh-pwsh-local` only adds
|
|
24
|
+
`PYTHONIOENCODING: utf-8`, so `ENCODING_PREAMBLE` still carries no `chcp` and the UTF-8 bridge
|
|
25
|
+
stays.
|
|
26
|
+
- No tool, prompt, install, gate or page behavior changed.
|
|
27
|
+
|
|
3
28
|
## 0.2.5 — transitional Windows UTF-8 console bridge
|
|
4
29
|
|
|
5
30
|
- Added `lib/utf8-console-bridge.js`, a removable layer that wraps the same `ctx.shell.execute`
|
package/README.en.md
CHANGED
|
@@ -18,7 +18,10 @@ default executor then chooses the runtime itself, and the plugin still never wri
|
|
|
18
18
|
## Verified environment
|
|
19
19
|
|
|
20
20
|
- Windows 10/11.
|
|
21
|
-
- Verified on DSH **0.2.
|
|
21
|
+
- Verified on DSH **0.2.2-rc.1** (see `dsh.verifiedAgainst` in `package.json`).
|
|
22
|
+
- Supported range: `>=0.2.1-rc.3 <0.3.0-0`. Ending the range at the next prerelease makes the
|
|
23
|
+
loader skip this bundle silently after every DSH patch release (no tools, no gate, no UTF-8
|
|
24
|
+
bridge), so the upper bound follows the **minor** line.
|
|
22
25
|
- The script path needs Microsoft Store; the manual path only needs the Store page.
|
|
23
26
|
- No administrator rights are required.
|
|
24
27
|
|
|
@@ -44,7 +47,7 @@ multi-session validation passed, so the switch is default-on again.
|
|
|
44
47
|
## Install this plugin
|
|
45
48
|
|
|
46
49
|
```powershell
|
|
47
|
-
dsh plugin --profile desktop add github:XinSpark/dsh-win-pwsh7#v0.2.
|
|
50
|
+
dsh plugin --profile desktop add github:XinSpark/dsh-win-pwsh7#v0.2.6
|
|
48
51
|
# or
|
|
49
52
|
dsh plugin --profile desktop add dsh-win-pwsh7
|
|
50
53
|
```
|
package/README.md
CHANGED
|
@@ -13,7 +13,9 @@
|
|
|
13
13
|
## 验证环境
|
|
14
14
|
|
|
15
15
|
- Windows 10/11。
|
|
16
|
-
- 已在 DSH **0.2.
|
|
16
|
+
- 已在 DSH **0.2.2-rc.1** 上验证(见 `package.json` 的 `dsh.verifiedAgainst`)。
|
|
17
|
+
- 支持范围 `>=0.2.1-rc.3 <0.3.0-0`:收紧到"下一个预发布"会让 loader 在 DSH 每次小版本更新后**静默跳过**本插件
|
|
18
|
+
(不装、不报错、`pwsh` 失去 gate 和 UTF-8 过渡层),所以上界按**次版本**划。
|
|
17
19
|
- 脚本安装需要 Microsoft Store 可用;手动安装只需要能打开 Store 页面。
|
|
18
20
|
- 不需要管理员权限。
|
|
19
21
|
|
|
@@ -36,7 +38,7 @@
|
|
|
36
38
|
|
|
37
39
|
```powershell
|
|
38
40
|
# 从 GitHub 安装(推荐锁定 tag)
|
|
39
|
-
dsh plugin --profile desktop add github:XinSpark/dsh-win-pwsh7#v0.2.
|
|
41
|
+
dsh plugin --profile desktop add github:XinSpark/dsh-win-pwsh7#v0.2.6
|
|
40
42
|
# 或 npm
|
|
41
43
|
dsh plugin --profile desktop add dsh-win-pwsh7
|
|
42
44
|
```
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-win-pwsh7",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Detect and validate PowerShell 7 for the Harness pwsh tool on Windows, and install it on request through the Microsoft Store — scripted or manual — while the pwsh tool stays blocked until a PowerShell 7 runtime actually resolves.",
|
|
6
6
|
"engines": {
|
|
7
|
-
"dsh": ">=0.2.1-rc.3 <0.
|
|
7
|
+
"dsh": ">=0.2.1-rc.3 <0.3.0-0"
|
|
8
8
|
},
|
|
9
9
|
"license": "MIT",
|
|
10
10
|
"icon": "./icon.svg",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"prepublishOnly": "npm run check"
|
|
58
58
|
},
|
|
59
59
|
"dsh": {
|
|
60
|
-
"verifiedAgainst": "0.2.
|
|
60
|
+
"verifiedAgainst": "0.2.2-rc.1",
|
|
61
61
|
"bundle": {
|
|
62
62
|
"patch": "./cordis.patch.yml"
|
|
63
63
|
},
|
package/tools/check-host.mjs
CHANGED
|
@@ -830,6 +830,27 @@ check('client copy: every t() key is defined in both dictionaries', () => {
|
|
|
830
830
|
}
|
|
831
831
|
})
|
|
832
832
|
|
|
833
|
+
// --- Manifest: the DSH range must not end at the next prerelease ---------------------------
|
|
834
|
+
//
|
|
835
|
+
// 0.2.5 declared `>=0.2.1-rc.3 <0.2.2-0` and the desktop moved to 0.2.2-rc.1: the loader skips a
|
|
836
|
+
// bundle whose `engines.dsh` the running build does not satisfy, prints nothing, and the whole
|
|
837
|
+
// plugin disappears (no tools, no gate, no UTF-8 bridge) until a new release ships. DSH patches
|
|
838
|
+
// arrive as prereleases, so a bound on the next patch's prerelease is a bound on "the next DSH
|
|
839
|
+
// update". The range therefore ends at the next minor: `<X.<minor+1>.0-0`.
|
|
840
|
+
|
|
841
|
+
check('engines.dsh ends at the next minor, not at the next prerelease', () => {
|
|
842
|
+
const manifest = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'))
|
|
843
|
+
const [major, minor] = manifest.dsh.verifiedAgainst.split('.').map((part) => Number.parseInt(part, 10))
|
|
844
|
+
const terms = manifest.engines.dsh.trim().split(/\s+/u)
|
|
845
|
+
const upper = terms.find((term) => term.startsWith('<'))
|
|
846
|
+
assert.equal(
|
|
847
|
+
upper,
|
|
848
|
+
`<${major}.${minor + 1}.0-0`,
|
|
849
|
+
'a `<X.Y.Z-0` bound on the next patch prerelease makes the loader skip this bundle after the next DSH release',
|
|
850
|
+
)
|
|
851
|
+
assert.ok(terms.some((term) => term.startsWith('>=')), 'the range needs a lower bound')
|
|
852
|
+
})
|
|
853
|
+
|
|
833
854
|
// --- Verdict -----------------------------------------------------------------------------
|
|
834
855
|
|
|
835
856
|
if (failures.length > 0) {
|
|
@@ -72,6 +72,32 @@ function source(name) {
|
|
|
72
72
|
throw new Error(`${name}: neither lib/types/index.js nor lib/index.js exists`)
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Read one named file of an installed package, or '' when the build does not have it.
|
|
77
|
+
*
|
|
78
|
+
* A contract can move between files without being lost: 0.2.2 moved the tool pipeline out of
|
|
79
|
+
* `lib/index.js` into `lib/pipeline.js`. Reading a fixed list and joining what exists keeps the
|
|
80
|
+
* assertion on the behavior when the file split changes, instead of on a remembered path.
|
|
81
|
+
*/
|
|
82
|
+
function trySource(name, relative) {
|
|
83
|
+
try {
|
|
84
|
+
return readFileSync(join(packageDir(name), relative), 'utf8')
|
|
85
|
+
} catch {
|
|
86
|
+
return ''
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** The tool pipeline: one file up to 0.2.1, split into `lib/pipeline.js` from 0.2.2. */
|
|
91
|
+
function pipeline() {
|
|
92
|
+
return ['lib/index.js', 'lib/pipeline.js'].map((relative) => trySource('dsh-tools', relative)).join('\n')
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** The single file that hosts the pre-execute waterfall in this build. */
|
|
96
|
+
function pipelineFile() {
|
|
97
|
+
const split = trySource('dsh-tools', 'lib/pipeline.js')
|
|
98
|
+
return split === '' ? trySource('dsh-tools', 'lib/index.js') : split
|
|
99
|
+
}
|
|
100
|
+
|
|
75
101
|
let checks = 0
|
|
76
102
|
/** One assertion with a stable label for the summary. */
|
|
77
103
|
function check(label, assertion) {
|
|
@@ -109,11 +135,30 @@ async function run() {
|
|
|
109
135
|
|
|
110
136
|
// ---- tool registry: guard is global, monotonic, and lands as an error --
|
|
111
137
|
check('defineTool is exported', () => assert.equal(typeof tools.defineTool, 'function'))
|
|
112
|
-
for (const method of ['guard', '
|
|
138
|
+
for (const method of ['guard', 'restrict', 'execute']) {
|
|
113
139
|
check(`ToolRuntime.${method} exists`, () => assert.equal(typeof tools.ToolRuntime.prototype[method], 'function'))
|
|
114
140
|
}
|
|
115
|
-
|
|
116
|
-
|
|
141
|
+
// 0.2.1 read guards in `guardReason`; 0.2.2 renamed it `checkGuards` and moved the one who
|
|
142
|
+
// materializes the denial into `lib/pipeline.js`. The plugin only needs "guards are still read
|
|
143
|
+
// once, after pre-execute, and the reason reaches the model" — not a particular private name.
|
|
144
|
+
const GUARD_READERS = ['checkGuards', 'guardReason']
|
|
145
|
+
const guardReader = GUARD_READERS.find((name) => typeof tools.ToolRuntime.prototype[name] === 'function')
|
|
146
|
+
check('ToolRuntime reads its guards through one method', () => {
|
|
147
|
+
assert.ok(
|
|
148
|
+
guardReader !== undefined,
|
|
149
|
+
`neither ${GUARD_READERS.join(' nor ')} exists — the gate cannot tell whether guards still deny`,
|
|
150
|
+
)
|
|
151
|
+
})
|
|
152
|
+
check('a guard denial is materialized as `Error: <reason>`', () => {
|
|
153
|
+
assert.match(pipeline(), /Error: \$\{denialReason\}/u)
|
|
154
|
+
assert.ok(pipeline().includes(`.${guardReader}(`), `the denial pipeline never calls ${guardReader}`)
|
|
155
|
+
})
|
|
156
|
+
check('guards are read after the pre-execute waterfall', () => {
|
|
157
|
+
const text = pipelineFile()
|
|
158
|
+
const waterfall = text.search(/tools\/pre-execute/u)
|
|
159
|
+
const read = text.search(new RegExp(`${guardReader}\\(`, 'u'))
|
|
160
|
+
assert.ok(waterfall >= 0, 'the pre-execute waterfall is gone; the gate cannot be refreshed before a pwsh call')
|
|
161
|
+
assert.ok(read > waterfall, 'guards run before the pre-execute waterfall, so a stale reading would decide the call')
|
|
117
162
|
})
|
|
118
163
|
check('restrict refuses a plain-context caller', () => {
|
|
119
164
|
assert.match(source('dsh-tools'), /tools\.restrict\(\) requires a scoped context/u)
|