@sparkelf/dsh-patch-wsl-native-open 0.2.0-rc.8 → 0.2.1-alpha.1

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/package.json CHANGED
@@ -4,11 +4,11 @@
4
4
  "formatVersion": 1,
5
5
  "variants": [
6
6
  {
7
- "dsh": ">=0.1.6-alpha.1",
7
+ "dsh": ">=0.2.0-rc.2",
8
8
  "file": "./patches/wsl-native-open.patch",
9
9
  "id": "wsl-native-path-translation",
10
10
  "target": {
11
- "baseRevision": "ddefc45fbc7f8e46dd73185e68295696d1297887",
11
+ "baseRevision": "5badb15009ae1756c3afe0ae0cef1faafc290ccc",
12
12
  "kind": "dsh-source",
13
13
  "paths": [
14
14
  "packages/host/open-in-app/",
@@ -32,5 +32,5 @@
32
32
  "url": "git+https://github.com/SparkElf/deepseek-harness-plus.git"
33
33
  },
34
34
  "type": "module",
35
- "version": "0.2.0-rc.8"
35
+ "version": "0.2.1-alpha.1"
36
36
  }
@@ -1,8 +1,8 @@
1
1
  diff --git a/packages/host/open-in-app/src/icons.ts b/packages/host/open-in-app/src/icons.ts
2
- index 6908ce7..570dc45 100644
2
+ index 4b07441c8c..519ba1344b 100644
3
3
  --- a/packages/host/open-in-app/src/icons.ts
4
4
  +++ b/packages/host/open-in-app/src/icons.ts
5
- @@ -97,6 +97,41 @@ const EXTRACT_ICON_PS1 = [
5
+ @@ -98,6 +98,41 @@ const EXTRACT_ICON_PS1 = [
6
6
  '',
7
7
  ].join('\n')
8
8
 
@@ -44,7 +44,7 @@ index 6908ce7..570dc45 100644
44
44
  /** Extract one Windows executable's associated icon as a 32px PNG. */
45
45
  async function extractExecutableIconPng(
46
46
  executablePath: string, timeoutMs: number, internals: ResolvedInternals,
47
- @@ -106,8 +141,12 @@ async function extractExecutableIconPng(
47
+ @@ -107,8 +142,12 @@ async function extractExecutableIconPng(
48
48
  const script = join(workDir, 'extract-icon.ps1')
49
49
  const outPng = join(workDir, 'icon.png')
50
50
  await writeFile(script, EXTRACT_ICON_PS1, 'utf8')
@@ -58,7 +58,7 @@ index 6908ce7..570dc45 100644
58
58
  ], timeoutMs, internals)
59
59
  if (ran === null) return null
60
60
  try {
61
- @@ -192,6 +231,13 @@ export async function extractAppIcon(
61
+ @@ -148,6 +187,13 @@ export async function extractAppIcon(
62
62
  ): Promise<OpenInAppIcon | null> {
63
63
  const completed = resolveInternals(internals)
64
64
  if (completed.platform === 'linux') {
@@ -73,10 +73,10 @@ index 6908ce7..570dc45 100644
73
73
  return desktopId === undefined ? null : extractLinuxIcon(desktopId, completed)
74
74
  }
75
75
  diff --git a/packages/host/open-in-app/src/resolver.ts b/packages/host/open-in-app/src/resolver.ts
76
- index 7ab964b..8ea7318 100644
76
+ index 970118e22a..ad8d176da4 100644
77
77
  --- a/packages/host/open-in-app/src/resolver.ts
78
78
  +++ b/packages/host/open-in-app/src/resolver.ts
79
- @@ -14,10 +14,10 @@
79
+ @@ -14,10 +14,11 @@
80
80
 
81
81
  import { spawn } from 'node:child_process'
82
82
  import { readdir, readFile, stat } from 'node:fs/promises'
@@ -84,12 +84,13 @@ index 7ab964b..8ea7318 100644
84
84
  +import { homedir, release as osRelease, platform as osPlatform } from 'node:os'
85
85
  import { dirname, isAbsolute, join } from 'node:path'
86
86
  import {
87
- - canOpenNativePath, openNativePath, runNativeCommand, type NativeCommandRunner,
88
- + canOpenLinuxDesktop, canOpenNativePath, openNativePath, runNativeCommand, type NativeCommandRunner,
87
+ - canOpenNativePath, openNativePath, runNativeCommand, desktopEntryFields, desktopDataDirectories, type NativeCommandRunner,
88
+ + canOpenLinuxDesktop, canOpenNativePath, openNativePath, runNativeCommand,
89
+ + desktopEntryFields, desktopDataDirectories, type NativeCommandRunner,
89
90
  } from '@deepseek-ai/dsh-native-command'
90
91
  import { scrubbedParentEnv } from '@deepseek-ai/dsh-subprocess'
91
92
  import {
92
- @@ -98,6 +98,8 @@ export const launchDetachedApp: OpenInAppLauncher = (command, args, options) =>
93
+ @@ -98,6 +99,8 @@ export const launchDetachedApp: OpenInAppLauncher = (command, args, options) =>
93
94
  /** Injectable platform facts for deterministic tests. */
94
95
  export interface OpenInAppInternals {
95
96
  platform?: NodeJS.Platform
@@ -98,7 +99,7 @@ index 7ab964b..8ea7318 100644
98
99
  /** SSH launch fact from the inherited process layer, independent of `.env` values. */
99
100
  ssh?: boolean
100
101
  /** Bundle-directory roots replacing `/Applications` and `~/Applications`. */
101
- @@ -115,6 +117,7 @@ export interface OpenInAppInternals {
102
+ @@ -115,6 +118,7 @@ export interface OpenInAppInternals {
102
103
  /** Platform facts after the one explicit defaulting step at each public entry. */
103
104
  export interface ResolvedInternals {
104
105
  platform: NodeJS.Platform
@@ -106,7 +107,7 @@ index 7ab964b..8ea7318 100644
106
107
  ssh: boolean
107
108
  applicationRoots: readonly string[]
108
109
  env: Readonly<Record<string, string | undefined>>
109
- @@ -140,6 +143,7 @@ export function resolveInternals(internals: OpenInAppInternals): ResolvedInterna
110
+ @@ -140,6 +144,7 @@ export function resolveInternals(internals: OpenInAppInternals): ResolvedInterna
110
111
  }
111
112
  return {
112
113
  platform: internals.platform ?? osPlatform(),
@@ -114,7 +115,7 @@ index 7ab964b..8ea7318 100644
114
115
  ssh: internals.ssh ?? false,
115
116
  applicationRoots: internals.applicationRoots ?? ['/Applications', join(home, 'Applications')],
116
117
  env: internals.env ?? process.env,
117
- @@ -467,6 +471,14 @@ export function specFor(app: OpenInAppApp, platform: NodeJS.Platform): OpenInApp
118
+ @@ -453,6 +458,14 @@ export function specFor(app: OpenInAppApp, platform: NodeJS.Platform): OpenInApp
118
119
  : undefined
119
120
  }
120
121
 
@@ -129,7 +130,7 @@ index 7ab964b..8ea7318 100644
129
130
  /** Icon source for a resolved executable: Windows extracts from the binary itself. */
130
131
  function executableIcon(path: string, internals: ResolvedInternals): OpenInAppIconSource | undefined {
131
132
  return internals.platform === 'win32' ? { kind: 'executable', path } : undefined
132
- @@ -518,10 +530,29 @@ async function locate(
133
+ @@ -504,10 +517,29 @@ async function locate(
133
134
  }
134
135
  }
135
136
  case 'cli': {
@@ -164,7 +165,7 @@ index 7ab964b..8ea7318 100644
164
165
  return found === null
165
166
  ? null
166
167
  diff --git a/packages/util/native-command/src/index.ts b/packages/util/native-command/src/index.ts
167
- index a2e23a3..f1ea7c6 100644
168
+ index 9ad8e0883c..3377ddcb6f 100644
168
169
  --- a/packages/util/native-command/src/index.ts
169
170
  +++ b/packages/util/native-command/src/index.ts
170
171
  @@ -6,6 +6,7 @@
@@ -176,13 +177,13 @@ index a2e23a3..f1ea7c6 100644
176
177
  nativeFileManager,
177
178
  revealNativePath,
178
179
  diff --git a/packages/util/native-command/src/path-opener.ts b/packages/util/native-command/src/path-opener.ts
179
- index 8f3443b..79e7ace 100644
180
+ index 67bbc35902..dd062cc003 100644
180
181
  --- a/packages/util/native-command/src/path-opener.ts
181
182
  +++ b/packages/util/native-command/src/path-opener.ts
182
- @@ -99,13 +99,49 @@ function isWsl(internals: PathOpenerInternals): boolean {
183
- return (internals.osRelease ?? osRelease()).toLowerCase().includes('microsoft')
184
- }
185
-
183
+ @@ -150,8 +150,55 @@ async function runExplorer(args: readonly string[], signal: AbortSignal, run: Pa
184
+ * @param signal - caller lifetime; abort terminates the command.
185
+ * @param run - shell-free command runner.
186
+ */
186
187
  +/**
187
188
  + * Windows PowerShell locations to try when PATH carries no Windows entry.
188
189
  + *
@@ -192,37 +193,42 @@ index 8f3443b..79e7ace 100644
192
193
  + * the volume the distribution mounts, and naming it there is what keeps the Windows
193
194
  + * desktop reachable from those processes.
194
195
  + */
195
- +const WINDOWS_POWERSHELL_CANDIDATES = [
196
- + '/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe',
197
- + '/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe',
196
+ +const WINDOWS_EXPLORER_CANDIDATES = [
197
+ + '/mnt/c/Windows/explorer.exe',
198
+ + '/c/Windows/explorer.exe',
198
199
  +] as const
199
200
  +
200
- /** Open one Windows-resolvable path through its registered desktop application. */
201
+ +/**
202
+ + * Open one Windows-resolvable path through its registered desktop application.
203
+ + *
204
+ + * Explorer resolves the association, but a process whose PATH lost its Windows entries
205
+ + * cannot name the shell. WSL injects those entries only for the processes it starts
206
+ + * interactively, so a service or scheduled job resolves nothing by name; naming the
207
+ + * shell on the volume the distribution mounts keeps the Windows desktop reachable.
208
+ + * @param path - Windows-resolvable path.
209
+ + * @param signal - caller lifetime; abort terminates the command.
210
+ + * @param run - shell-free command runner.
211
+ + */
201
212
  async function openWindowsPath(path: string, signal: AbortSignal, run: PathOpenerRunner): Promise<void> {
202
- - await run('powershell.exe', [
203
- + const args = [
204
- '-NoProfile',
205
- '-Command',
206
- `Invoke-Item -LiteralPath ${powershellLiteral(path)}`,
207
- - ], signal)
208
- + ]
213
+ - await runExplorer([explorerTarget(path)], signal, run)
214
+ + const target = explorerTarget(path)
209
215
  + try {
210
- + await run('powershell.exe', args, signal)
216
+ + await runExplorer([target], signal, run)
211
217
  + return
212
218
  + } catch (error) {
213
- + // A missing executable is the one failure a mounted path can answer; a non-zero
214
- + // exit from PowerShell itself is a real launch failure and must stay reported.
219
+ + // A missing executable is the one failure a mounted path can answer; any other
220
+ + // failure is Explorer's own and must stay reported.
215
221
  + if (!isMissingExecutable(error)) throw error
216
222
  + }
217
- + for (const candidate of WINDOWS_POWERSHELL_CANDIDATES) {
223
+ + for (const candidate of WINDOWS_EXPLORER_CANDIDATES) {
218
224
  + try {
219
- + await run(candidate, args, signal)
225
+ + await run(candidate, [target], signal, 'visible')
220
226
  + return
221
227
  + } catch (error) {
222
228
  + if (!isMissingExecutable(error)) throw error
223
229
  + }
224
230
  + }
225
- + throw new Error('Windows PowerShell is not reachable from this WSL mount')
231
+ + throw new Error('Windows Explorer is not reachable from this WSL mount')
226
232
  +}
227
233
  +
228
234
  +/** Whether a spawn failed because the executable could not be found at all. */
@@ -231,7 +237,7 @@ index 8f3443b..79e7ace 100644
231
237
  }
232
238
 
233
239
  /** Translate a WSL path before handing it to the Windows desktop. */
234
- @@ -154,6 +190,25 @@ async function openNativePathWithIntent(
240
+ @@ -200,6 +247,25 @@ async function openNativePathWithIntent(
235
241
  throw new Error(`native path opener is unsupported on ${platform}`)
236
242
  }
237
243