@deepseek-ai/dsh-native-command 0.1.1-rc.2 → 0.1.2-alpha.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/README.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write packages/util/native-command/README.md
5
- README.md: 8a552ac36fac150ae18b2a8adaab1dd489ecb476
6
- README.zh.md: 7b5ea7fa1985d394b88fd5bcdbce9d9831eef8ee
5
+ README.md: 282cefcc00d296b3c09741e64c88647343e1bef2
6
+ README.zh.md: aecb60d2e8f68a6e9468e3f2d4b0e2d99bebefc6
package/README.md CHANGED
@@ -1,27 +1,115 @@
1
- # dsh-native-command
1
+ ---
2
+ description: "Host-native command and path-opening utilities with shell-free execution, cancellation, desktop detection, and WSL path handoff."
3
+ kind: "package-library"
4
+ ---
5
+
6
+ # @deepseek-ai/dsh-native-command
2
7
 
3
8
  English | [中文](README.zh.md)
4
9
 
5
- A **zero-dependency no-shell `execFile` runner** shared by host-native OS integrations: one `runNativeCommand(command, args, signal)` call spawns the executable directly (never a shell string), captures utf8 stdout/stderr, propagates the caller's abort into child termination, and hides the transient console window on Windows. Failures reject with the exit `code` and both captured streams attached, so callers classify (missing tool, cancelled, real failure) without re-running anything.
10
+ ## Summary
11
+
12
+ `dsh-native-command` runs host executables without a shell and opens Host filesystem paths through the desktop. The command runner captures utf8 output, propagates cancellation, and hides transient Windows consoles. The path opener supports default-application and text-editor intents, browser-renderable documents, WSL translation, and desktop availability checks. It is a library, not a plugin: no `ctx`, no state, no events.
13
+
14
+ ## Table of Contents
15
+
16
+ - [Use this package](#use-this-package)
17
+ - [Understand the implementation](#understand-the-implementation)
18
+ - [Further Exploration](#further-exploration)
19
+ - [Model Experience](#model-experience)
20
+ - [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
21
+ - [Dev Note](#dev-note)
6
22
 
7
- Its two consumers are the host-side native integrations: the [`directory-picker-native`](../../host/directory-picker-native/README.md) backend's OS chooser commands and the gateway's open-with-default-application hand-off ([`dsh-host-apiproxy`](../../host/apiproxy/README.md) `host.openPath`). The `NativeCommandRunner` type is their injectable command boundary.
23
+ -----
8
24
 
9
- It is a **library, not a service or plugin**: no `ctx`, registers nothing, holds no state, emits no events.
25
+ <a id="use-this-package"></a>
26
+ ## Use this package
10
27
 
11
- ## Surface
28
+ Use this runner when a host-side integration must execute one native command and needs its output, its failure, or both — and must never involve a shell.
29
+
30
+ ### Running a command
12
31
 
13
32
  ```ts
14
- import { runNativeCommand, type NativeCommandRunner } from '@deepseek-ai/dsh-native-command'
33
+ import { runNativeCommand } from '@deepseek-ai/dsh-native-command'
34
+
35
+ declare const script: string
36
+ declare const signal: AbortSignal
37
+ const { stdout, stderr } = await runNativeCommand('osascript', ['-e', script], signal)
15
38
  ```
16
39
 
40
+ On exit 0 the call resolves with captured stdout and stderr. On any failure it rejects with the exit `code` and both captured streams attached, so a caller can tell a missing tool (`ENOENT`), a cancellation (`ABORT_ERR`), and a real command failure apart without re-running the command.
41
+
42
+ ### Injecting the command boundary
43
+
44
+ The `NativeCommandRunner` type is the injectable command boundary for host integrations: pass the function (or a wrapper) where the integration needs a testable seam, so tests can substitute a fake runner.
45
+
46
+ ### Opening a Host path
47
+
48
+ `openNativePath(path, signal)` hands a path to the default application and prefers the named default browser for HTML and SVG where the platform can identify one. `openNativeTextFile(path, signal)` selects text-editor intent; on macOS it uses `open -t`. WSL paths are translated with `wslpath -w` before the Windows desktop receives them. `canOpenNativePath()` reports whether the current Host plausibly has a desktop target.
49
+
50
+ -----
51
+
52
+ <a id="understand-the-implementation"></a>
53
+ ## Understand the implementation
54
+
55
+ <details>
56
+ <summary>Implementation internals — click to expand</summary>
57
+
58
+ The command runner is a thin wrapper over Node's `execFile`. The path opener selects one shell-free command from platform and environment facts, while callers retain authority over which path may be opened.
59
+
60
+ ### Source map
61
+
62
+ | File | Role |
63
+ |---|---|
64
+ | [`src/index.ts`](src/index.ts) | Public command-runner and path-opener exports |
65
+ | [`src/runner.ts`](src/runner.ts) | Shell-free `execFile` adapter |
66
+ | [`src/path-opener.ts`](src/path-opener.ts) | Desktop detection, open intents, browser preference, and WSL translation |
67
+ | [`src/invariant.ts`](src/invariant.ts) | Invariant companion (no runtime invariant; each run is one stateless child-process round trip) |
68
+
69
+ ### What execFile gives the runner
70
+
71
+ `execFile` spawns the executable directly with an argv array — no shell string, no shell interpretation of the arguments. The `signal` option terminates the child when the caller's abort fires; `windowsHide` suppresses the transient console window on Windows. On a non-zero exit or spawn error, the callback attaches `code`, `stdout`, and `stderr` to the rejected error and keeps the original error as `cause`.
72
+
73
+ </details>
74
+
75
+ -----
76
+
77
+ <a id="further-exploration"></a>
78
+ ## Further Exploration
79
+
80
+ Read these pages when you need the consumers or the general subprocess capability this utility deliberately is not.
81
+
82
+ - [Native directory picker](../../host/directory-picker-native/README.md) — the OS chooser commands this runner executes.
83
+ - [Session Controller](../../api/session-controller/README.md) — resolves Session-relative workspace paths before opening them.
84
+ - [Settings Controller](../../api/settings-controller/README.md) — selects settings documents and agent-preset directories.
85
+ - [Subprocess capability](../../subprocess/subprocess/README.md) — the general subprocess seam, of which this package is not a part.
86
+
87
+ -----
88
+
89
+ <a id="model-experience"></a>
17
90
  ## Model Experience
18
91
 
19
- None, as this is host-side subprocess plumbing; nothing here reaches a model request.
92
+ None, as the host-side utilities register nothing model-facing.
20
93
 
21
94
  #### KV Cache effect
22
95
 
23
- None; this package neither assembles nor sends a provider request.
96
+ Nothing here enters a request prefix; this package neither assembles nor sends a provider request.
24
97
 
25
98
  ## Known Limitations and Deferred Work
26
99
 
100
+ <a id="known-limitations-and-deferred-work"></a>
101
+
102
+
103
+ These limits define when this runner is not the right tool. They are current package constraints, not a task backlog.
104
+
27
105
  - **No output bounding** — both streams buffer unbounded in memory; every current caller invokes small native tools whose output is a path or an error line. Adopt `dsh-output-retention` bounding before pointing this at commands with meaningful output volume.
106
+
107
+ <a id="dev-note"></a>
108
+ ### Dev Note
109
+
110
+ <details>
111
+ <summary>Working context for maintainers — click to expand</summary>
112
+
113
+ None.
114
+
115
+ </details>
package/README.zh.md CHANGED
@@ -1,27 +1,115 @@
1
- # dsh-native-command
1
+ ---
2
+ description: "宿主原生命令与路径打开工具,提供无 shell 执行、取消、桌面探测与 WSL 路径交接。"
3
+ kind: "package-library"
4
+ ---
5
+
6
+ # @deepseek-ai/dsh-native-command
2
7
 
3
8
  [English](README.md) | 中文
4
9
 
5
- 宿主原生 OS 集成共享的**零依赖免 shell `execFile` 运行器**:一次 `runNativeCommand(command, args, signal)` 调用直接 spawn 可执行文件(绝不拼 shell 字符串),以 utf8 捕获 stdout/stderr,把调用方的 abort 传播为子进程终止,并在 Windows 上隐藏瞬时控制台窗口。失败时,调用会以错误拒绝;该错误附带退出 `code` 与两路已捕获输出,调用方无需重跑即可分类(工具缺失、已取消、真实失败)。
10
+ ## 概述
11
+
12
+ `dsh-native-command` 无需 shell 即可运行 Host 可执行文件,并通过桌面打开 Host 文件系统路径。命令运行器捕获 utf8 输出、传播取消,并隐藏 Windows 瞬时控制台。路径打开器支持默认应用与文本编辑器意图、浏览器可渲染文档、WSL 转换与桌面可用性检查。它是库而非插件:没有 `ctx`、无状态、不发事件。
13
+
14
+ ## 目录
15
+
16
+ - [使用本包](#use-this-package)
17
+ - [理解实现](#understand-the-implementation)
18
+ - [进一步探索](#further-exploration)
19
+ - [模型体验](#model-experience)
20
+ - [已知限制与延期工作](#known-limitations-and-deferred-work)
21
+ - [开发备注](#dev-note)
6
22
 
7
- 它的两个消费方都是宿主侧原生集成:[`directory-picker-native`](../../host/directory-picker-native/README.zh.md) 后端的 OS 选择器命令,以及网关将路径交由默认应用打开的操作([`dsh-host-apiproxy`](../../host/apiproxy/README.zh.md) 的 `host.openPath`)。`NativeCommandRunner` 类型是这些调用方的可注入命令边界。
23
+ -----
8
24
 
9
- 它是**库,不是服务或插件**:没有 `ctx`、不注册任何东西、不持有状态、不发事件。
25
+ <a id="use-this-package"></a>
26
+ ## 使用本包
10
27
 
11
- ## 接口面
28
+ 当宿主侧集成需要执行一条原生命令、并需要它的输出或失败信息(或两者兼要)、且绝不能涉及 shell 时,使用本运行器。
29
+
30
+ ### 运行一条命令
12
31
 
13
32
  ```ts
14
- import { runNativeCommand, type NativeCommandRunner } from '@deepseek-ai/dsh-native-command'
33
+ import { runNativeCommand } from '@deepseek-ai/dsh-native-command'
34
+
35
+ declare const script: string
36
+ declare const signal: AbortSignal
37
+ const { stdout, stderr } = await runNativeCommand('osascript', ['-e', script], signal)
15
38
  ```
16
39
 
40
+ 退出码为 0 时,调用解析为捕获到的 stdout 与 stderr。任何失败都会以错误拒绝,错误附带退出 `code` 与两路已捕获输出,因此调用方无需重跑命令即可区分工具缺失(`ENOENT`)、取消(`ABORT_ERR`)与真实的命令失败。
41
+
42
+ ### 注入命令边界
43
+
44
+ `NativeCommandRunner` 类型是宿主集成的可注入命令边界:在集成需要一个可测试接缝的位置传入该函数(或其包装层),测试即可替换为假运行器。
45
+
46
+ ### 打开 Host 路径
47
+
48
+ `openNativePath(path, signal)` 将路径交给默认应用;平台能够确定默认浏览器时,HTML 与 SVG 会优先交给该浏览器。`openNativeTextFile(path, signal)` 选择文本编辑器意图;macOS 使用 `open -t`。WSL 路径先通过 `wslpath -w` 转换,再交给 Windows 桌面。`canOpenNativePath()` 报告当前 Host 是否可能具备桌面目标。
49
+
50
+ -----
51
+
52
+ <a id="understand-the-implementation"></a>
53
+ ## 理解实现
54
+
55
+ <details>
56
+ <summary>实现细节——点击展开</summary>
57
+
58
+ 命令运行器是 Node `execFile` 的薄包装。路径打开器根据平台与环境事实选择一条无 shell 命令,而调用方继续负责决定允许打开哪个路径。
59
+
60
+ ### 源码地图
61
+
62
+ | 文件 | 职责 |
63
+ |---|---|
64
+ | [`src/index.ts`](src/index.ts) | 命令运行器与路径打开器的公共导出 |
65
+ | [`src/runner.ts`](src/runner.ts) | 无 shell 的 `execFile` 适配器 |
66
+ | [`src/path-opener.ts`](src/path-opener.ts) | 桌面探测、打开意图、浏览器偏好与 WSL 转换 |
67
+ | [`src/invariant.ts`](src/invariant.ts) | 不变式伴生插件(无运行时不变式;每次运行都是一次无状态的子进程往返) |
68
+
69
+ ### execFile 给了运行器什么
70
+
71
+ `execFile` 以 argv 数组直接 spawn 可执行文件——没有 shell 字符串,参数不经 shell 解释。`signal` 选项在调用方中止触发时终止子进程;`windowsHide` 在 Windows 上抑制瞬时控制台窗口。遇到非零退出或 spawn 错误时,回调把 `code`、`stdout`、`stderr` 挂到被拒绝的错误上,并保留原始错误作为 `cause`。
72
+
73
+ </details>
74
+
75
+ -----
76
+
77
+ <a id="further-exploration"></a>
78
+ ## 进一步探索
79
+
80
+ 当你需要消费方或本工具刻意不属于的通用子进程能力时,阅读以下页面。
81
+
82
+ - [原生目录选择器](../../host/directory-picker-native/README.zh.md)——本运行器执行的 OS 选择器命令。
83
+ - [Session Controller](../../api/session-controller/README.zh.md)——打开前解析 Session 相对 workspace 路径。
84
+ - [Settings Controller](../../api/settings-controller/README.zh.md)——选择 settings 文档与 agent-preset 目录。
85
+ - [子进程能力](../../subprocess/subprocess/README.zh.md)——通用子进程 seam,本包并非其组成部分。
86
+
87
+ -----
88
+
89
+ <a id="model-experience"></a>
17
90
  ## 模型体验
18
91
 
19
- 无;这是宿主侧子进程管道,这里没有任何东西进入模型请求。
92
+ 无:宿主侧工具不注册任何面向模型的内容。
20
93
 
21
94
  #### KV Cache 影响
22
95
 
23
- 无;该包既不组装也不发送提供方请求。
96
+ 此处没有任何内容进入请求前缀;本包既不组装也不发送提供方请求。
97
+
98
+ ## 已知限制与延期工作
99
+
100
+ <a id="known-limitations-and-deferred-work"></a>
101
+
24
102
 
25
- ## 已知限制与暂缓事项
103
+ 这些限制说明本运行器何时不是合适的工具。它们是当前包约束,不是任务积压。
26
104
 
27
105
  - **不做输出限量**——两路流在内存中无界缓冲;当前每个调用方只运行输出为一个路径或一行错误的小型原生工具。把它指向输出量可观的命令之前,先接入 `dsh-output-retention` 限量。
106
+
107
+ <a id="dev-note"></a>
108
+ ### 开发备注
109
+
110
+ <details>
111
+ <summary>维护者的工作上下文——点击展开</summary>
112
+
113
+ 无。
114
+
115
+ </details>
package/lib/index.js CHANGED
@@ -1,11 +1,10 @@
1
1
  import { execFile } from "node:child_process";
2
- //#region lib/types/index.js
2
+ import { release } from "node:os";
3
+ import { extname } from "node:path";
4
+ //#region lib/types/runner.js
3
5
  /**
4
- * Shared no-shell `execFile` runner for host-native OS integrations (the
5
- * native directory chooser, the open-with-default-application hand-off):
6
- * utf8 stdio capture, abort propagation, Windows console hide. A library,
7
- * not a plugin — no ctx, no state, no events.
8
- * @module @deepseek-ai/dsh-native-command
6
+ * Shared no-shell `execFile` runner for host-native OS integrations.
7
+ * @module @deepseek-ai/dsh-native-command/runner
9
8
  */
10
9
  /**
11
10
  * Run a host command with utf8 stdio, abort propagation, and Windows hide.
@@ -35,4 +34,157 @@ const runNativeCommand = (command, args, signal) => new Promise((resolve, reject
35
34
  });
36
35
  });
37
36
  //#endregion
38
- export { runNativeCommand };
37
+ //#region lib/types/path-opener.js
38
+ /**
39
+ * Cross-platform native path and text-document openers for Host UI
40
+ * integrations.
41
+ *
42
+ * The default intent prefers the default browser for documents it renders when
43
+ * the platform can name one, then falls back to the default application. WSL
44
+ * translates every path for the Windows desktop instead of assuming a Linux
45
+ * GUI. The text-editor intent never consults the browser.
46
+ * @module @deepseek-ai/dsh-native-command/path-opener
47
+ */
48
+ /** Documents a browser renders, as opposed to ones an editor merely edits. */
49
+ const BROWSER_DOCUMENTS = new Set([
50
+ ".html",
51
+ ".htm",
52
+ ".xhtml",
53
+ ".svg"
54
+ ]);
55
+ /**
56
+ * The macOS bundle registered for `https` — the default browser, as
57
+ * LaunchServices records it. The nested version dict is stripped first
58
+ * because it carries its own `LSHandlerRoleAll`.
59
+ */
60
+ function macBundleForHttps(plist) {
61
+ const stripped = plist.replace(/LSHandlerPreferredVersions\s*=\s*\{[^}]*\};/g, "");
62
+ const block = /\{[^{}]*LSHandlerURLScheme\s*=\s*"?https"?;[^{}]*\}/.exec(stripped)?.[0];
63
+ if (block === void 0) return void 0;
64
+ return /LSHandlerRoleAll\s*=\s*"?([\w.-]+)"?;/.exec(block)?.[1];
65
+ }
66
+ /**
67
+ * Open one browser-renderable document with the default browser.
68
+ * @returns true when a browser took it; false when this platform cannot name
69
+ * one, or naming it failed — the caller then uses the default application.
70
+ */
71
+ async function openInBrowser(path, signal, platform, run, env) {
72
+ if (platform === "darwin") {
73
+ let bundle;
74
+ try {
75
+ const { stdout } = await run("defaults", ["read", "com.apple.LaunchServices/com.apple.launchservices.secure"], signal);
76
+ bundle = macBundleForHttps(stdout);
77
+ } catch {
78
+ return false;
79
+ }
80
+ if (bundle === void 0) return false;
81
+ await run("open", [
82
+ "-b",
83
+ bundle,
84
+ path
85
+ ], signal);
86
+ return true;
87
+ }
88
+ if (platform === "linux") {
89
+ const browser = env.BROWSER;
90
+ if (browser === void 0 || browser === "") return false;
91
+ await run(browser, [path], signal);
92
+ return true;
93
+ }
94
+ return false;
95
+ }
96
+ /** PowerShell single-quoted literal (doubles embedded quotes). */
97
+ function powershellLiteral(path) {
98
+ return `'${path.replace(/'/g, "''")}'`;
99
+ }
100
+ /** Whether one environment marker is set to a non-empty value. */
101
+ function present(value) {
102
+ return value !== void 0 && value !== "";
103
+ }
104
+ /** Distinguish WSL from desktop Linux using its process and kernel markers. */
105
+ function isWsl(internals) {
106
+ const env = internals.env ?? process.env;
107
+ if (present(env.WSL_DISTRO_NAME) || present(env.WSL_INTEROP)) return true;
108
+ return (internals.osRelease ?? release()).toLowerCase().includes("microsoft");
109
+ }
110
+ /** Open one Windows-resolvable path through its registered desktop application. */
111
+ async function openWindowsPath(path, signal, run) {
112
+ await run("powershell.exe", [
113
+ "-NoProfile",
114
+ "-Command",
115
+ `Invoke-Item -LiteralPath ${powershellLiteral(path)}`
116
+ ], signal);
117
+ }
118
+ /** Translate a WSL path before handing it to the Windows desktop. */
119
+ async function openWslPath(path, signal, run) {
120
+ const translated = await run("wslpath", ["-w", path], signal);
121
+ signal.throwIfAborted();
122
+ const windowsPath = translated.stdout.replace(/[\r\n]+$/, "");
123
+ if (windowsPath === "") throw new Error("wslpath returned no Windows path");
124
+ await openWindowsPath(windowsPath, signal, run);
125
+ }
126
+ /** Dispatch one shell-free platform command for the requested open intent. */
127
+ async function openNativePathWithIntent(path, signal, intent, internals = {}) {
128
+ const platform = internals.platform ?? process.platform;
129
+ const run = internals.run ?? runNativeCommand;
130
+ const env = internals.env ?? process.env;
131
+ const wsl = platform === "linux" && isWsl(internals);
132
+ if (!wsl && intent === "default" && BROWSER_DOCUMENTS.has(extname(path).toLowerCase()) && await openInBrowser(path, signal, platform, run, env)) return;
133
+ if (platform === "darwin") {
134
+ await run("open", intent === "text-editor" ? ["-t", path] : [path], signal);
135
+ return;
136
+ }
137
+ if (platform === "win32") {
138
+ await openWindowsPath(path, signal, run);
139
+ return;
140
+ }
141
+ if (platform === "linux") {
142
+ if (wsl) {
143
+ await openWslPath(path, signal, run);
144
+ return;
145
+ }
146
+ await run("xdg-open", [path], signal);
147
+ return;
148
+ }
149
+ throw new Error(`native path opener is unsupported on ${platform}`);
150
+ }
151
+ /**
152
+ * Whether {@link openNativePath} plausibly reaches a desktop on this host.
153
+ *
154
+ * macOS and Windows always carry a desktop opener; Linux does when it is WSL
155
+ * (the Windows desktop takes the path) or a display server is announced.
156
+ * A headless or containerised Linux host answers false, which is what lets a
157
+ * surface show a path as text instead of offering a button that would spawn
158
+ * `xdg-open` into nothing.
159
+ * @param internals - platform and environment seam for deterministic tests.
160
+ * @returns true when handing a path to the native opener can work at all.
161
+ */
162
+ function canOpenNativePath(internals = {}) {
163
+ const platform = internals.platform ?? process.platform;
164
+ if (platform === "darwin" || platform === "win32") return true;
165
+ if (platform !== "linux") return false;
166
+ const env = internals.env ?? process.env;
167
+ return isWsl(internals) || present(env.DISPLAY) || present(env.WAYLAND_DISPLAY);
168
+ }
169
+ /**
170
+ * Open a filesystem path with the operating system's default application, or
171
+ * with the default browser when the path names a document a browser renders.
172
+ * @param path - absolute or host-resolvable path (caller owns resolution).
173
+ * @param signal - caller/connection lifetime; abort terminates the native command.
174
+ * @param internals - Platform, environment, and runner hooks for deterministic tests.
175
+ */
176
+ function openNativePath(path, signal, internals = {}) {
177
+ return openNativePathWithIntent(path, signal, "default", internals);
178
+ }
179
+ /**
180
+ * Open a text document for editing; macOS bypasses the file-type association
181
+ * so a YAML association with a browser cannot consume the gesture.
182
+ * @param path - absolute or host-resolvable text-document path.
183
+ * @param signal - caller/connection lifetime; abort terminates the native command.
184
+ * @param internals - Platform and runner hooks for deterministic tests.
185
+ */
186
+ function openNativeTextFile(path, signal, internals = {}) {
187
+ return openNativePathWithIntent(path, signal, "text-editor", internals);
188
+ }
189
+ //#endregion
190
+ export { canOpenNativePath, openNativePath, openNativeTextFile, runNativeCommand };
@@ -1,21 +1,9 @@
1
1
  /**
2
- * Shared no-shell `execFile` runner for host-native OS integrations (the
3
- * native directory chooser, the open-with-default-application hand-off):
4
- * utf8 stdio capture, abort propagation, Windows console hide. A library,
5
- * not a plugin — no ctx, no state, no events.
2
+ * Host-native command execution and path-opening utilities.
6
3
  * @module @deepseek-ai/dsh-native-command
7
4
  */
8
- /** Testable command boundary; native implementations never invoke a shell. */
9
- export type NativeCommandRunner = (command: string, args: readonly string[], signal: AbortSignal) => Promise<{
10
- stdout: string;
11
- stderr: string;
12
- }>;
13
- /**
14
- * Run a host command with utf8 stdio, abort propagation, and Windows hide.
15
- * @param command - executable path or PATH name.
16
- * @param args - argv (never a shell string).
17
- * @param signal - caller/connection lifetime; abort terminates the child.
18
- * @returns captured stdout/stderr on exit 0.
19
- */
20
- export declare const runNativeCommand: NativeCommandRunner;
5
+ export { runNativeCommand } from './runner.ts';
6
+ export type { NativeCommandRunner } from './runner.ts';
7
+ export { canOpenNativePath, openNativePath, openNativeTextFile, } from './path-opener.ts';
8
+ export type { PathOpenerInternals, PathOpenerRunner, } from './path-opener.ts';
21
9
  //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Cross-platform native path and text-document openers for Host UI
3
+ * integrations.
4
+ *
5
+ * The default intent prefers the default browser for documents it renders when
6
+ * the platform can name one, then falls back to the default application. WSL
7
+ * translates every path for the Windows desktop instead of assuming a Linux
8
+ * GUI. The text-editor intent never consults the browser.
9
+ * @module @deepseek-ai/dsh-native-command/path-opener
10
+ */
11
+ import { type NativeCommandRunner } from './runner.ts';
12
+ /** Testable command boundary; native implementations never invoke a shell. */
13
+ export type PathOpenerRunner = NativeCommandRunner;
14
+ /** Injectable platform facts for deterministic adapter tests. */
15
+ export interface PathOpenerInternals {
16
+ platform?: NodeJS.Platform;
17
+ /** Kernel release override used to distinguish WSL from desktop Linux. */
18
+ osRelease?: string;
19
+ /** Environment used for WSL markers and the desktop Linux browser convention. */
20
+ env?: NodeJS.ProcessEnv;
21
+ run?: PathOpenerRunner;
22
+ }
23
+ /**
24
+ * Whether {@link openNativePath} plausibly reaches a desktop on this host.
25
+ *
26
+ * macOS and Windows always carry a desktop opener; Linux does when it is WSL
27
+ * (the Windows desktop takes the path) or a display server is announced.
28
+ * A headless or containerised Linux host answers false, which is what lets a
29
+ * surface show a path as text instead of offering a button that would spawn
30
+ * `xdg-open` into nothing.
31
+ * @param internals - platform and environment seam for deterministic tests.
32
+ * @returns true when handing a path to the native opener can work at all.
33
+ */
34
+ export declare function canOpenNativePath(internals?: PathOpenerInternals): boolean;
35
+ /**
36
+ * Open a filesystem path with the operating system's default application, or
37
+ * with the default browser when the path names a document a browser renders.
38
+ * @param path - absolute or host-resolvable path (caller owns resolution).
39
+ * @param signal - caller/connection lifetime; abort terminates the native command.
40
+ * @param internals - Platform, environment, and runner hooks for deterministic tests.
41
+ */
42
+ export declare function openNativePath(path: string, signal: AbortSignal, internals?: PathOpenerInternals): Promise<void>;
43
+ /**
44
+ * Open a text document for editing; macOS bypasses the file-type association
45
+ * so a YAML association with a browser cannot consume the gesture.
46
+ * @param path - absolute or host-resolvable text-document path.
47
+ * @param signal - caller/connection lifetime; abort terminates the native command.
48
+ * @param internals - Platform and runner hooks for deterministic tests.
49
+ */
50
+ export declare function openNativeTextFile(path: string, signal: AbortSignal, internals?: PathOpenerInternals): Promise<void>;
51
+ //# sourceMappingURL=path-opener.d.ts.map
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Shared no-shell `execFile` runner for host-native OS integrations.
3
+ * @module @deepseek-ai/dsh-native-command/runner
4
+ */
5
+ /** Testable command boundary; native implementations never invoke a shell. */
6
+ export type NativeCommandRunner = (command: string, args: readonly string[], signal: AbortSignal) => Promise<{
7
+ stdout: string;
8
+ stderr: string;
9
+ }>;
10
+ /**
11
+ * Run a host command with utf8 stdio, abort propagation, and Windows hide.
12
+ * @param command - executable path or PATH name.
13
+ * @param args - argv (never a shell string).
14
+ * @param signal - caller/connection lifetime; abort terminates the child.
15
+ * @returns captured stdout/stderr on exit 0.
16
+ */
17
+ export declare const runNativeCommand: NativeCommandRunner;
18
+ //# sourceMappingURL=runner.d.ts.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-native-command",
3
- "description": "Zero-dependency no-shell execFile runner for host-native OS integrations: utf8 stdio capture, abort propagation, Windows hide",
4
- "version": "0.1.1-rc.2",
3
+ "description": "Host-native command and path-opening utilities with shell-free execution, cancellation, desktop detection, and WSL handoff",
4
+ "version": "0.1.2-alpha.2",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -32,11 +32,11 @@
32
32
  ],
33
33
  "license": "MIT",
34
34
  "peerDependencies": {
35
- "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
36
- "@deepseek-ai/cordis": "^4.0.1"
35
+ "@deepseek-ai/cordis": "^4.0.2",
36
+ "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.2"
37
37
  },
38
38
  "devDependencies": {
39
- "@deepseek-ai/cordis": "^4.0.1",
40
- "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2"
39
+ "@deepseek-ai/cordis": "^4.0.2",
40
+ "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.2"
41
41
  }
42
42
  }