@xiaoso/dsh-tool-plus 0.1.15 → 0.1.16
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.en.md +164 -164
- package/lib/index.mjs +2 -2
- package/lib/pi-natives-bootstrap.mjs +34 -0
- package/lib/{scrapers-Dm5OqvyJ.mjs → scrapers-C1ujjV8k.mjs} +1 -1
- package/lib/{utils-CW2Qi8Gk.mjs → utils-CE3DSebh.mjs} +1 -1
- package/package.json +3 -3
- package/scripts/patch-pi-natives.mjs +241 -108
package/README.en.md
CHANGED
|
@@ -1,164 +1,164 @@
|
|
|
1
|
-
<div align="center">
|
|
2
|
-
<img width="160" src="assets/icon.png" alt="dsh tool plus icon"><br>
|
|
3
|
-
<img width="240" src="assets/wordmark.png" alt="dsh tool plus">
|
|
4
|
-
</div>
|
|
5
|
-
|
|
6
|
-
<div align="center">
|
|
7
|
-
|
|
8
|
-
[简体中文](./README.md) | English
|
|
9
|
-
|
|
10
|
-
</div>
|
|
11
|
-
|
|
12
|
-
<div align="center">
|
|
13
|
-
|
|
14
|
-
[](https://www.npmjs.com/package/@xiaoso/dsh-tool-plus)
|
|
15
|
-
[](LICENSE)
|
|
16
|
-
[](https://nodejs.org)
|
|
17
|
-
[]()
|
|
18
|
-
|
|
19
|
-
</div>
|
|
20
|
-
|
|
21
|
-
Essential tools enhancement for DeepSeek Harness: persistent bash, structured read, multi-mode edit, atomic write, full-text search, and direct image reading — one plugin covers it all. Ported from the [Oh My Pi](https://github.com/can1357/oh-my-pi) core; once installed it automatically takes over the official bash / pwsh / file / search tools, with optional `ast_grep` / `ast_edit` structural search and rewrite.
|
|
22
|
-
|
|
23
|
-
## Features
|
|
24
|
-
|
|
25
|
-
- **bash**: persistent shell; `cd` and `export` keep state across calls; verbose logs (git/npm/cargo…) are condensed automatically; overlong output keeps only head and tail, with the full content written to disk for later retrieval; long-running commands are moved to the background automatically; optionally intercepts `cat`, `grep`, `find`, and `sed -i` to steer you toward the dedicated tools (off by default)
|
|
26
|
-
- **Safe delete**: `rm` moves files to the system trash by default instead of deleting permanently, so accidental deletes are recoverable; use `command rm` for a real delete
|
|
27
|
-
- **read**: precise line-range reading (supports `:N-M`, `:raw`, and multiple ranges); large code files return a structural summary by default, with details expanded on demand; zip/tar archives, SQLite, notebooks, and PDFs read directly; PNG / JPEG / WebP / GIF images read directly, oversized ones scaled down automatically; can fetch web content (including images embedded in the page); SPA sites (content rendered dynamically by JS, e.g. excalidraw.com) are automatically fetched via local browser rendering
|
|
28
|
-
- **write**: atomic writes that return a diff of the changes; supports patch-style writing, and can write directly into zip/tar archive members and SQLite data
|
|
29
|
-
- **edit**: `replace` by default, with patch / hashline / apply-patch formats also supported; multi-hunk edits, uniqueness validation, fuzzy matching tolerant of whitespace differences
|
|
30
|
-
- **grep / glob**: full-text search and filename matching; mtime sorting, context lines, and configurable ignore rules
|
|
31
|
-
- **ast_grep / ast_edit** (optional): syntax-tree based structural code search and rewrite, enabled in settings
|
|
32
|
-
- **agent presets**: two companion templates — Standard enhanced and PTC (Code Mode)
|
|
33
|
-
|
|
34
|
-
Settings panel (Bash tab):
|
|
35
|
-
|
|
36
|
-

|
|
37
|
-
|
|
38
|
-
## Recommended Environment & Configuration
|
|
39
|
-
|
|
40
|
-
- **Full-access mode**: the plugin is best used under `danger-full-access`, which avoids file writes being blocked by mistake in sandboxed modes.
|
|
41
|
-
|
|
42
|
-
## Installation
|
|
43
|
-
|
|
44
|
-
### Install from npm (recommended)
|
|
45
|
-
|
|
46
|
-
#### Web
|
|
47
|
-
|
|
48
|
-
```sh
|
|
49
|
-
dsh plugin --profile web add --allow-build=@xiaoso/dsh-tool-plus --allow-build=koffi @xiaoso/dsh-tool-plus
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
`--allow-build` lets install scripts run (once for this package, once for `koffi`); it requires pnpm ≥ 10.4, and on pnpm 11+ omitting it fails the install with `ERR_PNPM_IGNORED_BUILDS`. If your pnpm is too old for the flag, allow the scripts first and re-run:
|
|
53
|
-
|
|
54
|
-
- Append to `~/.dsh/profiles/web/pnpm-workspace.yaml`: `allowBuilds:`, ` '@xiaoso/dsh-tool-plus': true` and ` koffi: true`
|
|
55
|
-
- Or run `cd ~/.dsh/profiles/web && pnpm approve-builds --all` (pnpm ≥ 10.32)
|
|
56
|
-
|
|
57
|
-
#### Desktop
|
|
58
|
-
|
|
59
|
-
The profile is owned by the desktop app, so `dsh plugin --profile desktop` is refused; install inside the profile directory instead:
|
|
60
|
-
|
|
61
|
-
```sh
|
|
62
|
-
cd ~/.dsh/profiles/desktop
|
|
63
|
-
pnpm add --allow-build=@xiaoso/dsh-tool-plus --allow-build=koffi @xiaoso/dsh-tool-plus
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
### Local development
|
|
67
|
-
|
|
68
|
-
```sh
|
|
69
|
-
dsh plugin --profile web add link:<path to this repo>
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
### Presets
|
|
73
|
-
|
|
74
|
-
| Preset | Base | What it is |
|
|
75
|
-
|---|---|---|
|
|
76
|
-
| **Tool Plus 标准增强版** | official standard | everything in standard mode, with the file/shell toolset swapped for this plugin |
|
|
77
|
-
| **Tool Plus PTC 增强版** | official PTC (Code Mode) | same, but composing multi-step operations through Code Mode's `run_code` |
|
|
78
|
-
|
|
79
|
-
Both presets are **declared by the plugin**: installing it (as a profile bundle) brings two `@deepseek-ai/dsh-agent-preset` declaration rows along in its patch layer, so they appear in the preset list immediately. There is no install step, and the plugin writes nothing at startup.
|
|
80
|
-
|
|
81
|
-
**Tool Plus → Presets** in the settings page is where you change them. Since dsh 0.1.7 the official preset page is read-only and the `agent_preset` tool is gone, so this panel is the only graphical entry point:
|
|
82
|
-
|
|
83
|
-
- **Minimal update** — disables just the official tool rows still mounted, touching nothing else
|
|
84
|
-
- **Align to template** — replaces this row's whole plugin list with the selected template
|
|
85
|
-
- **Revert to bundled** — removes the override you wrote in the profile configuration (`<profile>/cordis.patch.yml`), falling back to the bundled declaration
|
|
86
|
-
- **View differences** — shows which entries differ before you touch anything
|
|
87
|
-
|
|
88
|
-
Writes go through the harness's own `ctx.configEditor`, so they inherit the profile lock, concurrency protection, atomic write and rollback.
|
|
89
|
-
|
|
90
|
-
> Upgrading from 0.1.9 or earlier: `~/.dsh/.agent-presets/` is **read by nothing** any more (0.1.7 moved presets into the patch layer), so it is safe to delete; the panel tells you when it finds one.
|
|
91
|
-
|
|
92
|
-
## Configuration
|
|
93
|
-
|
|
94
|
-
Works out of the box, no configuration needed. Common tweaks: background threshold (`autoBackgroundMs`), timeouts (`defaultTimeoutMs` / `maxTimeoutMs`), the output truncation window, the default edit mode (`editMode`, default `replace`), and the structured summary toggle (`readSummarizeEnabled`).
|
|
95
|
-
|
|
96
|
-
## Performance
|
|
97
|
-
|
|
98
|
-
On Linux, comparing the official standard mode with this plugin's Standard enhanced preset (SWE-bench Multilingual, 20 tasks × 3 rounds):
|
|
99
|
-
|
|
100
|
-
| Metric | Official tool surface | This plugin (command interception on) | This plugin (command interception off, default) |
|
|
101
|
-
|---|---|---|---|
|
|
102
|
-
| Resolve rate | 83.3% | **83.3%** | 81.7% (1 task lower, within normal variance) |
|
|
103
|
-
| Steps / task | 24.4 | 29.7 (+21.7%) | 25.3 (+3.8%) |
|
|
104
|
-
| Total tokens / task | **729,434** | 954,100 (+30.8%) | 779,799 (+6.9%) |
|
|
105
|
-
| System prompt / step (incl. tool definitions) | **4,642** | 6,305 (+35.8%) | 6,305 (+35.8%) |
|
|
106
|
-
| System prompt total / task (per step × steps) | **113,265** | 187,259 (+65.3%) | 159,622 (+40.9%) |
|
|
107
|
-
| Net tokens / task (excl. system prompt) | **616,169** | 766,841 (+24.5%) | 620,177 (+0.7%) |
|
|
108
|
-
| Average context length at task end (tokens) | **42,817** | 48,645 (+13.6%) | 44,781 (+4.6%) |
|
|
109
|
-
| `read` calls | 225 | 459 | **198** |
|
|
110
|
-
| `bash` time per call | 3,140 ms | **2,391 ms** | 2,045 ms |
|
|
111
|
-
| `bash` tokens returned per call | 790 tok | **343 tok** | 638 tok |
|
|
112
|
-
| Average time / task (excludes token-rate and network-speed fluctuation) | **123 s** | 135 s | 151 s |
|
|
113
|
-
|
|
114
|
-
Notes:
|
|
115
|
-
|
|
116
|
-
1. The average time is an end-to-end measurement that does not account for token-rate or network-speed fluctuation; use the absolute values for horizontal comparison only.
|
|
117
|
-
2. The figures above describe this benchmark only (its tasks use little context, so they do not represent large tasks).
|
|
118
|
-
|
|
119
|
-
Conclusions:
|
|
120
|
-
|
|
121
|
-
1. The resolve rate matches the official tools (the interception-off group is 1 task lower, within normal variance).
|
|
122
|
-
2. `bash` itself is leaner, and the tokens it saves roughly offset the extra system prompt from thicker tool definitions (the plugin adds capabilities, so its definitions are thicker than the official preset's).
|
|
123
|
-
3. `bash` is faster, but the gain is cancelled out by side effects from `bash` itself and the other enhanced tools.
|
|
124
|
-
4. On Linux the combined performance is on par with the official standard-mode tools, with a richer feature set.
|
|
125
|
-
|
|
126
|
-
So on Linux this plugin cannot promise lower token usage or better performance; Windows is not suitable for running the standard benchmark, so the only claim there is a **personal impression** that it is more pleasant to use than PowerShell.
|
|
127
|
-
|
|
128
|
-
Full data and the per-task matrix: [docs/benchmark/sbml-compare-ops.md](docs/benchmark/sbml-compare-ops.md) (Chinese).
|
|
129
|
-
|
|
130
|
-
## Requirements
|
|
131
|
-
|
|
132
|
-
- **dsh CLI**: installed globally, `npm i -g @deepseek-ai/dsh`
|
|
133
|
-
- **Node.js** ≥ 22.19 or ≥ 24
|
|
134
|
-
- **Git Bash** (recommended): serves as the bash execution environment on Windows
|
|
135
|
-
- Targets DeepSeek Harness `dsh` v0.2.0-rc.2 (pre-release; interfaces may change)
|
|
136
|
-
|
|
137
|
-
## Notes
|
|
138
|
-
|
|
139
|
-
- No standalone `pwsh` tool: shell work is handled by the more capable persistent `bash`
|
|
140
|
-
|
|
141
|
-
**Safe rm sends deleted files to the system trash.** Where they land, and whether you can see them:
|
|
142
|
-
|
|
143
|
-
| | Windows | macOS | Linux |
|
|
144
|
-
|---|---|---|---|
|
|
145
|
-
| **Location** | Recycle Bin | Trash `~/.Trash` | `$XDG_DATA_HOME/Trash`; defaults to `~/.local/share/Trash` |
|
|
146
|
-
| **Another volume / external disk** | That volume's Recycle Bin | That volume's `.Trashes` | `.Trash-<uid>` at that mount point |
|
|
147
|
-
| **Visible in the OS trash** | Yes | Yes | Same volume: yes; another volume: open that location |
|
|
148
|
-
| **File name** | Unchanged | Unchanged | A UUID; the original name comes from `.trashinfo` |
|
|
149
|
-
| **Put back** | Yes | Yes | Depends on the file manager |
|
|
150
|
-
| **Implementation** | Shell API | System API | Third-party XDG implementation |
|
|
151
|
-
| **Known limits** | No Recycle Bin on network drives | Requires macOS 10.12+ | Some desktops do not list it — restore from the location above |
|
|
152
|
-
|
|
153
|
-
## Build
|
|
154
|
-
|
|
155
|
-
```sh
|
|
156
|
-
pnpm install
|
|
157
|
-
pnpm build # type declarations (tsc) + tsdown bundle + asset copy
|
|
158
|
-
pnpm typecheck
|
|
159
|
-
pnpm test # includes real-bash cases; needs Git Bash on Windows, auto-skips when missing
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
## License
|
|
163
|
-
|
|
164
|
-
[MIT](LICENSE); third-party component licenses in [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
|
|
1
|
+
<div align="center">
|
|
2
|
+
<img width="160" src="assets/icon.png" alt="dsh tool plus icon"><br>
|
|
3
|
+
<img width="240" src="assets/wordmark.png" alt="dsh tool plus">
|
|
4
|
+
</div>
|
|
5
|
+
|
|
6
|
+
<div align="center">
|
|
7
|
+
|
|
8
|
+
[简体中文](./README.md) | English
|
|
9
|
+
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
<div align="center">
|
|
13
|
+
|
|
14
|
+
[](https://www.npmjs.com/package/@xiaoso/dsh-tool-plus)
|
|
15
|
+
[](LICENSE)
|
|
16
|
+
[](https://nodejs.org)
|
|
17
|
+
[]()
|
|
18
|
+
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
Essential tools enhancement for DeepSeek Harness: persistent bash, structured read, multi-mode edit, atomic write, full-text search, and direct image reading — one plugin covers it all. Ported from the [Oh My Pi](https://github.com/can1357/oh-my-pi) core; once installed it automatically takes over the official bash / pwsh / file / search tools, with optional `ast_grep` / `ast_edit` structural search and rewrite.
|
|
22
|
+
|
|
23
|
+
## Features
|
|
24
|
+
|
|
25
|
+
- **bash**: persistent shell; `cd` and `export` keep state across calls; verbose logs (git/npm/cargo…) are condensed automatically; overlong output keeps only head and tail, with the full content written to disk for later retrieval; long-running commands are moved to the background automatically; optionally intercepts `cat`, `grep`, `find`, and `sed -i` to steer you toward the dedicated tools (off by default)
|
|
26
|
+
- **Safe delete**: `rm` moves files to the system trash by default instead of deleting permanently, so accidental deletes are recoverable; use `command rm` for a real delete
|
|
27
|
+
- **read**: precise line-range reading (supports `:N-M`, `:raw`, and multiple ranges); large code files return a structural summary by default, with details expanded on demand; zip/tar archives, SQLite, notebooks, and PDFs read directly; PNG / JPEG / WebP / GIF images read directly, oversized ones scaled down automatically; can fetch web content (including images embedded in the page); SPA sites (content rendered dynamically by JS, e.g. excalidraw.com) are automatically fetched via local browser rendering
|
|
28
|
+
- **write**: atomic writes that return a diff of the changes; supports patch-style writing, and can write directly into zip/tar archive members and SQLite data
|
|
29
|
+
- **edit**: `replace` by default, with patch / hashline / apply-patch formats also supported; multi-hunk edits, uniqueness validation, fuzzy matching tolerant of whitespace differences
|
|
30
|
+
- **grep / glob**: full-text search and filename matching; mtime sorting, context lines, and configurable ignore rules
|
|
31
|
+
- **ast_grep / ast_edit** (optional): syntax-tree based structural code search and rewrite, enabled in settings
|
|
32
|
+
- **agent presets**: two companion templates — Standard enhanced and PTC (Code Mode)
|
|
33
|
+
|
|
34
|
+
Settings panel (Bash tab):
|
|
35
|
+
|
|
36
|
+

|
|
37
|
+
|
|
38
|
+
## Recommended Environment & Configuration
|
|
39
|
+
|
|
40
|
+
- **Full-access mode**: the plugin is best used under `danger-full-access`, which avoids file writes being blocked by mistake in sandboxed modes.
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
### Install from npm (recommended)
|
|
45
|
+
|
|
46
|
+
#### Web
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
dsh plugin --profile web add --allow-build=@xiaoso/dsh-tool-plus --allow-build=koffi @xiaoso/dsh-tool-plus
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`--allow-build` lets install scripts run (once for this package, once for `koffi`); it requires pnpm ≥ 10.4, and on pnpm 11+ omitting it fails the install with `ERR_PNPM_IGNORED_BUILDS`. If your pnpm is too old for the flag, allow the scripts first and re-run:
|
|
53
|
+
|
|
54
|
+
- Append to `~/.dsh/profiles/web/pnpm-workspace.yaml`: `allowBuilds:`, ` '@xiaoso/dsh-tool-plus': true` and ` koffi: true`
|
|
55
|
+
- Or run `cd ~/.dsh/profiles/web && pnpm approve-builds --all` (pnpm ≥ 10.32)
|
|
56
|
+
|
|
57
|
+
#### Desktop
|
|
58
|
+
|
|
59
|
+
The profile is owned by the desktop app, so `dsh plugin --profile desktop` is refused; install inside the profile directory instead:
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
cd ~/.dsh/profiles/desktop
|
|
63
|
+
pnpm add --allow-build=@xiaoso/dsh-tool-plus --allow-build=koffi @xiaoso/dsh-tool-plus
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Local development
|
|
67
|
+
|
|
68
|
+
```sh
|
|
69
|
+
dsh plugin --profile web add link:<path to this repo>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Presets
|
|
73
|
+
|
|
74
|
+
| Preset | Base | What it is |
|
|
75
|
+
|---|---|---|
|
|
76
|
+
| **Tool Plus 标准增强版** | official standard | everything in standard mode, with the file/shell toolset swapped for this plugin |
|
|
77
|
+
| **Tool Plus PTC 增强版** | official PTC (Code Mode) | same, but composing multi-step operations through Code Mode's `run_code` |
|
|
78
|
+
|
|
79
|
+
Both presets are **declared by the plugin**: installing it (as a profile bundle) brings two `@deepseek-ai/dsh-agent-preset` declaration rows along in its patch layer, so they appear in the preset list immediately. There is no install step, and the plugin writes nothing at startup.
|
|
80
|
+
|
|
81
|
+
**Tool Plus → Presets** in the settings page is where you change them. Since dsh 0.1.7 the official preset page is read-only and the `agent_preset` tool is gone, so this panel is the only graphical entry point:
|
|
82
|
+
|
|
83
|
+
- **Minimal update** — disables just the official tool rows still mounted, touching nothing else
|
|
84
|
+
- **Align to template** — replaces this row's whole plugin list with the selected template
|
|
85
|
+
- **Revert to bundled** — removes the override you wrote in the profile configuration (`<profile>/cordis.patch.yml`), falling back to the bundled declaration
|
|
86
|
+
- **View differences** — shows which entries differ before you touch anything
|
|
87
|
+
|
|
88
|
+
Writes go through the harness's own `ctx.configEditor`, so they inherit the profile lock, concurrency protection, atomic write and rollback.
|
|
89
|
+
|
|
90
|
+
> Upgrading from 0.1.9 or earlier: `~/.dsh/.agent-presets/` is **read by nothing** any more (0.1.7 moved presets into the patch layer), so it is safe to delete; the panel tells you when it finds one.
|
|
91
|
+
|
|
92
|
+
## Configuration
|
|
93
|
+
|
|
94
|
+
Works out of the box, no configuration needed. Common tweaks: background threshold (`autoBackgroundMs`), timeouts (`defaultTimeoutMs` / `maxTimeoutMs`), the output truncation window, the default edit mode (`editMode`, default `replace`), and the structured summary toggle (`readSummarizeEnabled`).
|
|
95
|
+
|
|
96
|
+
## Performance
|
|
97
|
+
|
|
98
|
+
On Linux, comparing the official standard mode with this plugin's Standard enhanced preset (SWE-bench Multilingual, 20 tasks × 3 rounds):
|
|
99
|
+
|
|
100
|
+
| Metric | Official tool surface | This plugin (command interception on) | This plugin (command interception off, default) |
|
|
101
|
+
|---|---|---|---|
|
|
102
|
+
| Resolve rate | 83.3% | **83.3%** | 81.7% (1 task lower, within normal variance) |
|
|
103
|
+
| Steps / task | 24.4 | 29.7 (+21.7%) | 25.3 (+3.8%) |
|
|
104
|
+
| Total tokens / task | **729,434** | 954,100 (+30.8%) | 779,799 (+6.9%) |
|
|
105
|
+
| System prompt / step (incl. tool definitions) | **4,642** | 6,305 (+35.8%) | 6,305 (+35.8%) |
|
|
106
|
+
| System prompt total / task (per step × steps) | **113,265** | 187,259 (+65.3%) | 159,622 (+40.9%) |
|
|
107
|
+
| Net tokens / task (excl. system prompt) | **616,169** | 766,841 (+24.5%) | 620,177 (+0.7%) |
|
|
108
|
+
| Average context length at task end (tokens) | **42,817** | 48,645 (+13.6%) | 44,781 (+4.6%) |
|
|
109
|
+
| `read` calls | 225 | 459 | **198** |
|
|
110
|
+
| `bash` time per call | 3,140 ms | **2,391 ms** | 2,045 ms |
|
|
111
|
+
| `bash` tokens returned per call | 790 tok | **343 tok** | 638 tok |
|
|
112
|
+
| Average time / task (excludes token-rate and network-speed fluctuation) | **123 s** | 135 s | 151 s |
|
|
113
|
+
|
|
114
|
+
Notes:
|
|
115
|
+
|
|
116
|
+
1. The average time is an end-to-end measurement that does not account for token-rate or network-speed fluctuation; use the absolute values for horizontal comparison only.
|
|
117
|
+
2. The figures above describe this benchmark only (its tasks use little context, so they do not represent large tasks).
|
|
118
|
+
|
|
119
|
+
Conclusions:
|
|
120
|
+
|
|
121
|
+
1. The resolve rate matches the official tools (the interception-off group is 1 task lower, within normal variance).
|
|
122
|
+
2. `bash` itself is leaner, and the tokens it saves roughly offset the extra system prompt from thicker tool definitions (the plugin adds capabilities, so its definitions are thicker than the official preset's).
|
|
123
|
+
3. `bash` is faster, but the gain is cancelled out by side effects from `bash` itself and the other enhanced tools.
|
|
124
|
+
4. On Linux the combined performance is on par with the official standard-mode tools, with a richer feature set.
|
|
125
|
+
|
|
126
|
+
So on Linux this plugin cannot promise lower token usage or better performance; Windows is not suitable for running the standard benchmark, so the only claim there is a **personal impression** that it is more pleasant to use than PowerShell.
|
|
127
|
+
|
|
128
|
+
Full data and the per-task matrix: [docs/benchmark/sbml-compare-ops.md](docs/benchmark/sbml-compare-ops.md) (Chinese).
|
|
129
|
+
|
|
130
|
+
## Requirements
|
|
131
|
+
|
|
132
|
+
- **dsh CLI**: installed globally, `npm i -g @deepseek-ai/dsh`
|
|
133
|
+
- **Node.js** ≥ 22.19 or ≥ 24
|
|
134
|
+
- **Git Bash** (recommended): serves as the bash execution environment on Windows
|
|
135
|
+
- Targets DeepSeek Harness `dsh` v0.2.0-rc.2 (pre-release; interfaces may change)
|
|
136
|
+
|
|
137
|
+
## Notes
|
|
138
|
+
|
|
139
|
+
- No standalone `pwsh` tool: shell work is handled by the more capable persistent `bash`
|
|
140
|
+
|
|
141
|
+
**Safe rm sends deleted files to the system trash.** Where they land, and whether you can see them:
|
|
142
|
+
|
|
143
|
+
| | Windows | macOS | Linux |
|
|
144
|
+
|---|---|---|---|
|
|
145
|
+
| **Location** | Recycle Bin | Trash `~/.Trash` | `$XDG_DATA_HOME/Trash`; defaults to `~/.local/share/Trash` |
|
|
146
|
+
| **Another volume / external disk** | That volume's Recycle Bin | That volume's `.Trashes` | `.Trash-<uid>` at that mount point |
|
|
147
|
+
| **Visible in the OS trash** | Yes | Yes | Same volume: yes; another volume: open that location |
|
|
148
|
+
| **File name** | Unchanged | Unchanged | A UUID; the original name comes from `.trashinfo` |
|
|
149
|
+
| **Put back** | Yes | Yes | Depends on the file manager |
|
|
150
|
+
| **Implementation** | Shell API | System API | Third-party XDG implementation |
|
|
151
|
+
| **Known limits** | No Recycle Bin on network drives | Requires macOS 10.12+ | Some desktops do not list it — restore from the location above |
|
|
152
|
+
|
|
153
|
+
## Build
|
|
154
|
+
|
|
155
|
+
```sh
|
|
156
|
+
pnpm install
|
|
157
|
+
pnpm build # type declarations (tsc) + tsdown bundle + asset copy
|
|
158
|
+
pnpm typecheck
|
|
159
|
+
pnpm test # includes real-bash cases; needs Git Bash on Windows, auto-skips when missing
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## License
|
|
163
|
+
|
|
164
|
+
[MIT](LICENSE); third-party component licenses in [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
|
package/lib/index.mjs
CHANGED
|
@@ -3,7 +3,7 @@ __installBunShim();
|
|
|
3
3
|
import { n as fs_promises_shim_exports, t as exists } from "./fs-promises-shim-B2YFWkcd.mjs";
|
|
4
4
|
import { i as setRuntimeLogger, n as systemChromiumCandidatesForTest, r as runtimeLogger } from "./launch-BIWNyrBB.mjs";
|
|
5
5
|
import { $ as isEnoent, A as formatCount, C as warn, D as installBunShim, F as $flag, N as $env, O as formatAge, P as $envpos, Q as hasFsCode, U as getAutoQaDbPath, V as VERSION, X as getRemoteDir, Y as getPuppeteerDir, at as findOnPath, b as debug, c as combineSignals, d as $which, et as isEnotdir, it as untilAborted, k as formatBytes$1, l as exec, m as readImageMetadata, nt as peekFile, q as getInstallId, tt as isProbablyBinary, u as render, x as error } from "./src-AELUUb5U.mjs";
|
|
6
|
-
import { C as truncateToWidth$1, S as replaceTabs, _ as formatCodeFrameLine, a as fetchBinary, b as LRUCache, c as getParallelExtractContent, d as convertFileWithMarkit, f as Settings, g as capParseErrors, h as OMP_DEFAULTS, i as convertWithMarkit, l as ensureTool, m as settings, o as extractWithParallel, p as isSettingsInitialized, s as findParallelApiKey, u as CONVERTIBLE_EXTENSIONS, v as formatParseErrors, x as padding, y as shortenPath } from "./utils-
|
|
6
|
+
import { C as truncateToWidth$1, S as replaceTabs, _ as formatCodeFrameLine, a as fetchBinary, b as LRUCache, c as getParallelExtractContent, d as convertFileWithMarkit, f as Settings, g as capParseErrors, h as OMP_DEFAULTS, i as convertWithMarkit, l as ensureTool, m as settings, o as extractWithParallel, p as isSettingsInitialized, s as findParallelApiKey, u as CONVERTIBLE_EXTENSIONS, v as formatParseErrors, x as padding, y as shortenPath } from "./utils-CE3DSebh.mjs";
|
|
7
7
|
import { t as sanitizeText } from "./sanitize-text-BCf5KmCf.mjs";
|
|
8
8
|
import { n as isRecord$5 } from "./type-guards-CttAANEx.mjs";
|
|
9
9
|
import { n as ToolError, r as throwIfAborted, t as ToolAbortError } from "./tool-errors-DoyxivAR.mjs";
|
|
@@ -11479,7 +11479,7 @@ let specialHandlersPromise;
|
|
|
11479
11479
|
* requires a special handler, so we keep them out of the cold-startup graph.
|
|
11480
11480
|
*/
|
|
11481
11481
|
function loadSpecialHandlers() {
|
|
11482
|
-
specialHandlersPromise ??= import("./scrapers-
|
|
11482
|
+
specialHandlersPromise ??= import("./scrapers-C1ujjV8k.mjs").then((m) => m.specialHandlers);
|
|
11483
11483
|
return specialHandlersPromise;
|
|
11484
11484
|
}
|
|
11485
11485
|
/**
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tool-plus package entry — pi-natives compatibility bootstrap.
|
|
3
|
+
*
|
|
4
|
+
* \`@oh-my-pi/pi-natives\` ships a Bun-only \`import.meta.dir\` in
|
|
5
|
+
* native/loader-state.js. A postinstall patch alone is not enough under pnpm:
|
|
6
|
+
* the file is a hardlink to the content-addressable store, and a later
|
|
7
|
+
* install that repairs or re-links the store can restore the buggy bytes.
|
|
8
|
+
*
|
|
9
|
+
* This entry runs the shared patch helper first, then dynamically imports the
|
|
10
|
+
* real plugin entry. It must not statically import index.mjs (or any module
|
|
11
|
+
* that reaches pi-natives): static imports are evaluated before this body.
|
|
12
|
+
*
|
|
13
|
+
* The helper writes through a temp file + rename, so it breaks the store
|
|
14
|
+
* hardlink and keeps this profile's copy private.
|
|
15
|
+
*/
|
|
16
|
+
import { ensurePiNativesPatched } from "../scripts/patch-pi-natives.mjs";
|
|
17
|
+
|
|
18
|
+
const result = ensurePiNativesPatched({ detach: true });
|
|
19
|
+
if (result.error) {
|
|
20
|
+
console.warn("[tool-plus] pi-natives compat: " + result.error);
|
|
21
|
+
} else if (result.patched > 0 || result.detached > 0) {
|
|
22
|
+
console.log(
|
|
23
|
+
"[tool-plus] pi-natives compat: patched=" + result.patched + ", detached=" + result.detached,
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Runtime-computed URL: keep this dynamic so a bundler never inlines the
|
|
28
|
+
// plugin graph (and its static pi-natives imports) ahead of the patch above.
|
|
29
|
+
const mod = await import(new URL("./index.mjs", import.meta.url).href);
|
|
30
|
+
|
|
31
|
+
export const name = mod.name;
|
|
32
|
+
export const inject = mod.inject;
|
|
33
|
+
export const Config = mod.Config;
|
|
34
|
+
export const apply = mod.apply;
|
|
@@ -3,7 +3,7 @@ __installBunShim();
|
|
|
3
3
|
import { n as fs_promises_shim_exports } from "./fs-promises-shim-B2YFWkcd.mjs";
|
|
4
4
|
import { $ as isEnoent, B as USER_AGENT, C as warn, G as getDocsRsCacheDir, N as $env, c as combineSignals, j as formatNumber, k as formatBytes, l as exec, s as Snowflake, y as parseFrontmatter } from "./src-AELUUb5U.mjs";
|
|
5
5
|
import { W as tryParseJson } from "./utils-cZX3f_Ls.mjs";
|
|
6
|
-
import { a as fetchBinary, c as getParallelExtractContent, i as convertWithMarkit, l as ensureTool, m as settings, n as asRecord, o as extractWithParallel, r as asString, s as findParallelApiKey, t as asNumber } from "./utils-
|
|
6
|
+
import { a as fetchBinary, c as getParallelExtractContent, i as convertWithMarkit, l as ensureTool, m as settings, n as asRecord, o as extractWithParallel, r as asString, s as findParallelApiKey, t as asNumber } from "./utils-CE3DSebh.mjs";
|
|
7
7
|
import { n as isRecord } from "./type-guards-CttAANEx.mjs";
|
|
8
8
|
import { r as throwIfAborted, t as ToolAbortError } from "./tool-errors-DoyxivAR.mjs";
|
|
9
9
|
import { c as getLocalizedText, d as looksLikeHtml, i as decodeHtmlEntities, l as htmlToBasicMarkdown, o as formatIsoDate, r as buildResult, s as formatMediaDuration, u as loadPage } from "./types-BLrLohwK.mjs";
|
|
@@ -6267,7 +6267,7 @@ const settings = {
|
|
|
6267
6267
|
};
|
|
6268
6268
|
//#endregion
|
|
6269
6269
|
//#region package.json
|
|
6270
|
-
var version = "0.1.
|
|
6270
|
+
var version = "0.1.16";
|
|
6271
6271
|
/** `.tmp` files older than this are treated as orphaned writes and swept. */
|
|
6272
6272
|
const TMP_ORPHAN_MAX_AGE_MS = 3e5;
|
|
6273
6273
|
function markitConversionCacheKey(bytes, extension) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xiaoso/dsh-tool-plus",
|
|
3
3
|
"description": "Oh My Pi bash runtime ported as a deepseek-harness tool plugin: persistent shell session, output minimizer, managed background jobs, command interception",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.16",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"deepseek-harness",
|
|
@@ -36,13 +36,13 @@
|
|
|
36
36
|
"node": "^22.19.0 || >=24.0.0",
|
|
37
37
|
"dsh": ">=0.2.1-0 <0.3.0-0"
|
|
38
38
|
},
|
|
39
|
-
"main": "lib/
|
|
39
|
+
"main": "lib/pi-natives-bootstrap.mjs",
|
|
40
40
|
"types": "lib/types/index.d.ts",
|
|
41
41
|
"icon": "./assets/icon.png",
|
|
42
42
|
"exports": {
|
|
43
43
|
".": {
|
|
44
44
|
"types": "./lib/types/index.d.ts",
|
|
45
|
-
"default": "./lib/
|
|
45
|
+
"default": "./lib/pi-natives-bootstrap.mjs"
|
|
46
46
|
},
|
|
47
47
|
"./client": {
|
|
48
48
|
"types": "./lib/types/client/index.d.ts",
|
|
@@ -1,108 +1,241 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Self-heal
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Self-heal \`@oh-my-pi/pi-natives\` for Node runtimes.
|
|
4
|
+
*
|
|
5
|
+
* The upstream package locates its prebuilt binaries with the Bun-only
|
|
6
|
+
* \`import.meta.dir\` (native/loader-state.js). Node has no such property, so
|
|
7
|
+
* \`path.join(undefined, "..", "native")\` throws ERR_INVALID_ARG_TYPE before
|
|
8
|
+
* any tool can load.
|
|
9
|
+
*
|
|
10
|
+
* Two callers share this module:
|
|
11
|
+
* - the package postinstall (plain npm / yarn / npx installs), and
|
|
12
|
+
* - lib/pi-natives-bootstrap.mjs, which runs before the real plugin entry
|
|
13
|
+
* so a pnpm store repair/re-link cannot break the next dsh start.
|
|
14
|
+
*
|
|
15
|
+
* Copy-on-patch is intentional. pnpm links package files from its
|
|
16
|
+
* content-addressable store with hardlinks; writing the linked file in place
|
|
17
|
+
* mutates the store and every profile that shares that inode. Writing a temp
|
|
18
|
+
* file and renaming it over the target gives this profile a private inode and
|
|
19
|
+
* leaves the store untouched. When the file is already patched but still
|
|
20
|
+
* shared, the same operation detaches it once.
|
|
21
|
+
*
|
|
22
|
+
* Resolution prefers \`import.meta.resolve("@oh-my-pi/pi-natives")\` because the
|
|
23
|
+
* package is ESM-only; createRequire.resolve throws
|
|
24
|
+
* ERR_PACKAGE_PATH_NOT_EXPORTED for it. The historical ancestor walk remains
|
|
25
|
+
* as a fallback for unknown layouts, and can be forced with a searchRoot.
|
|
26
|
+
*
|
|
27
|
+
* Idempotent; the CLI never throws.
|
|
28
|
+
*
|
|
29
|
+
* Usage: node scripts/patch-pi-natives.mjs [searchRoot]
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import {
|
|
33
|
+
existsSync,
|
|
34
|
+
readdirSync,
|
|
35
|
+
readFileSync,
|
|
36
|
+
realpathSync,
|
|
37
|
+
renameSync,
|
|
38
|
+
rmSync,
|
|
39
|
+
statSync,
|
|
40
|
+
writeFileSync,
|
|
41
|
+
} from "node:fs";
|
|
42
|
+
import { dirname, join, resolve } from "node:path";
|
|
43
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
44
|
+
|
|
45
|
+
const PKG_SUBPATH = join("node_modules", "@oh-my-pi", "pi-natives");
|
|
46
|
+
const TARGET_REL = join("native", "loader-state.js");
|
|
47
|
+
// Exact buggy token; \b keeps an already-fixed \`import.meta.dirname\` intact.
|
|
48
|
+
const BUGGY = /import\.meta\.dir\b/g;
|
|
49
|
+
const PATCHED_LITERAL = "import.meta.dirname";
|
|
50
|
+
const PKG_NAME = "@oh-my-pi/pi-natives";
|
|
51
|
+
|
|
52
|
+
function log(message) {
|
|
53
|
+
console.log("[patch-pi-natives] " + message);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function warn(message) {
|
|
57
|
+
console.warn("[patch-pi-natives] " + message);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Walk upwards from start, yielding every candidate package directory. */
|
|
61
|
+
function* candidatePackageDirs(start) {
|
|
62
|
+
let current = resolve(start);
|
|
63
|
+
for (;;) {
|
|
64
|
+
const candidate = join(current, PKG_SUBPATH);
|
|
65
|
+
if (existsSync(candidate)) yield candidate;
|
|
66
|
+
const parent = dirname(current);
|
|
67
|
+
if (parent === current) return;
|
|
68
|
+
current = parent;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function firstMatchedFile(packageDir) {
|
|
73
|
+
const direct = join(packageDir, TARGET_REL);
|
|
74
|
+
if (existsSync(direct)) return direct;
|
|
75
|
+
const stack = [packageDir];
|
|
76
|
+
while (stack.length > 0) {
|
|
77
|
+
const entry = stack.pop();
|
|
78
|
+
for (const item of readdirSync(entry)) {
|
|
79
|
+
const full = join(entry, item);
|
|
80
|
+
const stat = statSync(full);
|
|
81
|
+
if (stat.isDirectory()) stack.push(full);
|
|
82
|
+
else if (item === "loader-state.js") return full;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Walk up from a resolved entry until the pi-natives package root. */
|
|
89
|
+
function packageRootFor(entry) {
|
|
90
|
+
let dir = dirname(entry);
|
|
91
|
+
for (;;) {
|
|
92
|
+
const manifest = join(dir, "package.json");
|
|
93
|
+
if (existsSync(manifest)) {
|
|
94
|
+
try {
|
|
95
|
+
const pkg = JSON.parse(readFileSync(manifest, "utf8"));
|
|
96
|
+
if (pkg.name === PKG_NAME) return dir;
|
|
97
|
+
} catch {
|
|
98
|
+
// Keep walking: a malformed package.json is not our target.
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const parent = dirname(dir);
|
|
102
|
+
if (parent === dir) return undefined;
|
|
103
|
+
dir = parent;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* @param {string | undefined} searchRoot
|
|
109
|
+
* @param {boolean} preferExact
|
|
110
|
+
* @returns {{ file: string | undefined, inspected: number }}
|
|
111
|
+
*/
|
|
112
|
+
function resolveTarget(searchRoot, preferExact) {
|
|
113
|
+
if (preferExact) {
|
|
114
|
+
try {
|
|
115
|
+
const entry = fileURLToPath(import.meta.resolve(PKG_NAME));
|
|
116
|
+
const packageRoot = packageRootFor(entry);
|
|
117
|
+
if (packageRoot !== undefined) {
|
|
118
|
+
const direct = join(packageRoot, TARGET_REL);
|
|
119
|
+
if (existsSync(direct)) return { file: direct, inspected: 1 };
|
|
120
|
+
}
|
|
121
|
+
} catch {
|
|
122
|
+
// Fall through to the ancestor walk.
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const start = searchRoot ? resolve(searchRoot) : dirname(fileURLToPath(import.meta.url));
|
|
127
|
+
let inspected = 0;
|
|
128
|
+
for (const packageDir of candidatePackageDirs(start)) {
|
|
129
|
+
inspected += 1;
|
|
130
|
+
const file = firstMatchedFile(packageDir);
|
|
131
|
+
if (file !== undefined) return { file, inspected };
|
|
132
|
+
}
|
|
133
|
+
return { file: undefined, inspected };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Replace the target with a private inode. rename() removes this path's link
|
|
138
|
+
* to the old inode (the store and other profiles keep theirs); the temp file
|
|
139
|
+
* becomes the new content.
|
|
140
|
+
*/
|
|
141
|
+
function atomicReplace(file, content) {
|
|
142
|
+
const real = realpathSync(file);
|
|
143
|
+
const mode = statSync(real).mode;
|
|
144
|
+
const tmp = real + "." + process.pid + "." + Date.now() + ".tmp";
|
|
145
|
+
writeFileSync(tmp, content, { mode });
|
|
146
|
+
try {
|
|
147
|
+
renameSync(tmp, real);
|
|
148
|
+
} catch (error) {
|
|
149
|
+
const code = error && error.code;
|
|
150
|
+
if (code !== "EEXIST" && code !== "EPERM" && code !== "EXDEV") throw error;
|
|
151
|
+
rmSync(real, { force: true });
|
|
152
|
+
renameSync(tmp, real);
|
|
153
|
+
} finally {
|
|
154
|
+
rmSync(tmp, { force: true });
|
|
155
|
+
}
|
|
156
|
+
return real;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* @param {{
|
|
161
|
+
* target?: string,
|
|
162
|
+
* searchRoot?: string,
|
|
163
|
+
* detach?: boolean,
|
|
164
|
+
* copyOnPatch?: boolean,
|
|
165
|
+
* }} [options]
|
|
166
|
+
* @returns {{ inspected: number, patched: number, detached: number, files: string[], error?: string }}
|
|
167
|
+
*/
|
|
168
|
+
export function ensurePiNativesPatched(options = {}) {
|
|
169
|
+
const { target, searchRoot, detach = true, copyOnPatch = true } = options;
|
|
170
|
+
|
|
171
|
+
try {
|
|
172
|
+
let found;
|
|
173
|
+
if (target) {
|
|
174
|
+
found = { file: existsSync(target) ? target : undefined, inspected: existsSync(target) ? 1 : 0 };
|
|
175
|
+
} else {
|
|
176
|
+
found = resolveTarget(searchRoot, searchRoot === undefined);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (found.file === undefined) {
|
|
180
|
+
return {
|
|
181
|
+
inspected: found.inspected,
|
|
182
|
+
patched: 0,
|
|
183
|
+
detached: 0,
|
|
184
|
+
files: [],
|
|
185
|
+
error: "no " + PKG_NAME + "/" + TARGET_REL + " found",
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const real = realpathSync(found.file);
|
|
190
|
+
const source = readFileSync(real, "utf8");
|
|
191
|
+
const next = source.replace(BUGGY, PATCHED_LITERAL);
|
|
192
|
+
const patched = next !== source;
|
|
193
|
+
const shared = statSync(real).nlink > 1;
|
|
194
|
+
const shouldDetach = !patched && detach && shared;
|
|
195
|
+
|
|
196
|
+
if (!patched && !shouldDetach) {
|
|
197
|
+
return { inspected: found.inspected, patched: 0, detached: 0, files: [real] };
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (copyOnPatch) atomicReplace(real, next);
|
|
201
|
+
else writeFileSync(real, next);
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
inspected: found.inspected,
|
|
205
|
+
patched: patched ? 1 : 0,
|
|
206
|
+
detached: shouldDetach ? 1 : 0,
|
|
207
|
+
files: [real],
|
|
208
|
+
};
|
|
209
|
+
} catch (error) {
|
|
210
|
+
return {
|
|
211
|
+
inspected: 0,
|
|
212
|
+
patched: 0,
|
|
213
|
+
detached: 0,
|
|
214
|
+
files: [],
|
|
215
|
+
error: error && error.message ? error.message : String(error),
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function main() {
|
|
221
|
+
const searchRoot = process.argv[2] ? resolve(process.argv[2]) : undefined;
|
|
222
|
+
const result = ensurePiNativesPatched({ detach: true, searchRoot });
|
|
223
|
+
if (result.error) {
|
|
224
|
+
warn("WARN " + result.error);
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
for (const file of result.files) {
|
|
228
|
+
if (result.patched > 0) log(file + ": patched import.meta.dir -> import.meta.dirname");
|
|
229
|
+
else if (result.detached > 0) log(file + ": detached hardlink (private copy)");
|
|
230
|
+
else log(file + ": already patched");
|
|
231
|
+
}
|
|
232
|
+
log("done: patched=" + result.patched + ", detached=" + result.detached);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (process.argv[1] && pathToFileURL(process.argv[1]).href === import.meta.url) {
|
|
236
|
+
try {
|
|
237
|
+
main();
|
|
238
|
+
} catch (error) {
|
|
239
|
+
warn("WARN self-heal skipped: " + (error && error.message ? error.message : String(error)));
|
|
240
|
+
}
|
|
241
|
+
}
|