dsh-win-pwsh7 0.0.0-stage → 0.2.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/CHANGELOG.md ADDED
@@ -0,0 +1,390 @@
1
+ # Changelog
2
+
3
+ ## 0.2.0 — 优化 Store 版 pwsh7 安装方式
4
+
5
+ 优化store版pwsh7安装方式,重构项目前端设计和工程设计,使其符合原生DSH工程习惯和界面风格。
6
+
7
+ - Removed the retired internal `method: 'store'` / winget install route. `pwsh7_install` has only
8
+ the Store-page script path plus the manual path; the tool schema never exposed the winget route.
9
+ - Removed the winget-only job spec, log follower, download measurement, failure-code translation,
10
+ inline fallback, constants, imports, and their test fixtures. The host gate now drives only the
11
+ Store-UI job, the VPN confirmation, the breaker, the disabled-row report, and activation.
12
+ - Deleted `docs/acceptance-0.13.0.md`, which described the retired metered/winget behaviour.
13
+ - READMEs no longer mention `installIfMissing`, the winget installer, or a "configuration card";
14
+ they describe the current two install paths and the status card.
15
+ - Declared `engines.dsh` (`>=0.2.1-rc.3 <0.2.2-0`) so an untested DSH line is rejected at install.
16
+
17
+ ## 0.16.6 — status card matching the official provider card
18
+
19
+ - The status section is now a native bordered card with an inner estimate/facts surface, matching
20
+ the official speech-to-text provider card: card title, three facts (status, runtime, executor),
21
+ footnote, and the compact Re-detect action.
22
+ - The Remote `status` endpoint now also returns the Host gate snapshot, so the card renders facts
23
+ instead of the model-facing report. The client falls back to the report when an older Host
24
+ answers without it.
25
+ - When the gate is present the raw report is no longer shown; the pasteable wiring patch remains
26
+ available in the card.
27
+
28
+ ## 0.16.5 — native plugin detail page
29
+
30
+ - The plugin page's two sections now match the native detail page instead of drawing their own
31
+ cards: flat sections, 14px/500 section titles, the page's 32px section rhythm, 34px native inputs,
32
+ 28px outline actions for Re-detect and Copy, and the native settings-form Save/Reset with matching
33
+ focus rings.
34
+ - The status report is native body text rather than a scrollable log box; the pasteable patch keeps
35
+ a compact native code block and copy action.
36
+ - Removed the stale metered-network warning and its dictionary entries — the feature was already
37
+ gone, but the client still carried its copy.
38
+ - The plugin card/detail description now describes the current behaviour (script or manual Store
39
+ install; no metered preflight).
40
+
41
+ ## 0.16.4 — make the companion recommendation an instruction to the model
42
+
43
+ - The completed script-install verdict now begins the companion text with `ACTION REQUIRED:`: the
44
+ model must tell the user that dsh-win-pwsh7 is best paired with the dsh-ops plugin, offer to install
45
+ it, and not install it without consent. The repository link and install command stay included.
46
+ - READMEs describe it as an instruction, not a passive recommendation.
47
+ - It is still emitted only in the completed script-install result.
48
+
49
+ ## 0.16.3 — link the dsh-ops companion
50
+
51
+ - The completed script-install recommendation now includes the companion repository link:
52
+ https://github.com/T-Auto/dsh-ops.
53
+ - Both READMEs link the same repository.
54
+
55
+ ## 0.16.2 — recommend the companion only after the script install completes
56
+
57
+ - 0.16.1 appended the dsh-ops recommendation to every healthy `pwsh7_status` report. That was too
58
+ broad: the request is a **one-time recommendation to the model after the script install
59
+ completes**.
60
+ - The recommendation now appears only in the successful Store-UI job verdict, after the runtime is
61
+ detected. `pwsh7_status`, manual installs, and the plugin page do not advertise it.
62
+ - READMEs and the host gate assert the narrower behavior.
63
+
64
+ ## 0.16.1 — recommend dsh-ops after a working runtime appears
65
+
66
+ - `pwsh7_status` appends a companion recommendation once a usable PowerShell 7 is detected, and a
67
+ successful Store-UI install includes it in the job verdict: **dsh-ops** — native file/PTC tools
68
+ plus Windows host PowerShell integration — pairs well with this plugin. It is explicitly not
69
+ installed automatically; the suggested command is
70
+ `dsh plugin --profile <profile> add dsh-ops`.
71
+ - Both READMEs document the recommendation.
72
+ - Host gate asserts a healthy status report names `dsh-ops` and says it is not installed
73
+ automatically.
74
+
75
+ ## 0.16.0 — no startup auto-install, three-failure breaker
76
+
77
+ - **`installIfMissing` is gone.** The config field, its activation branch, the install-attempt report
78
+ text and the client switch are removed. Starting the Host only detects and reports; installing is
79
+ always an explicit `pwsh7_install` (or the manual Win+R path). This also removes the old winget
80
+ startup route, so the only user-facing install methods are script install and manual install.
81
+ - **Three-failure breaker, per Host process.** Consecutive failed script installs are counted
82
+ (exit 2/3/4/5 and job/spawn errors; a started-but-not-yet-installed click does not count). After
83
+ 3, `pwsh7_install` starts no job: it asks the user to check the network/VPN, and only accepts
84
+ `userConfirmedFixed: true` after the user explicitly says it is fixed. That resets the counter and
85
+ allows one retry; three more failures open the breaker again. Success and an already-installed
86
+ runtime reset it. The failure text now begins by asking whether a VPN is active.
87
+ - **Tool schema** gains `userConfirmedFixed`; the block is enforced before the VPN confirmation, so
88
+ repeating `vpnOffConfirmed: true` cannot bypass it.
89
+ - **Docs** rewritten to the current behavior: two install paths, VPN confirmation, Store notice,
90
+ breaker, no metered preflight, three gates, no `installIfMissing`.
91
+ - **Tests.** Host gate covers the breaker/no-job path and the explicit-fix reset; activation must
92
+ not start an install; client gate no longer expects the removed switch; all old config-driven
93
+ install tests use `method: 'store'` internally.
94
+
95
+ ## 0.15.1 — the Store script survives 5.1 encoding, and only two install paths
96
+
97
+ The 0.15.0 Store-UI script failed before opening the page with a parser error, not a VPN problem:
98
+ Windows PowerShell 5.1 decoded the inline text in the host's ANSI code page, and characters inside
99
+ the Chinese notice string became illegal tokens.
100
+
101
+ - **The script is a UTF-8 BOM file, not inline text.** `spawnStoreUi` writes it to a temporary
102
+ `.ps1` with a BOM and launches `powershell.exe -File`; 5.1 reads BOM-prefixed files as UTF-8
103
+ regardless of the console code page. The temporary directory is removed when the job settles.
104
+ - **Smart quotes are gone.** The Store notice uses 「安装」/「取消」 instead of curly quotes, which
105
+ PowerShell 7 itself treats as string delimiters. The generated script now parses under both
106
+ Windows PowerShell 5.1 and PowerShell 7.
107
+ - **Only two install paths are exposed.** The `pwsh7_install` schema no longer has a `method`
108
+ parameter and the status/report text no longer offers a winget command: the tool is the script
109
+ install (open Store, notice, click Install), and the other path is manual Win+R. The manual
110
+ instructions remain in every report and failure message.
111
+ - **Tests.** The host gate still drives the Store-UI click/failure jobs and no longer expects a
112
+ winget command in the report.
113
+
114
+ ## 0.15.0 — VPN confirmation, Store notice, no metered probe, no context injection
115
+
116
+ The 2026-10-27 run exposed three usability gaps around the Store-UI install and one unnecessary
117
+ mechanism: the model could start an install without asking about the VPN; the Store window could be
118
+ closed because nothing explained it; the metered preflight was no longer relevant to the manual
119
+ Store page; and the injected status note was unwanted.
120
+
121
+ - **VPN is now a question, not a probe.** `pwsh7_install` requires `vpnOffConfirmed: true`. Without
122
+ it the tool starts nothing and returns: ask the user whether a VPN is active, have them
123
+ disconnect it and restart Microsoft Store, then call again. There is no adapter/default-route
124
+ detection anywhere.
125
+ - **The Store window explains itself.** Before opening the page, the UI script shows a modal
126
+ `MessageBox`: dsh-win-pwsh7 is about to open Microsoft Store and click Install; do not close the
127
+ Store window; if a VPN is active, cancel, disconnect it, fully quit/restart Store, then retry.
128
+ Closing the notice with Cancel exits cleanly and the job reports "cancelled at the Store notice".
129
+ - **Metered/network preflight removed.** `METERED_COST`, `NETWORK_COST_SCRIPT`, `networkCost`,
130
+ `networkWarning`, the metered status warning and the install preflight are gone. `store-ui` is
131
+ the manual Store page, where the metered queue behaviour does not apply; `method: 'store'` keeps
132
+ the winget job but no longer probes the connection.
133
+ - **Context injection removed.** `agent.inject`, `createUserMessage`, the settled-state note and the
134
+ related contract check are gone; the job output and `pwsh7_status` remain the sources of truth.
135
+ - **Tests.** The host gate asserts the confirmation gate starts no job, a store-ui click job, a
136
+ page-not-loaded failure with VPN/manual guidance, and no network preflight; the probe environment
137
+ stays isolated from the host's real PowerShell.
138
+
139
+ ## 0.14.0 — open the Store page and click Install
140
+
141
+ The 2026-10-27 test proved that the manual Store page is the path that works, and that a script can
142
+ open it and invoke the same Install button with Windows UI Automation. `pwsh7_install` now defaults
143
+ to that route; winget remains available as `method: 'store'`.
144
+
145
+ - **`store-ui` (default).** It opens
146
+ `ms-windows-store://pdp/?ProductId=9MZ1SNWT0N5D`, waits up to 90 seconds for the Store window, then
147
+ invokes the localized Install / 获取 / Get / 更新 / Update button through `UIAutomationClient` and
148
+ falls back to `LegacyIAccessiblePattern`. This is the same Store page and button a human uses.
149
+ - **A click is not a failed install.** The job reports "Store page opened and Install was clicked —
150
+ downloading/installing in the background", tells the model not to retry, and asks it to poll
151
+ `pwsh7_status`; a runtime found within the bounded re-check is reported as installed.
152
+ - **Page-load failure asks about the VPN.** If no Install button appears within the timeout — or the
153
+ page cannot be opened/invoked — the report says the Store page did not load, and tells the user:
154
+ disconnect any VPN, fully quit and restart Microsoft Store, then retry; manual Win+R steps remain.
155
+ - **VPN auto-detection is gone.** The 0.13.4 adapter/default-route heuristic was removed; the
156
+ network probe is back to `<cost>|<profile>` and the only automatic connection gate is metering.
157
+ The VPN question is asked only when the Store page fails to load, as the user requested.
158
+ - **`store` remains.** `pwsh7_install` with `method: 'store'` runs the existing winget background job
159
+ with its queue hints and 30-second polling guidance.
160
+ - **Tests.** The host gate drives a successful Store-UI click, a page-not-loaded failure with VPN
161
+ and manual guidance, and keeps all winget tests on `method: 'store'`; the probe environment is
162
+ isolated from the host's real PowerShell so the gate is reproducible with PowerShell 7 installed.
163
+
164
+ ## 0.13.4 — configure-it guidance, manual Store steps, VPN preflight
165
+
166
+ Two gaps from the 2026-10-27 run: the blocked `pwsh` call mentioned install and disable but not the
167
+ case where PowerShell 7 is already installed and only needs wiring, and the automatic install was
168
+ also blocked by an active VPN while the plugin only understood metering.
169
+
170
+ - **The denial and status now offer three ways.** When a usable PowerShell 7 is detectable, the
171
+ first answer is "configure the executor": call `pwsh7_status` for the exact
172
+ `pwsh-sandbox.pwshPath` patch, apply it to the active profile and reload DSH. Installing
173
+ (`pwsh7_install` or manual) and disabling stay available. The status report says
174
+ "Three ways forward" with the same split.
175
+ - **Manual Store install is spelled out.** Every install path prints:
176
+ press Win+R, paste `ms-windows-store://pdp/?ProductId=9MZ1SNWT0N5D`, press Enter; outside a VPN the
177
+ Store page for PowerShell 7 opens — click Install. A manual install is auto-detected; no pin.
178
+ (The product id in the text is generated from `STORE_PRODUCT_ID`.)
179
+ - **VPN is detected and intercepted.** The Windows PowerShell 5.1 probe now also inspects active
180
+ adapters whose name/description match common VPN/TUN clients, a default route through such an
181
+ adapter, and `Get-VpnConnection`. A reading of `Unrestricted|<profile>|WireGuard` (or any active
182
+ VPN) makes the install preflight refuse to start and returns: disconnect the VPN, fully quit and
183
+ restart Microsoft Store, retry; or use the manual Store steps. `pwsh7_status` shows the same
184
+ warning; a failed install re-checks and names the VPN if it appeared after the preflight.
185
+ The adapter-name match is a heuristic — a false positive still leaves the manual Store install.
186
+ - **Tests.** The host gate asserts manual/configure text in the denial, a VPN preflight that starts
187
+ no job and names the adapter, and the VPN status warning.
188
+
189
+ ## 0.13.3 — queued Store, VPN, 30-second polling, context injection
190
+
191
+ The automatic Store download can sit in "Queued" indefinitely; the reports confirmed the cause is
192
+ the Store client/queue, not the plugin's preflight. This release makes that state actionable and
193
+ stops the model from waiting on one long `job_output` call.
194
+
195
+ - **The queue is a first-class recovery path.** The install start report, the 2-minute progress
196
+ hint, and the failure verdict all say: pause/resume the PowerShell entry in Microsoft Store →
197
+ Downloads/Library; if the Store itself is stuck, disable any VPN, fully quit and restart Microsoft
198
+ Store; or install manually from `ms-windows-store://pdp/?ProductId=9MZ1SNWT0N5D`. A manual Store
199
+ install is detected automatically, no `pwshPath` pin.
200
+ - **The model is told to poll in 30-second rounds.** `pwsh7_install` now says
201
+ `job_output(wait:true, timeout_ms:30000)` then `job_list`; a `wait aborted` is not an install
202
+ failure. This is guidance for the model, not a change to the job's own 15-second progress tick.
203
+ - **The settled state is injected into the agent context.** When the background job finishes, the
204
+ owning agent receives a short `agent.inject()` note (feature-detected): runtime now effective,
205
+ install failed with the queue/VPN/manual options, or installed-but-still-pinned. The prompt
206
+ section alone could not tell a model that stopped looking.
207
+ - **The queue hint names VPN.** The progress line after `Started MSStore package execution` now
208
+ includes pause/resume, VPN off + restart Store, and the Windows Update pause check.
209
+ - **Tests.** The install start report carries the 30-second/`timeout_ms:30000`/VPN text; the
210
+ settled failure injects a note into a stub agent.
211
+
212
+ ## 0.13.2 — the Store handoff is named correctly
213
+
214
+ The 2026-10-27 work-exp reports showed the automatic install failing on a **non-metered**
215
+ connection while the plugin still told the user to turn metering off. The failure path now re-reads
216
+ the connection, translates the Store error codes, and offers the recovery that actually worked:
217
+ the Store entry was parked in "Queued", and pause → resume started the download.
218
+
219
+ - **Failure attribution re-reads the connection.** `installVerdict` calls `networkCost` again;
220
+ metered advice appears only for `Fixed` / `Variable`. An unreadable cost is explicitly not
221
+ attributed to metering.
222
+ - **Store error codes are translated.** `wingetFailureCode` normalizes decimal exit codes to
223
+ `0x…`, and `describeWingetFailure` explains `0x80D03801` (download/deployment), `0x80004004`
224
+ (E_ABORT) and `0x80072EFD` (WinHTTP 12029).
225
+ - **Non-metered failures point at the queue and the manual fallback.** The verdict says to open
226
+ Microsoft Store → Downloads/Library, pause and resume a "Queued" PowerShell entry, check Windows
227
+ Update is not paused, and gives `ms-windows-store://pdp/?ProductId=9MZ1SNWT0N5D`. A manual Store
228
+ install is detected automatically — no `pwshPath` pin is needed.
229
+ - **The handoff is visible in progress.** `Started MSStore package execution` is now a named phase;
230
+ after `DSH_WIN_PWSH7_STORE_QUEUE_HINT_MS` (default 2 minutes) the progress line says the entry may
231
+ be queued, and the stall line names both the queue and metering instead of only metering.
232
+ - **The gate stub formats `%s`** (OBS-01), so the host gate no longer prints the placeholder.
233
+ - **Tests.** A non-metered failure must not give metering advice; `0x80D03801` must be translated and
234
+ keep the manual Store path; the metered preflight still starts no job.
235
+
236
+ ## 0.13.1 — install, then actually usable
237
+
238
+ The 0.13.0 gate read the executor correctly but inherited the executor's own cache: `pwsh7_install`
239
+ could install PowerShell 7.6.9 while the running `PwshLocalExecutor` still resolved to 5.1, so the
240
+ tool stayed blocked and `pwsh7_status` could even answer "Nothing to configure". Both are fixed.
241
+
242
+ - **The install now nudges the executor cache.** `PwshLocalExecutor.pwshPath` re-resolves only when
243
+ its declared config changes. After a successful install the plugin writes a sentinel into that
244
+ cache and re-reads the getter, so the same running process resolves the new Store alias. No
245
+ profile patch and no restart are needed when the concrete executor exposes the cache.
246
+ - **The nudge is feature-detected.** If a composition does not expose `declaredPwshPath` /
247
+ `resolvedPwshPath`, the install report says so and prints the pin + reload patch instead of
248
+ pretending the tool is ready. `tools/check-native-contracts.mjs` now guards both fields.
249
+ - **The status can no longer call a stale executor "native".** When the live executor still reports
250
+ 5.1, the report keeps the `running executor still resolves to Windows PowerShell 5.1` warning,
251
+ prints the pin patch, and never says "Nothing to configure" just because the standalone resolver
252
+ now finds a Store alias.
253
+ - **Failures that cannot be nudged are explicit.** `installVerdict` distinguishes "the running
254
+ executor now resolves to 7.6.9" from "it still resolves to 5.1; pin pwsh-sandbox.pwshPath and
255
+ reload DSH", so the user is not left with a contradictory success message.
256
+ - **The Re-detect button moved into the status section.** It used to sit in the detail page's
257
+ head actions, away from the text it refreshes; it now renders beside the status title, where a
258
+ reader looks for it. `plugins.detail.actions` no longer carries a second copy.
259
+ - **Tests.** A stale live 5.1 + resolver 7 case must not render "Nothing to configure"; a stubbed
260
+ executor cache must be refreshed by the nudge; the contract gate asserts the cache fields exist;
261
+ the client gate renders the section's own Re-detect and drives the Remote flow through it.
262
+
263
+ Measured against the 2026-10-27 work-exp report: install landed 7.6.9, the tool remained blocked
264
+ until a manual `pwsh-sandbox.pwshPath` pin, and status said "Nothing to configure" in the same
265
+ window. This release removes that manual step on the normal path and makes the fallback honest.
266
+
267
+ ## 0.13.0 — enabled means gated
268
+
269
+ The plugin was observational: with only Windows PowerShell 5.1 it warned the model and let the
270
+ command run. This release makes the enabled state a contract. The model sees a call-time error
271
+ before dispatch, the user sees the install/configure/disable choices, and no PowerShell process is
272
+ started for a denied call.
273
+
274
+ - **The gate reads the executor, not a reconstruction.** `ctx.shell.pwshPath` is the exact
275
+ executable `dsh-tool-pwsh` will spawn (`PwshLocalExecutor.argv()` uses it). The read
276
+ feature-detects that concrete getter and falls back to `shell.config.pwshPath.get()` +
277
+ `resolvePwshPath()`, then to the default resolver. `configEditor.entries()` only exposes
278
+ `include`-owned rows, so a Desktop-generated live row could be missed; the report still lists the
279
+ rows for diagnosis.
280
+ - **`ctx.tools.guard` denies `pwsh` before dispatch.** A per-context snapshot drives the guard; a
281
+ `tools/pre-execute` listener refreshes it when the snapshot is unknown or older than
282
+ `DSH_WIN_PWSH7_GATE_TTL_MS`, and `app-boot/config-reload` / `plugin-manager/changed` refresh it
283
+ after profile changes. `pwsh7_status` and `pwsh7_install` are never gated.
284
+ - **The denial is self-contained.** It names the reason (5.1, broken pin, no PowerShell 7, unknown),
285
+ says not to retry, and offers `pwsh7_status` (exact patch), `pwsh7_install` (after the user
286
+ agrees), or disabling the plugin. The 5.1 prompt section now says the tool is blocked instead of
287
+ advising the `;` workaround.
288
+ - **Install preflight.** `resolveWinget()` then `networkCost()`: the install starts only for
289
+ `Unrestricted` with a readable profile. `Fixed` / `Variable` / `Unknown` / an unreadable probe /
290
+ a missing winget returns a report naming the network and the setting to turn off, and registers
291
+ **no job**. `installIfMissing` skips with the same reason instead of starting a doomed install.
292
+ - **Configuration guidance.** The report keeps the row's existing config fields (the `config` block
293
+ is replaced wholesale), distinguishes add vs update, and states that `overridePath` only changes
294
+ probing and recommendations — not executor wiring.
295
+ - **Client.** The diagnostics section derives a metered-network line and a fix block with the
296
+ profile patch and a copy button from the report it already receives.
297
+ - **Native contracts.** `docs/native-contracts.md` records the 0.2.1-rc.3 package table and the
298
+ symbols the plugin relies on; `tools/check-native-contracts.mjs` (36 checks over 12 packages)
299
+ runs after the host and client gates and fails when a symbol or prototype disappears.
300
+ - **Tests.** Gate allow/deny, broken pin, own tools, pre-execute refresh, config reload, add/update
301
+ patch, wholesale-config warning, overridePath-not-wiring, metered/unknown/no-winget starting no
302
+ job, and the client's network line and copyable patch.
303
+
304
+ This is a breaking behavioral change for anyone who relied on the 5.1 fallback while the plugin was
305
+ enabled. Disabling the plugin restores the previous behavior; the plugin still never writes
306
+ `pwshPath` and never uninstalls PowerShell.
307
+
308
+ ## 0.12.3 — the gates read the desktop build's nested harness
309
+
310
+ The desktop build (`DeepSeek Harness.exe` 0.2.1-rc.2) packs a different layout than 2.0.19-next: the
311
+ archive root carries only a shell — `dsh`, `node_modules`, `lib`, `package.json`, `renderer` — and its
312
+ `node_modules/@deepseek-ai` holds eight packages (cordis, cosmokit, dsh-api-gateway, dsh-brand,
313
+ dsh-deque, dsh-timeout, dsh-typert-protocol, schemastery), while all 306 harness packages sit under
314
+ `dsh/`. `0.12.2` accepted any root carrying `cordis`, so it picked the shell and the Host gate died
315
+ with `ERR_MODULE_NOT_FOUND: Cannot find package '@deepseek-ai/dsh-tools'`.
316
+
317
+ - **The harness is recognised by carrying both `cordis` and `dsh-tools`**, not `cordis` alone, and a
318
+ root or its `dsh`/`app` subdirectory is searched for it — `harnessAt()` in
319
+ `tools/dsh-app-modules.mjs`.
320
+ - **The unpack now also takes `dsh/`**, so a packed desktop archive yields `<cache>\dsh`, and the
321
+ `note — the harness packages come from an asar archive, unpacked for this run into …` line names it.
322
+ - **A cache created by an older release is not trusted**: it is reused only when the full harness is
323
+ inside it, so upgrading the plugin repairs the stale unpack instead of failing again.
324
+ - **Measured**: new desktop install 30 s cold / 6 s cached, previous 2.0.19-next install 6 s cached,
325
+ both gates green; no installation at all still prints the actionable note and exits 1.
326
+ - **The READMEs now describe all three layouts** and the "Releasing" sections explain that the tag
327
+ publishes through CI (trusted publishing, OIDC) and that provenance requires `repository.url` to be
328
+ byte-identical to the GitHub context.
329
+
330
+ ## 0.12.2 — the gates read a packed DSH 2.0 install
331
+
332
+ DSH 2.0.19-next ships its packages inside `resources/app.asar`, and the Host gate could not find them
333
+ there: Node resolves nothing inside an archive, and the sibling `app.asar.unpacked` holds only five
334
+ packages (cordis is not among them), so the run ended in
335
+ `ERR_MODULE_NOT_FOUND: Cannot find package '@deepseek-ai/cordis'`. The plugin's own halves were never
336
+ affected — both load and run on 2.0.19-next — but nobody could re-run the gates on a 2.0 machine,
337
+ which is exactly when they are wanted.
338
+
339
+ - **`tools/dsh-app-modules.mjs` (new)** resolves the harness: `DSH_APP_DIR` first, then the nearest
340
+ ancestor of the tools directory or the working directory that carries
341
+ `node_modules/@deepseek-ai/cordis`. `DSH_APP_DIR` now accepts an install root, `resources/app`
342
+ (an unpacked install), or `resources/app.asar` (a packed one).
343
+ - **A packed installation is unpacked once** into `%TEMP%\dsh-app-modules-<archive>-<size>-<mtime>` —
344
+ only `node_modules` and `package.json`, staged under `<cache>.<pid>` and renamed into place, reused
345
+ by later runs. Unpacking the real tree instead of re-implementing Node's resolution over the archive
346
+ is deliberate: a hand-written resolver can disagree with Node on `exports` maps, conditions, and
347
+ CJS/ESM, and that disagreement would show up as a green gate that proves nothing.
348
+ - **The hook and the Host gate use it**, and the gate says where the packages came from. A machine
349
+ with no installation at all still fails the gate — with one actionable line naming the three
350
+ accepted hand-offs, instead of a bare `ERR_MODULE_NOT_FOUND`.
351
+ - **Measured on 2.0.19-next**: 38 s cold (the unpack is most of it), 6 s on the cached second run,
352
+ both gates green.
353
+
354
+ ## 0.12.1 — first public release
355
+
356
+ `dsh-win-pwsh7` puts a real PowerShell 7 under the Harness `pwsh` tool on Windows. This is the first
357
+ release published outside its author's machine, and it is the behaviour the README documents:
358
+
359
+ - **Detection and reporting** — `pwsh7_status` reads the executor rows, validates an explicit path,
360
+ refuses a version-numbered Store path as un-pinnable, and prints a pasteable profile patch for the
361
+ version-stable Store family alias.
362
+ - **One install path** — `pwsh7_install` installs the Microsoft Store package as a background job.
363
+ It reads the connection cost first, because Windows defers Store downloads on a connection marked
364
+ as metered, and the remedy it offers is that setting — never a second installer. When PowerShell 7
365
+ is not wanted at all, the other answer is to disable the plugin.
366
+ - **A failed exit is evidence, not a verdict** — the runtime is re-probed before a failure is
367
+ reported, because the Store can finish an install after winget has already exited non-zero.
368
+ - **Progress that is measured** — the job follows winget's own diagnostic log for named phases
369
+ (downloading, deploying, registering) and measures the installer download from the bytes on disk,
370
+ because winget reports no numeric progress of its own.
371
+ - **A prompt section** — the model is told which PowerShell the executor actually runs, so it knows
372
+ whether `&&` and `||` work before it writes a command.
373
+ - **Two offline gates** under `tools/`, shipped with the package so anyone can re-run them. They run
374
+ unconditionally — the behaviour half is fixture-pinned (`PATH`, `LOCALAPPDATA`, `ProgramFiles`)
375
+ rather than skipped on a machine that has the per-machine MSI — they cover job cancellation and
376
+ both inline install fallbacks, and every assertion has been shown to fail under a deliberate
377
+ mutation.
378
+ - **Reviewed independently before release** — three Open Code Review passes, 36 findings triaged.
379
+ 30 were confirmed and fixed. The material ones: a gate that silently skipped half its checks; all
380
+ inline install paths taking a single-shot verdict instead of the bounded re-probe; a stall warning
381
+ that had become unreachable; a cancelled job that kept working (twice — the re-probe loop was the
382
+ second half); an activation-time install that reported nothing because it waited forever; a report
383
+ that could name a disabled row as the runtime. 4 findings were refuted with citations (the harness's
384
+ own schema dialect, the ecosystem's client descriptor shape, the optional `jobs` service, and the
385
+ string-error envelope), and 1 is recorded as backlog: the module-level state a second application
386
+ in one process would share.
387
+ - **Confirmed on the machine that found it** — the "a disabled row is never named as the runtime" fix
388
+ is not theoretical. This profile carries `pwsh-sandbox` (disabled) beside
389
+ `desktop-windows-pwsh-sandbox` (enabled), both pinning the same Store family alias; the report named
390
+ the *disabled* row until the fix, and after the restart it names the enabled one instead.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 XinSpark
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.en.md ADDED
@@ -0,0 +1,157 @@
1
+ # dsh-win-pwsh7
2
+
3
+ <p align="center"><sub><a href="README.md">简体中文</a> · English</sub></p>
4
+
5
+ Give the Harness `pwsh` tool a real PowerShell 7 on Windows instead of silently falling back to
6
+ Windows PowerShell 5.1.
7
+
8
+ 1. **Detect** PowerShell 7 on the machine (plus an optional `overridePath`);
9
+ 2. **Validate** an explicit path (`pwsh7_status`'s `path` argument);
10
+ 3. **Install** via the script path (`pwsh7_install` opens the Microsoft Store page and clicks
11
+ Install) or the manual path (Win+R, same Store link). There is no automatic startup install.
12
+
13
+ While enabled, the `pwsh` tool is allowed **only when the live executor resolves to PowerShell 7+**.
14
+ Otherwise every call is denied by `ctx.tools.guard` before dispatch, the model is told to configure /
15
+ install / disable, and no PowerShell process starts. Disabling the plugin removes the gate; the
16
+ default executor then chooses the runtime itself, and the plugin still never writes `pwshPath`.
17
+
18
+ ## Verified environment
19
+
20
+ - Windows 10/11.
21
+ - Verified on DSH **0.2.1-rc.3** (see `dsh.verifiedAgainst` in `package.json`).
22
+ - The script path needs Microsoft Store; the manual path only needs the Store page.
23
+ - No administrator rights are required.
24
+
25
+ ## What it does not do
26
+
27
+ - It does not install anything at startup.
28
+ - It does not write the executor's `pwshPath` or edit the profile patch.
29
+ - It does not uninstall a Store-installed PowerShell 7.
30
+ - It does not auto-detect VPNs or preflight metered connections.
31
+
32
+ ## Install this plugin
33
+
34
+ ```powershell
35
+ dsh plugin --profile desktop add github:XinSpark/dsh-win-pwsh7#v0.2.0
36
+ # or
37
+ dsh plugin --profile desktop add dsh-win-pwsh7
38
+ ```
39
+
40
+ Restart DSH after installing. To wire a path, paste the exact profile patch printed by
41
+ `pwsh7_status` into `~\.dsh\profiles\<profile>\cordis.patch.yml` and restart; the plugin never writes
42
+ the executor's `pwshPath` itself.
43
+
44
+ ## Two install paths
45
+
46
+ | Path | Behavior |
47
+ |---|---|
48
+ | **Script install** | Call `pwsh7_install`. The tool first requires the user to confirm no VPN is active (`vpnOffConfirmed: true`). It then opens the Microsoft Store page for PowerShell 7, shows a notice (do not close the Store window; cancel if a VPN is on), and invokes the localized Install / Get / 获取 / 安装 button with Windows UI Automation. |
49
+ | **Manual install** | Win+R → paste `ms-windows-store://pdp/?ProductId=9MZ1SNWT0N5D` → Enter → click Install. A manual install is detected automatically; no pin is needed. |
50
+
51
+ If the Store page exposes no Install button within 90 seconds, the report asks the user to confirm
52
+ the VPN first: disconnect it, fully quit and restart Microsoft Store, then retry or use the manual
53
+ path.
54
+
55
+ ### Three-failure breaker
56
+
57
+ Within one Host process, after **3 consecutive failed script installs**:
58
+
59
+ - the next `pwsh7_install` starts no job;
60
+ - it first asks the user to check the network, especially the VPN;
61
+ - only after the user explicitly says it is fixed may the model call with
62
+ `userConfirmedFixed: true`; that resets the counter and allows one retry;
63
+ - three more failures open the breaker again;
64
+ - otherwise the report points at the manual install.
65
+
66
+ The count lives in the current process and resets when DSH restarts.
67
+
68
+ ## Configuration
69
+
70
+ | Key | Default | Meaning |
71
+ |---|---|---|
72
+ | `overridePath` | unset | Priority ① explicit `pwsh.exe`. It must spawn and report PowerShell 7+; a failed validation falls back to auto-detection. **It only changes probing and recommendations, not executor wiring.** |
73
+
74
+ `overridePath` is `volatile()`: the plugin-page configuration section saves it live; it can also be
75
+ set in the profile patch:
76
+
77
+ ```yaml
78
+ - id: win-pwsh7
79
+ config:
80
+ overridePath: 'D:\tools\pwsh\pwsh.exe'
81
+ ```
82
+
83
+ ## Recommended companion
84
+
85
+ When the script install completes, its job verdict contains an **instruction to the model**: tell the
86
+ user that dsh-win-pwsh7 works best paired with the **dsh-ops** plugin
87
+ ([https://github.com/T-Auto/dsh-ops](https://github.com/T-Auto/dsh-ops)); ask before installing it and
88
+ never install it without the user's consent. Command:
89
+
90
+ ```powershell
91
+ dsh plugin --profile <profile> add dsh-ops
92
+ ```
93
+
94
+ The recommendation appears only in the completed script-install result; `pwsh7_status` and manual
95
+ installs do not advertise it repeatedly.
96
+
97
+ ## Report example
98
+
99
+ `pwsh7_status` returns the text report below; the plugin page renders the same state as a card
100
+ rather than showing the whole report.
101
+
102
+ ```
103
+ Executor rows:
104
+ pwsh-sandbox : pwshPath = C:\Users\...\WindowsApps\Microsoft.PowerShell_8wekyb3d8bbwe\pwsh.exe (PowerShell 7.6.9, Store family alias)
105
+
106
+ The pwsh tool currently runs on PowerShell 7.6.9 via the executor (…).
107
+ The executor resolves to the version-stable Store family alias: … Nothing to configure.
108
+ ```
109
+
110
+ When nothing usable exists, the report ends with `Three ways forward:`: install / configure the
111
+ executor / disable the plugin.
112
+
113
+ ## Plugin page
114
+
115
+ The status section is a native card showing the state, runtime and executor path, with Re-detect
116
+ at the top right; the configuration section exposes only `overridePath`.
117
+
118
+ ## Disable or uninstall
119
+
120
+ Disabling the plugin removes the `pwsh` gate and the plugin page; it does not uninstall PowerShell 7.
121
+ As long as `pwsh.exe` remains on the machine, the default executor still prefers it (normally the
122
+ Store build); Windows PowerShell 5.1 is used only when no `pwsh.exe` can be found. To truly return
123
+ to 5.1, uninstall the Store build or pin the executor to `powershell.exe` (not recommended).
124
+
125
+ ## Troubleshooting
126
+
127
+ | Symptom | What to do |
128
+ |---|---|
129
+ | The Store page opens without an Install button | Confirm the VPN is off, fully quit and restart Microsoft Store, then retry; or use the manual path. |
130
+ | `pwsh` still runs 5.1 | Run `pwsh7_status`, paste the reported `pwshPath` patch into the profile, and restart; or install the Store build. |
131
+ | The plugin page is missing | Confirm `dsh-win-pwsh7` is enabled in the current profile, then restart DSH. |
132
+
133
+ ## Verification
134
+
135
+ ```powershell
136
+ $env:DSH_APP_DIR = 'E:\FXS\APP\DSH NEXT\resources\app.asar'
137
+ node tools/check-host.mjs
138
+ node tools/check-client.mjs
139
+ node tools/check-native-contracts.mjs
140
+ ```
141
+
142
+ All three gates run offline against a packed installation (`resources/app.asar`); `DSH_APP_DIR`
143
+ accepts the install root, `resources/app`, or `resources/app.asar`.
144
+
145
+ ## Design constraints
146
+
147
+ - The reading comes from the executor's own `ctx.shell.pwshPath`, not from config rows;
148
+ `overridePath` only changes probing and recommendations.
149
+ - No VPN auto-detection and no metered-connection preflight: the user confirms the VPN before an
150
+ install, and a page-load failure asks again.
151
+ - No automatic retries; the three-failure breaker ends the loop.
152
+ - Run all three gates before publishing; end-to-end installation is release evidence, not part of
153
+ the automated gate.
154
+
155
+ ## License
156
+
157
+ [MIT](LICENSE)