dsh-patch-edit-plus 0.2.6 → 0.2.7
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 +20 -9
- package/CHANGELOG.zh.md +20 -9
- package/README.ja.md +11 -9
- package/README.ko.md +11 -9
- package/README.md +11 -9
- package/README.zh.md +11 -9
- package/cordis.patch.yml +8 -0
- package/dsh.plugin.json +4 -4
- package/lib/client/index.js +135 -0
- package/lib/client.js +203 -0
- package/lib/client.js.map +1 -0
- package/lib/config.js +12 -4
- package/lib/index.js +21 -38
- package/lib/shellops.js +3 -2
- package/lib/types/client/index.d.ts +47 -0
- package/lib/types/config.d.ts +44 -5
- package/lib/types/index.d.ts +18 -14
- package/package.json +40 -14
package/CHANGELOG.md
CHANGED
|
@@ -1,20 +1,31 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
## [0.2.5] — 2026-10-01
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
- **A bare multi-file unified diff collapsed into one file section.** The parser only opened a new file section at `--- ` when no section existed; the second file's `--- `/`+++ ` headers therefore REWROTE the previous section's paths and its hunks were matched against the wrong file's content, failing every section after the first with `Hunk N of "..." does not apply: the context does not match the file` (one-call-per-file was the only workaround). `--- ` now opens a new section whenever the current one has already been claimed by a path or a hunk; `diff --git`-delimited diffs are unaffected (their `--- ` always follows a fresh section). Regression-tested at the parser level and end-to-end with a two-file bare patch.
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- none
|
|
12
|
+
## [0.2.4] — 2026-10-01
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
- **Both patch syntaxes are now enabled by default**: `allowCodexPatch` default `false` → `true`, so the tool accepts git/unified diff and Codex `apply_patch` out of the box. The inserted `cordis.patch.yml` row now carries the config itself (`allowUnifiedDiff: true` + `allowCodexPatch: true`) — configuration ships through the patch layer instead of leaning on the settings panel. Set `allowCodexPatch: false` to restore unified-diff-only; the `.volatile()` settings projection is unchanged, so an explicit toggle still takes effect without a restart. The actionable UNSUPPORTED hint is unchanged for the explicit-opt-out case.
|
|
16
|
+
- Smoke reworked for the new default: default-on apply, explicit `true` + dryRun, explicit `false` deny, and an assertion that the inserted patch row ships both modes enabled.
|
|
17
|
+
## [0.2.3] — 2026-09-29
|
|
5
18
|
|
|
6
19
|
### Fixed
|
|
7
|
-
- **
|
|
8
|
-
## [0.2.2] — 2026-09-29
|
|
20
|
+
- **Delete/Move silently degraded to UNSUPPORTED on real 0.1.7 hosts** (defect shipped in 0.2.1): the shell duck-detect looked for `run()`, but DSH renamed `ShellExecutor.run(spec)` → `execute(spec)` (returning a `ShellProcess` handle with a `result()` foreground projection) between 0.1.2 and 0.1.7 — so the detect never matched and Delete/Move always fell back to a structured UNSUPPORTED error. `shellops` now calls `shell.execute(...)` + `await execution.result()` and duck-detects `execute`; shell test mocks moved to the `execute()`/`result()` shape.
|
|
9
21
|
|
|
10
22
|
### Changed
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
|
|
23
|
+
- devDependencies 4x `@deepseek-ai/dsh-*` `0.1.2-rc.1` → `0.1.7-rc.2`: the type baseline now matches the hosts this line serves (the stale pin was the "fake green" pattern — it masked the rename above).
|
|
24
|
+
- Docs x4 (README/INSTALL): compatibility re-based to the `0.1.7` segment; ja/ko matrices were stale at the 0.1.2~0.1.5 wide claim.
|
|
25
|
+
|
|
26
|
+
## [0.2.1] — 2026-09-25
|
|
14
27
|
|
|
15
|
-
|
|
16
|
-
- No source changes required: `ShellExecutor.run(spec)` is intact across `0.1.5-rc.1`–`rc.3` (the `run` → `execute` rename lands in the 0.1.6→0.1.7 window), and the imperative settings fallback (`settings.register` / `installSection`) still exists on 0.1.5 hosts.
|
|
17
|
-
- build / typecheck / test (94) / lint / smoke / `typecheck:0.1.5` (static proof against real `0.1.5-rc.2` peers) all green; lockfile regenerated against `0.1.5-rc.3`.
|
|
28
|
+
- First release of the 0.1.7 line (`dsh-0.1.7` dist-tag): declarative settings — `allowCodexPatch` marked `.volatile()`, settings form auto-rendered, tool re-registered in place via `loader/volatile-update` (no remount, no registration call).
|
|
18
29
|
|
|
19
30
|
## [0.1.2] — 2026-09-19
|
|
20
31
|
|
package/CHANGELOG.zh.md
CHANGED
|
@@ -1,20 +1,31 @@
|
|
|
1
1
|
# 更新日志
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
## [0.2.5] — 2026-10-01
|
|
6
|
+
|
|
7
|
+
### 修复
|
|
8
|
+
- **裸多文件 unified diff 被折叠进同一个文件 section。** 解析器只在"当前无 section"时才在 `--- ` 处开新 section;第二个文件的 `--- `/`+++ ` 头因此直接改写了前一个 section 的路径,前一个文件的 hunk 被拿到错误文件的内容上匹配,第一个文件之后的每个 section 都报 `Hunk N of "..." does not apply: the context does not match the file`(此前只能逐文件调用绕过)。现在只要当前 section 已被路径或 hunk 认领,`--- ` 就开新 section;带 `diff --git` 分隔的 diff 不受影响(其 `--- ` 总是紧跟新 section)。解析层与端到端双层回归测试(双文件裸补丁)。
|
|
9
|
+
|
|
10
|
+
### 变更
|
|
11
|
+
- 无
|
|
12
|
+
## [0.2.4] — 2026-10-01
|
|
13
|
+
|
|
14
|
+
### 变更
|
|
15
|
+
- **两种补丁语法默认全部开启**:`allowCodexPatch` 默认值 `false` → `true`,工具开箱即接受 git/unified diff 与 Codex `apply_patch`。插入的 `cordis.patch.yml` 行现在自带配置(`allowUnifiedDiff: true` + `allowCodexPatch: true`)——配置随 patch 层下发,不再依赖设置面板。设 `allowCodexPatch: false` 可退回仅 unified diff;`.volatile()` 设置投影不变,显式开关依旧即时生效、无需重启。显式关闭时的可操作 UNSUPPORTED 提示保持不变。
|
|
16
|
+
- Smoke 按新默认重写:默认开启直接应用、显式 `true` + dryRun、显式 `false` 拒绝,并断言插入的 patch 行携带两种模式开启。
|
|
17
|
+
## [0.2.3] — 2026-09-29
|
|
5
18
|
|
|
6
19
|
### 修复
|
|
7
|
-
-
|
|
8
|
-
## [0.2.2] — 2026-09-29
|
|
20
|
+
- **Delete/Move 在真实 0.1.7 宿主上被静默降级为 UNSUPPORTED**(0.2.1 携带的缺陷):shell 鸭子探测查的是 `run()`,但 DSH 在 0.1.2→0.1.7 之间把 `ShellExecutor.run(spec)` 改名为 `execute(spec)`(返回 `ShellProcess` 句柄 + `result()` 前台投影)——探测永不命中,Delete/Move 一直落到结构化 UNSUPPORTED 错误。`shellops` 现改用 `shell.execute(...)` + `await execution.result()`,鸭子探测改查 `execute`;shell 测试 mock 同步迁移到 `execute()`/`result()` 形态。
|
|
9
21
|
|
|
10
22
|
### 变更
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
|
|
23
|
+
- devDependencies 4 项 `@deepseek-ai/dsh-*` `0.1.2-rc.1` → `0.1.7-rc.2`:类型基线与本线服务的宿主对齐(过期的钉版正是「假绿」形态——它掩盖了上述改名)。
|
|
24
|
+
- 文档 ×4(README/INSTALL):兼容矩阵重基于 `0.1.7` 线段;ja/ko 矩阵此前停留在 0.1.2~0.1.5 旧态。
|
|
25
|
+
|
|
26
|
+
## [0.2.1] — 2026-09-25
|
|
14
27
|
|
|
15
|
-
|
|
16
|
-
- 无需源码改动:`ShellExecutor.run(spec)` 在 `0.1.5-rc.1`–`rc.3` 全程未变(`run` → `execute` 改名落在 0.1.6→0.1.7 区间),且 0.1.5 宿主仍有命令式设置回退(`settings.register` / `installSection`)。
|
|
17
|
-
- build / typecheck / test(94)/ lint / smoke / `typecheck:0.1.5`(对真实 `0.1.5-rc.2` peer 静态核验)全绿;lockfile 按 `0.1.5-rc.3` 重新生成。
|
|
28
|
+
- 0.1.7 线首发(`dsh-0.1.7` dist-tag):声明式设置——`allowCodexPatch` 标记 `.volatile()`,设置表单自动生成,工具经 `loader/volatile-update` 原位重注册(不 remount、无注册调用)。
|
|
18
29
|
|
|
19
30
|
## [0.1.2] — 2026-09-19
|
|
20
31
|
|
package/README.ja.md
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
# dsh-patch-edit-plus
|
|
2
2
|
|
|
3
|
-
[DeepSeek Harness (DSH)](https://github.com/deepseek-ai/deepseek-harness) 向けのパッチ形式ファイル編集。モデルに公開される単一の `apply_patch` ツールが **git/unified diff
|
|
3
|
+
[DeepSeek Harness (DSH)](https://github.com/deepseek-ai/deepseek-harness) 向けのパッチ形式ファイル編集。モデルに公開される単一の `apply_patch` ツールが **git/unified diff** と **Codex `apply_patch` 構文**(両方とも既定で有効)を受け付け、すべての変更を **オール・オア・ナッシング**で適用し、DSH の動作には一切干渉しません。
|
|
4
4
|
|
|
5
5
|
## DSH バージョン互換性
|
|
6
6
|
|
|
7
7
|
| DSH バージョン | ステータス | 備考 |
|
|
8
8
|
|---|---|---|
|
|
9
|
-
| `0.1.
|
|
10
|
-
| `0.1.2-rc.1` ~ `0.1.4` | ↗ メンテナンスライン | 公開済みの `0.1.2`(main
|
|
9
|
+
| `0.1.7-rc.1+` | ✅ サポート対象(本ライン、v0.2.1+) | 宣言的設定:`allowCodexPatch` を `.volatile()` とマーク — 設定フォームは自動生成され、`loader/volatile-update` が remount なしでツールをその場で再登録します。v0.2.3 は Delete/Move を修正:shell のダックタイピング検出が `run()` を見ていましたが、実 0.1.7 ホストでは決して一致しません(`run` は 0.1.2→0.1.7 の間に `execute` に改名)、Delete/Move は UNSUPPORTED にサイレント降格していました。現在は `execute()` + `result()` を使用。v0.2.4:`allowCodexPatch` の既定値が `true` に — 両構文が初期状態で有効;設定は挿入される `cordis.patch.yml` 行経由で配布。 |
|
|
10
|
+
| `0.1.2-rc.1` ~ `0.1.4` | ↗ メンテナンスライン | 公開済みの `0.1.2`(main ブランチ)が担います。 |
|
|
11
|
+
| `0.1.5-rc.x` | ↗ メンテナンスライン | `dsh-0.1.5` dist-tag(v0.2.2+)が担います。 |
|
|
12
|
+
| `0.2.0-rc.1+` | ↗ 次ライン | `dsh-0.2.0` dist-tag(v0.3.0+)が担います。 |
|
|
11
13
|
|
|
12
|
-
|
|
14
|
+
本ラインの設定は宣言的です:`Config` で `.volatile()` とマークされたフィールドが設定フォームになり、プラグインは登録 hooks を受け取る代わりに `loader/volatile-update` を購読します。
|
|
13
15
|
|
|
14
16
|
## そもそもなぜパッチなのか?(ツールルーティングガイド)
|
|
15
17
|
|
|
@@ -43,7 +45,7 @@ dsh web --dump-config # verify the plugin row appears
|
|
|
43
45
|
}
|
|
44
46
|
```
|
|
45
47
|
|
|
46
|
-
### Codex apply_patch
|
|
48
|
+
### Codex apply_patch 構文(既定で有効)
|
|
47
49
|
|
|
48
50
|
```text
|
|
49
51
|
*** Begin Patch
|
|
@@ -59,7 +61,7 @@ dsh web --dump-config # verify the plugin row appears
|
|
|
59
61
|
*** End Patch
|
|
60
62
|
```
|
|
61
63
|
|
|
62
|
-
`allowCodexPatch: true`
|
|
64
|
+
両構文とも既定で有効です。既定値は `Config` スキーマが持ち、挿入される `cordis.patch.yml` 行も `allowUnifiedDiff: true` + `allowCodexPatch: true` を運びます。`allowCodexPatch: false` で unified diff 専用に戻せます。明示的に無効化した状態で Codex パッチが届くと、ツールは汎用のパースエラーではなく **実行可能なヒント**(有効化の方法、または unified diff での再試行方法)を返します。Codex 構文は GPT 系モデルにとって強い事前分布であり、無言の失敗はモデルを再試行ループに閉じ込めてしまいます。
|
|
63
65
|
|
|
64
66
|
### 操作
|
|
65
67
|
|
|
@@ -86,7 +88,7 @@ dsh web --dump-config # verify the plugin row appears
|
|
|
86
88
|
| `conflictPolicy` | `rename` | ツール名が使用済みの場合の `rename` / `skip` / `fail`。 |
|
|
87
89
|
| `renameSuffix` | `_1` | リネーム回避で使用される接尾辞。 |
|
|
88
90
|
| `allowUnifiedDiff` | `true` | git/unified diff を受け付けます。 |
|
|
89
|
-
| `allowCodexPatch` | `
|
|
91
|
+
| `allowCodexPatch` | `true` | Codex `apply_patch` 構文を受け付けます(`false` で unified diff 専用)。DSH 設定パネルからも変更でき、変更は即座に反映されます(再起動不要)。 |
|
|
90
92
|
| `deleteBackend` | `shell` | `shell` または `none`(削除/移動は構造化エラーを返します)。 |
|
|
91
93
|
| `shellDialect` | `auto` | `auto`(win32 では pwsh)/ `posix` / `pwsh`。 |
|
|
92
94
|
| `deleteCommand` / `moveCommand` | 組み込み | カスタムコマンドテンプレート。パスは依然として **env 経由のみ**(`DSH_PATCH_TARGET` / `DSH_PATCH_SOURCE`)で渡され、コマンド文字列に補間されることはありません。 |
|
|
@@ -108,8 +110,8 @@ dsh web --dump-config # verify the plugin row appears
|
|
|
108
110
|
|
|
109
111
|
```bash
|
|
110
112
|
npm install
|
|
111
|
-
npm run typecheck # against 0.1.
|
|
112
|
-
npm run typecheck:0.1.5 # 実在の 0.1.5-rc.2
|
|
113
|
+
npm run typecheck # against 0.1.7-rc.2 peers (devDependencies)
|
|
114
|
+
npm run typecheck:0.1.5 # 実在の 0.1.5-rc.2 ピアに対するレガシー交叉検証
|
|
113
115
|
npm test # vitest, 90 tests
|
|
114
116
|
npm run lint
|
|
115
117
|
npm run build # lib/
|
package/README.ko.md
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
# dsh-patch-edit-plus
|
|
2
2
|
|
|
3
|
-
[DeepSeek Harness (DSH)](https://github.com/deepseek-ai/deepseek-harness)를 위한 패치 스타일 파일 편집 도구입니다. 모델에 노출되는 단일 `apply_patch` 도구가 **git/unified diff
|
|
3
|
+
[DeepSeek Harness (DSH)](https://github.com/deepseek-ai/deepseek-harness)를 위한 패치 스타일 파일 편집 도구입니다. 모델에 노출되는 단일 `apply_patch` 도구가 **git/unified diff**와 **Codex `apply_patch` 문법**(둘 다 기본 활성화)을 받아들이고, 모든 변경을 **전부 아니면 전무(all-or-nothing)** 방식으로 적용하며, DSH의 동작에는 전혀 간섭하지 않습니다.
|
|
4
4
|
|
|
5
5
|
## DSH 버전 호환성
|
|
6
6
|
|
|
7
7
|
| DSH 버전 | 상태 | 비고 |
|
|
8
8
|
|---|---|---|
|
|
9
|
-
| `0.1.
|
|
10
|
-
| `0.1.2-rc.1` ~ `0.1.4` | ↗ 유지보수 라인 | 게시된 `0.1.2`(main 브랜치)가 담당합니다.
|
|
9
|
+
| `0.1.7-rc.1+` | ✅ 지원됨(본 라인, v0.2.1+) | 선언적 설정: `allowCodexPatch`에 `.volatile()` 마크 — 설정 폼이 자동 생성되고, `loader/volatile-update`가 remount 없이 도구를 그 자리에서 재등록합니다. v0.2.3은 Delete/Move를 수정: shell 덕 타이핑 감지가 `run()`을 보았으나 실 0.1.7 호스트에서는 결코 일치하지 않았고(`run`은 0.1.2→0.1.7 사이에 `execute`로 개명), Delete/Move가 UNSUPPORTED로 조용히 강등되었습니다. 이제 `execute()` + `result()` 사용. v0.2.4: `allowCodexPatch` 기본값이 `true`로 — 두 문법 모두 초기 상태에서 활성화; 설정은 삽입되는 `cordis.patch.yml` 행을 통해 배포. |
|
|
10
|
+
| `0.1.2-rc.1` ~ `0.1.4` | ↗ 유지보수 라인 | 게시된 `0.1.2`(main 브랜치)가 담당합니다. |
|
|
11
|
+
| `0.1.5-rc.x` | ↗ 유지보수 라인 | `dsh-0.1.5` dist-tag(v0.2.2+)가 담당합니다. |
|
|
12
|
+
| `0.2.0-rc.1+` | ↗ 다음 라인 | `dsh-0.2.0` dist-tag(v0.3.0+)가 담당합니다. |
|
|
11
13
|
|
|
12
|
-
|
|
14
|
+
본 라인의 설정은 선언적입니다: `Config`에서 `.volatile()`로 마크된 필드가 설정 폼이 되고, 플러그인은 등록 hooks를 받는 대신 `loader/volatile-update`를 구독합니다.
|
|
13
15
|
|
|
14
16
|
## 왜 굳이 패치인가? (도구 라우팅 가이드)
|
|
15
17
|
|
|
@@ -43,7 +45,7 @@ dsh web --dump-config # verify the plugin row appears
|
|
|
43
45
|
}
|
|
44
46
|
```
|
|
45
47
|
|
|
46
|
-
### Codex apply_patch 문법 (
|
|
48
|
+
### Codex apply_patch 문법 (기본 활성화)
|
|
47
49
|
|
|
48
50
|
```text
|
|
49
51
|
*** Begin Patch
|
|
@@ -59,7 +61,7 @@ dsh web --dump-config # verify the plugin row appears
|
|
|
59
61
|
*** End Patch
|
|
60
62
|
```
|
|
61
63
|
|
|
62
|
-
`allowCodexPatch: true`로
|
|
64
|
+
두 문법 모두 기본 활성화되어 있습니다. 기본값은 `Config` 스키마가 가지며, 삽입되는 `cordis.patch.yml` 행도 `allowUnifiedDiff: true` + `allowCodexPatch: true`를 운반합니다. `allowCodexPatch: false`로 unified diff 전용으로 되돌릴 수 있습니다. 명시적으로 비활성화된 상태에서 Codex 패치가 들어오면 도구는 일반적인 파싱 오류 대신 **실행 가능한 힌트**(활성화 방법, 또는 unified diff로 재시도하는 방법)를 반환합니다. Codex 문법은 GPT 계열 모델에게 강한 사전 확률(prior)이며, 조용한 실패는 모델을 재시도 루프에 가두기 때문입니다.
|
|
63
65
|
|
|
64
66
|
### 연산
|
|
65
67
|
|
|
@@ -86,7 +88,7 @@ dsh web --dump-config # verify the plugin row appears
|
|
|
86
88
|
| `conflictPolicy` | `rename` | 도구 이름이 이미 사용 중일 때의 동작으로, `rename` / `skip` / `fail` 중 하나입니다. |
|
|
87
89
|
| `renameSuffix` | `_1` | 이름 회피에 사용되는 접미사입니다. |
|
|
88
90
|
| `allowUnifiedDiff` | `true` | git/unified diff를 허용합니다. |
|
|
89
|
-
| `allowCodexPatch` | `
|
|
91
|
+
| `allowCodexPatch` | `true` | Codex `apply_patch` 문법을 허용합니다(`false`로 unified diff 전용). DSH 설정 패널에서도 변경할 수 있으며, 변경은 즉시 적용됩니다(재시작 불필요). |
|
|
90
92
|
| `deleteBackend` | `shell` | `shell` 또는 `none`(`none`이면 삭제/이동이 구조화된 오류를 반환합니다). |
|
|
91
93
|
| `shellDialect` | `auto` | `auto`(win32에서는 pwsh) / `posix` / `pwsh` 중 하나입니다. |
|
|
92
94
|
| `deleteCommand` / `moveCommand` | 내장 | 사용자 지정 명령 템플릿입니다. 경로는 여전히 **환경 변수로만**(`DSH_PATCH_TARGET` / `DSH_PATCH_SOURCE`) 전달되며, 명령 문자열에 끼워 넣어서는 안 됩니다. |
|
|
@@ -108,8 +110,8 @@ dsh web --dump-config # verify the plugin row appears
|
|
|
108
110
|
|
|
109
111
|
```bash
|
|
110
112
|
npm install
|
|
111
|
-
npm run typecheck # against 0.1.
|
|
112
|
-
npm run typecheck:0.1.5 # 실제 0.1.5-rc.2 peer 대상
|
|
113
|
+
npm run typecheck # against 0.1.7-rc.2 peers (devDependencies)
|
|
114
|
+
npm run typecheck:0.1.5 # 실제 0.1.5-rc.2 peer 대상 레거시 교차 검증
|
|
113
115
|
npm test # vitest, 90 tests
|
|
114
116
|
npm run lint
|
|
115
117
|
npm run build # lib/
|
package/README.md
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
# dsh-patch-edit-plus
|
|
2
2
|
|
|
3
|
-
Patch-style file editing for [DeepSeek Harness (DSH)](https://github.com/deepseek-ai/deepseek-harness): one model-facing `apply_patch` tool that accepts **git/unified diff**
|
|
3
|
+
Patch-style file editing for [DeepSeek Harness (DSH)](https://github.com/deepseek-ai/deepseek-harness): one model-facing `apply_patch` tool that accepts **git/unified diff** and **Codex `apply_patch` syntax** (both enabled by default), applies every change **all-or-nothing**, and stays completely out of DSH's way.
|
|
4
4
|
|
|
5
5
|
## DSH version compatibility
|
|
6
6
|
|
|
7
7
|
| DSH version | Status | Notes |
|
|
8
8
|
|---|---|---|
|
|
9
|
-
| `0.1.
|
|
10
|
-
| `0.1.2-rc.1` ~ `0.1.4` | ↗ maintenance line | Served by the published `0.1.2` (main branch).
|
|
9
|
+
| `0.1.7-rc.1+` | ✅ supported (this line, v0.2.1+) | Declarative settings: `allowCodexPatch` is marked `.volatile()` — the settings form renders automatically and `loader/volatile-update` re-registers the tool without a remount. v0.2.3 fixes Delete/Move: the shell `run()` duck-detect never matched real 0.1.7 hosts (`run` was renamed `execute` between 0.1.2 and 0.1.7), silently degrading them to UNSUPPORTED; now uses `execute()` + `result()`. v0.2.4: `allowCodexPatch` defaults to `true` — both syntaxes on out of the box; the config ships on the inserted `cordis.patch.yml` row. |
|
|
10
|
+
| `0.1.2-rc.1` ~ `0.1.4` | ↗ maintenance line | Served by the published `0.1.2` (main branch). |
|
|
11
|
+
| `0.1.5-rc.x` | ↗ maintenance line | Served by the `dsh-0.1.5` dist-tag (v0.2.2+). |
|
|
12
|
+
| `0.2.0-rc.1+` | ↗ next line | Served by the `dsh-0.2.0` dist-tag (v0.3.0+). |
|
|
11
13
|
|
|
12
|
-
Settings
|
|
14
|
+
Settings are declarative on this line: fields marked `.volatile()` in `Config` become the settings form; the plugin subscribes to `loader/volatile-update` instead of receiving registration hooks.
|
|
13
15
|
|
|
14
16
|
## Why patch at all? (tool routing guide)
|
|
15
17
|
|
|
@@ -43,7 +45,7 @@ dsh web --dump-config # verify the plugin row appears
|
|
|
43
45
|
}
|
|
44
46
|
```
|
|
45
47
|
|
|
46
|
-
### Codex apply_patch syntax (
|
|
48
|
+
### Codex apply_patch syntax (on by default)
|
|
47
49
|
|
|
48
50
|
```text
|
|
49
51
|
*** Begin Patch
|
|
@@ -59,7 +61,7 @@ dsh web --dump-config # verify the plugin row appears
|
|
|
59
61
|
*** End Patch
|
|
60
62
|
```
|
|
61
63
|
|
|
62
|
-
|
|
64
|
+
Both syntaxes ship enabled: the defaults live in the `Config` schema and the inserted `cordis.patch.yml` row carries `allowUnifiedDiff: true` + `allowCodexPatch: true`. Set `allowCodexPatch: false` to go unified-diff-only. When a Codex patch arrives while explicitly disabled, the tool returns an **actionable hint** (how to enable or how to retry in unified diff) instead of a generic parse error — Codex syntax is a strong prior for GPT-family models and a silent failure traps them in retry loops.
|
|
63
65
|
|
|
64
66
|
### Operations
|
|
65
67
|
|
|
@@ -86,7 +88,7 @@ Enable with `allowCodexPatch: true`. When a Codex patch arrives while disabled,
|
|
|
86
88
|
| `conflictPolicy` | `rename` | `rename` / `skip` / `fail` when the tool name is taken. |
|
|
87
89
|
| `renameSuffix` | `_1` | Suffix used by rename avoidance. |
|
|
88
90
|
| `allowUnifiedDiff` | `true` | Accept git/unified diffs. |
|
|
89
|
-
| `allowCodexPatch` | `
|
|
91
|
+
| `allowCodexPatch` | `true` | Accept Codex `apply_patch` syntax (set `false` for unified-diff-only). Also editable in the DSH settings panel; changes take effect immediately, no restart needed. |
|
|
90
92
|
| `deleteBackend` | `shell` | `shell` or `none` (Delete/Move return a structured error). |
|
|
91
93
|
| `shellDialect` | `auto` | `auto` (pwsh on win32) / `posix` / `pwsh`. |
|
|
92
94
|
| `deleteCommand` / `moveCommand` | built-in | Custom command templates. Paths still arrive **only via env** (`DSH_PATCH_TARGET` / `DSH_PATCH_SOURCE`); never interpolate them into the command string. |
|
|
@@ -108,8 +110,8 @@ Enable with `allowCodexPatch: true`. When a Codex patch arrives while disabled,
|
|
|
108
110
|
|
|
109
111
|
```bash
|
|
110
112
|
npm install
|
|
111
|
-
npm run typecheck # against 0.1.
|
|
112
|
-
npm run typecheck:0.1.5 #
|
|
113
|
+
npm run typecheck # against 0.1.7-rc.2 peers (devDependencies)
|
|
114
|
+
npm run typecheck:0.1.5 # legacy cross-proof against real 0.1.5-rc.2 peers
|
|
113
115
|
npm test # vitest, 90 tests
|
|
114
116
|
npm run lint
|
|
115
117
|
npm run build # lib/
|
package/README.zh.md
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
# dsh-patch-edit-plus
|
|
2
2
|
|
|
3
|
-
面向 [DeepSeek Harness(DSH)](https://github.com/deepseek-ai/deepseek-harness) 的补丁风格文件编辑插件:只提供 **1 个**模型可见工具 `apply_patch`,接受 **git/unified diff
|
|
3
|
+
面向 [DeepSeek Harness(DSH)](https://github.com/deepseek-ai/deepseek-harness) 的补丁风格文件编辑插件:只提供 **1 个**模型可见工具 `apply_patch`,接受 **git/unified diff** 与 **Codex `apply_patch` 语法**(两种语法均默认开启),全量验证后**原子应用**,对 DSH 既有功能零负面影响。
|
|
4
4
|
|
|
5
5
|
## DSH 版本适配矩阵
|
|
6
6
|
|
|
7
7
|
| DSH 版本 | 状态 | 说明 |
|
|
8
8
|
|---|---|---|
|
|
9
|
-
| `0.1.
|
|
10
|
-
| `0.1.2-rc.1` ~ `0.1.4` | ↗ 维护线 | 由已发布的 `0.1.2`(main
|
|
9
|
+
| `0.1.7-rc.1+` | ✅ 支持(本线,v0.2.1+) | 声明式设置:`allowCodexPatch` 标记 `.volatile()`——设置表单自动生成,`loader/volatile-update` 驱动工具原位重注册(不 remount)。v0.2.3 修复 Delete/Move:shell 鸭子探测查 `run()` 在真实 0.1.7 宿主永不命中(`run` 在 0.1.2→0.1.7 间改名为 `execute`),Delete/Move 被静默降级为 UNSUPPORTED;现改用 `execute()` + `result()`。v0.2.4:`allowCodexPatch` 默认改为 `true`——两种语法开箱即用;配置随插入的 `cordis.patch.yml` 行下发。 |
|
|
10
|
+
| `0.1.2-rc.1` ~ `0.1.4` | ↗ 维护线 | 由已发布的 `0.1.2`(main 分支)服务。 |
|
|
11
|
+
| `0.1.5-rc.x` | ↗ 维护线 | 由 `dsh-0.1.5` dist-tag(v0.2.2+)服务。 |
|
|
12
|
+
| `0.2.0-rc.1+` | ↗ 下一线 | 由 `dsh-0.2.0` dist-tag(v0.3.0+)服务。 |
|
|
11
13
|
|
|
12
|
-
settings
|
|
14
|
+
本线的 settings 是声明式的:`Config` 中标记 `.volatile()` 的字段即设置表单;插件订阅 `loader/volatile-update`,不再接收注册 hooks。
|
|
13
15
|
|
|
14
16
|
## 工具路由指引(为什么该用谁)
|
|
15
17
|
|
|
@@ -43,7 +45,7 @@ dsh web --dump-config # 确认插件行出现
|
|
|
43
45
|
}
|
|
44
46
|
```
|
|
45
47
|
|
|
46
|
-
### Codex apply_patch
|
|
48
|
+
### Codex apply_patch 语法(默认开启)
|
|
47
49
|
|
|
48
50
|
```text
|
|
49
51
|
*** Begin Patch
|
|
@@ -58,7 +60,7 @@ dsh web --dump-config # 确认插件行出现
|
|
|
58
60
|
*** End Patch
|
|
59
61
|
```
|
|
60
62
|
|
|
61
|
-
|
|
63
|
+
两种语法默认全部开启:默认值由 `Config` schema 给出,插入的 `cordis.patch.yml` 行同样携带 `allowUnifiedDiff: true` + `allowCodexPatch: true`。设 `allowCodexPatch: false` 可退回仅 unified diff。显式关闭后收到 Codex 补丁时,工具返回**可操作提示**(如何开启、或改用 unified diff),而不是泛化的解析错误 —— Codex 语法是 GPT 系模型的强先验,沉默失败会让模型陷入重试循环。
|
|
62
64
|
|
|
63
65
|
### 四类操作
|
|
64
66
|
|
|
@@ -85,7 +87,7 @@ dsh web --dump-config # 确认插件行出现
|
|
|
85
87
|
| `conflictPolicy` | `rename` | 重名时 `rename` / `skip` / `fail`。 |
|
|
86
88
|
| `renameSuffix` | `_1` | 改名避让使用的后缀。 |
|
|
87
89
|
| `allowUnifiedDiff` | `true` | 接受 git/unified diff。 |
|
|
88
|
-
| `allowCodexPatch` | `
|
|
90
|
+
| `allowCodexPatch` | `true` | 接受 Codex `apply_patch` 语法(设 `false` 退回仅 unified diff)。也可在 DSH 设置面板中修改,改动立即生效、无需重启。 |
|
|
89
91
|
| `deleteBackend` | `shell` | `shell` 或 `none`(Delete/Move 返回结构化错误)。 |
|
|
90
92
|
| `shellDialect` | `auto` | `auto`(win32 上 pwsh)/ `posix` / `pwsh`。 |
|
|
91
93
|
| `deleteCommand` / `moveCommand` | 内置 | 自定义命令模板。路径仍然**只经 env** 传入(`DSH_PATCH_TARGET` / `DSH_PATCH_SOURCE`),请勿把路径插值进命令串。 |
|
|
@@ -107,8 +109,8 @@ dsh web --dump-config # 确认插件行出现
|
|
|
107
109
|
|
|
108
110
|
```bash
|
|
109
111
|
npm install
|
|
110
|
-
npm run typecheck # 针对 0.1.
|
|
111
|
-
npm run typecheck:0.1.5 # 对真实 0.1.5-rc.2 peer
|
|
112
|
+
npm run typecheck # 针对 0.1.7-rc.2 peer 包(devDependencies)
|
|
113
|
+
npm run typecheck:0.1.5 # 对真实 0.1.5-rc.2 peer 包的遗留交叉核验
|
|
112
114
|
npm test # vitest,90 个用例
|
|
113
115
|
npm run lint
|
|
114
116
|
npm run build # lib/
|
package/cordis.patch.yml
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
# Assembly layer for the dsh profiles whose bundles list includes this package.
|
|
2
2
|
# A pure addition: one insert row with an id no shipped entry uses. No shipped
|
|
3
3
|
# row is disabled, renamed, or reconfigured here.
|
|
4
|
+
#
|
|
5
|
+
# Config rides the inserted row: both patch syntaxes ship enabled. The DSH
|
|
6
|
+
# settings panel still projects the volatile `allowCodexPatch` from this row,
|
|
7
|
+
# so a user toggle keeps working without touching this file. Every knob left
|
|
8
|
+
# out of the row resolves to its schema default computed in lib/index.js.
|
|
4
9
|
- insert:
|
|
5
10
|
- id: dsh-patch-edit-plus
|
|
6
11
|
name: dsh-patch-edit-plus
|
|
12
|
+
config:
|
|
13
|
+
allowUnifiedDiff: true
|
|
14
|
+
allowCodexPatch: true
|
package/dsh.plugin.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "dsh-patch-edit-plus",
|
|
3
3
|
"name": "dsh-patch-edit-plus",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.5",
|
|
5
5
|
"description": {
|
|
6
|
-
"en": "Patch-style file editing: one apply_patch tool for git/unified diff
|
|
7
|
-
"zh": "补丁风格文件编辑:单个 apply_patch 工具,接受 git/unified diff
|
|
6
|
+
"en": "Patch-style file editing: one apply_patch tool for git/unified diff and Codex apply_patch syntax (both enabled by default), with all-or-nothing application and sandbox-aware delete/move.",
|
|
7
|
+
"zh": "补丁风格文件编辑:单个 apply_patch 工具,接受 git/unified diff 与 Codex apply_patch 语法(两种语法均默认开启),全量验证后原子应用,删除/移动走沙箱感知 shell。"
|
|
8
8
|
},
|
|
9
9
|
"homepage": "https://github.com/drscrewdriver/dsh-patch-edit-plus",
|
|
10
10
|
"repository": {
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
},
|
|
14
14
|
"license": "MIT",
|
|
15
15
|
"engines": {
|
|
16
|
-
"dsh": ">=0.1.
|
|
16
|
+
"dsh": ">=0.1.7-rc.1 <0.1.8-0"
|
|
17
17
|
},
|
|
18
18
|
"components": {
|
|
19
19
|
"host": "lib/index.js"
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* dsh-patch-edit-plus — 浏览器 half(0.1.7 线;自 0.2.0 线移植)。
|
|
4
|
+
*
|
|
5
|
+
* 单一职责:在 Plugins 页 bundle 详情页的 `plugins.bundle.config` 席位
|
|
6
|
+
* (key = 包名)下注册一张配置卡,暴露两个补丁风格开关——
|
|
7
|
+
* - `allowUnifiedDiff`(Git/unified diff,默认开)
|
|
8
|
+
* - `allowCodexPatch`(Codex apply_patch,默认开——本线两语法皆默认开启)
|
|
9
|
+
*
|
|
10
|
+
* 字段全部是 host 侧 `Config` 的 `.volatile()` 字段:卡片经
|
|
11
|
+
* `configForms.get('dsh-patch-edit-plus')` 自绑本插件 entry(entry id 与
|
|
12
|
+
* 包名一致,无错位),写入走 `form.set(field, !cur)`,提交后宿主经
|
|
13
|
+
* `loader/volatile-update` 原地重注册工具(见 src/index.ts 的 rejudge)。
|
|
14
|
+
*
|
|
15
|
+
* 构建:tsdown → lib/client.js(__ModuleLoader__.load 闭包工厂,id = 包名)。
|
|
16
|
+
* react 是唯一 value-import(平台 external);slots/configForms 经 cordis
|
|
17
|
+
* 服务消费,configForms 缺席时(理论上仅剩极老宿主)卡片整卡隐藏,不阻塞
|
|
18
|
+
* 客户端半。文案内联双语字典(zh/en),不建 locale 文件。
|
|
19
|
+
*/
|
|
20
|
+
import { createElement, useSyncExternalStore } from 'react';
|
|
21
|
+
/** 客户端所需服务:slots(席位注册)+ configForms(entry 配置读写)。同族插件
|
|
22
|
+
* (session-guard/perm-gate)同样显式声明——runner 只把声明了的服务提供给模块
|
|
23
|
+
* 上下文,不声明时 `ctx.get('configForms')` 恒为 undefined,卡会渲染成 null。 */
|
|
24
|
+
export const inject = ['slots', 'configForms'];
|
|
25
|
+
/** 内联双语字典(zh/en;按 document.documentElement.lang → navigator.language 兜底)。 */
|
|
26
|
+
const STRINGS = {
|
|
27
|
+
zh: {
|
|
28
|
+
cardName: '补丁风格',
|
|
29
|
+
cardDesc: 'apply_patch 接受的补丁语法',
|
|
30
|
+
unifiedTitle: 'Git 风格补丁',
|
|
31
|
+
unifiedDesc: '接受 Git/unified diff 补丁语法(默认开)',
|
|
32
|
+
codexTitle: 'Codex 风格补丁',
|
|
33
|
+
codexDesc: '接受 Codex apply_patch 语法(默认开)',
|
|
34
|
+
readonlyHint: '当前只读,无法修改。',
|
|
35
|
+
},
|
|
36
|
+
en: {
|
|
37
|
+
cardName: 'Patch style',
|
|
38
|
+
cardDesc: 'Patch syntax accepted by apply_patch',
|
|
39
|
+
unifiedTitle: 'Git-style patches',
|
|
40
|
+
unifiedDesc: 'Accept Git/unified diff patch syntax (default on)',
|
|
41
|
+
codexTitle: 'Codex-style patches',
|
|
42
|
+
codexDesc: 'Accept Codex apply_patch syntax (default on)',
|
|
43
|
+
readonlyHint: 'Read-only right now; changes are not allowed.',
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
/** 解析当前语言:document.documentElement.lang 优先,navigator.language 兜底,再退 zh。 */
|
|
47
|
+
function pickLang() {
|
|
48
|
+
try {
|
|
49
|
+
const docLang = typeof document !== 'undefined' ? document.documentElement.lang : '';
|
|
50
|
+
const navLang = typeof navigator !== 'undefined' ? navigator.language : '';
|
|
51
|
+
for (const tag of [docLang, navLang]) {
|
|
52
|
+
if (typeof tag === 'string' && /^zh/i.test(tag))
|
|
53
|
+
return 'zh';
|
|
54
|
+
if (typeof tag === 'string' && /^en/i.test(tag))
|
|
55
|
+
return 'en';
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// 非 DOM 环境保底 zh
|
|
60
|
+
}
|
|
61
|
+
return 'zh';
|
|
62
|
+
}
|
|
63
|
+
/** 卡片样式,注入一次(保持 bundle CSS-free;变量与 session-guard 卡同源)。 */
|
|
64
|
+
const CARD_CSS = `
|
|
65
|
+
.ppeCard{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;padding:4px 16px 8px}
|
|
66
|
+
.ppeCardName{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4;margin:10px 0 2px}
|
|
67
|
+
.ppeCardDesc{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5;margin:0 0 4px}
|
|
68
|
+
.ppeRow{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:center;gap:8px;padding:14px 0;display:flex}
|
|
69
|
+
.ppeRow:last-of-type{border-bottom:0}
|
|
70
|
+
.ppeRowText{flex-direction:column;flex:1;gap:4px;min-width:0;padding-right:48px;display:flex}
|
|
71
|
+
.ppeTitle{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:400;line-height:22px}
|
|
72
|
+
.ppeDesc{color:var(--dsw-alias-label-tertiary);font-size:12px;font-weight:400;line-height:18px}
|
|
73
|
+
.ppeSwitch{appearance:none;position:relative;width:40px;height:22px;flex:none;cursor:pointer;background:transparent;border:0;padding:0}
|
|
74
|
+
.ppeSwitch:disabled{cursor:not-allowed}
|
|
75
|
+
.ppeSwitchTrack{position:absolute;inset:0;border-radius:22px;background:var(--dsw-alias-interactive-bg-hover);transition:background .16s}
|
|
76
|
+
.ppeSwitch-on>.ppeSwitchTrack{background:var(--dsw-alias-button-primary-fill)}
|
|
77
|
+
.ppeSwitchThumb{position:absolute;top:2px;left:2px;width:18px;height:18px;border-radius:50%;background:#fff;box-shadow:0 1px 2px rgba(0,0,0,.25);transition:transform .16s}
|
|
78
|
+
.ppeSwitch-on>.ppeSwitchThumb{transform:translateX(18px)}
|
|
79
|
+
.ppeSwitch:disabled>.ppeSwitchTrack{opacity:.5}
|
|
80
|
+
.ppeReadonly{color:var(--dsw-alias-label-tertiary);font-size:12px;margin:8px 0 4px}
|
|
81
|
+
`;
|
|
82
|
+
/** 注入一次卡片样式。 */
|
|
83
|
+
function injectCss() {
|
|
84
|
+
if (typeof document !== 'undefined' && document.querySelector('style[data-plugin-css="patch-edit-plus-card"]') === null) {
|
|
85
|
+
const tag = document.createElement('style');
|
|
86
|
+
tag.dataset.plugin = 'dsh-patch-edit-plus';
|
|
87
|
+
tag.dataset.pluginCss = 'patch-edit-plus-card';
|
|
88
|
+
tag.textContent = CARD_CSS;
|
|
89
|
+
document.head.appendChild(tag);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const NOOP_SNAPSHOT = { status: 'unavailable', value: undefined, writable: false };
|
|
93
|
+
const unsubscribeNoop = () => { };
|
|
94
|
+
/** 一行 role="switch" 开关(aria-checked + data-* 便于测试选择器)。 */
|
|
95
|
+
function SwitchRow(props) {
|
|
96
|
+
const { field, title, description, checked, disabled, onToggle } = props;
|
|
97
|
+
return (_jsxs("div", { className: "ppeRow", children: [_jsxs("div", { className: "ppeRowText", children: [_jsx("div", { className: "ppeTitle", children: title }), _jsx("div", { className: "ppeDesc", children: description })] }), _jsxs("button", { type: "button", role: "switch", "aria-checked": checked, "data-plugin": "dsh-patch-edit-plus", "data-field": field, "data-checked": checked ? 'true' : 'false', className: 'ppeSwitch' + (checked ? ' ppeSwitch-on' : ''), disabled: disabled, onClick: () => onToggle(field, checked), children: [_jsx("span", { className: "ppeSwitchTrack" }), _jsx("span", { className: "ppeSwitchThumb" })] })] }));
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* 插件页配置卡主体:两行补丁风格开关。
|
|
101
|
+
* - status === 'unavailable'(无 configForms / entry 未装配)→ 整卡隐藏(null);
|
|
102
|
+
* - status === 'loading' → 开关禁用;
|
|
103
|
+
* - 仅 snapshot.writable 时可写(写路径 `form.set(field, !cur)`)。
|
|
104
|
+
*/
|
|
105
|
+
export function PatchStyleCard(props) {
|
|
106
|
+
const form = props.form;
|
|
107
|
+
const snapshot = useSyncExternalStore(form
|
|
108
|
+
? (listener) => form.subscribe(listener)
|
|
109
|
+
: () => unsubscribeNoop, () => form?.getSnapshot() ?? NOOP_SNAPSHOT);
|
|
110
|
+
injectCss();
|
|
111
|
+
if (snapshot.status === 'unavailable')
|
|
112
|
+
return null;
|
|
113
|
+
const t = STRINGS[pickLang()];
|
|
114
|
+
// loading / 只读都归约为「不可写」:开关禁用,仅 ready + writable 时可写。
|
|
115
|
+
const editable = snapshot.status === 'ready' && snapshot.writable;
|
|
116
|
+
const value = snapshot.value ?? {};
|
|
117
|
+
const toggle = (field, current) => {
|
|
118
|
+
if (form === undefined || !editable)
|
|
119
|
+
return;
|
|
120
|
+
void form.set(field, !current).catch(() => { });
|
|
121
|
+
};
|
|
122
|
+
return (_jsxs("section", { className: "ppeCard", "data-plugin": "dsh-patch-edit-plus", children: [_jsx("div", { className: "ppeCardName", children: t.cardName }), _jsx("p", { className: "ppeCardDesc", children: t.cardDesc }), _jsx(SwitchRow, { field: "allowUnifiedDiff", title: t.unifiedTitle, description: t.unifiedDesc, checked: value.allowUnifiedDiff ?? true, disabled: !editable, onToggle: toggle }), _jsx(SwitchRow, { field: "allowCodexPatch", title: t.codexTitle, description: t.codexDesc, checked: value.allowCodexPatch ?? true, disabled: !editable, onToggle: toggle }), snapshot.status === 'ready' && !snapshot.writable && _jsx("p", { className: "ppeReadonly", children: t.readonlyHint })] }));
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* 客户端插件体:在 Plugins 页 bundle 详情页的 keyed 席位(key = 包名)下
|
|
126
|
+
* 注册配置卡。卡片自绑 `configForms.get('dsh-patch-edit-plus')`——form 在
|
|
127
|
+
* 渲染期解析(而非 apply 期固化),宿主晚于客户端半开始服务该 entry 时
|
|
128
|
+
* 也能追上;页面注入的 owner props(`{ view: 'page', form }`)不消费,
|
|
129
|
+
* 以本插件 entry 的作用域为唯一事实来源。
|
|
130
|
+
*/
|
|
131
|
+
export function apply(ctx) {
|
|
132
|
+
const configForms = (ctx.configForms
|
|
133
|
+
?? (typeof ctx.get === 'function' ? ctx.get('configForms') : undefined));
|
|
134
|
+
ctx.slots.inject('plugins.bundle.config', () => ctx.slots.register({ name: 'plugins.bundle.config', key: 'dsh-patch-edit-plus' }, () => createElement(PatchStyleCard, { form: configForms?.get?.('dsh-patch-edit-plus') })));
|
|
135
|
+
}
|
package/lib/client.js
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: "dsh-patch-edit-plus",
|
|
3
|
+
factory: (require) => {
|
|
4
|
+
var module = { exports: {} };
|
|
5
|
+
var exports = module.exports;
|
|
6
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
|
+
let react = require("react");
|
|
8
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
9
|
+
//#region src/client/index.tsx
|
|
10
|
+
/**
|
|
11
|
+
* dsh-patch-edit-plus — 浏览器 half(0.1.7 线;自 0.2.0 线移植)。
|
|
12
|
+
*
|
|
13
|
+
* 单一职责:在 Plugins 页 bundle 详情页的 `plugins.bundle.config` 席位
|
|
14
|
+
* (key = 包名)下注册一张配置卡,暴露两个补丁风格开关——
|
|
15
|
+
* - `allowUnifiedDiff`(Git/unified diff,默认开)
|
|
16
|
+
* - `allowCodexPatch`(Codex apply_patch,默认开——本线两语法皆默认开启)
|
|
17
|
+
*
|
|
18
|
+
* 字段全部是 host 侧 `Config` 的 `.volatile()` 字段:卡片经
|
|
19
|
+
* `configForms.get('dsh-patch-edit-plus')` 自绑本插件 entry(entry id 与
|
|
20
|
+
* 包名一致,无错位),写入走 `form.set(field, !cur)`,提交后宿主经
|
|
21
|
+
* `loader/volatile-update` 原地重注册工具(见 src/index.ts 的 rejudge)。
|
|
22
|
+
*
|
|
23
|
+
* 构建:tsdown → lib/client.js(__ModuleLoader__.load 闭包工厂,id = 包名)。
|
|
24
|
+
* react 是唯一 value-import(平台 external);slots/configForms 经 cordis
|
|
25
|
+
* 服务消费,configForms 缺席时(理论上仅剩极老宿主)卡片整卡隐藏,不阻塞
|
|
26
|
+
* 客户端半。文案内联双语字典(zh/en),不建 locale 文件。
|
|
27
|
+
*/
|
|
28
|
+
/** 客户端所需服务:slots(席位注册)+ configForms(entry 配置读写)。同族插件
|
|
29
|
+
* (session-guard/perm-gate)同样显式声明——runner 只把声明了的服务提供给模块
|
|
30
|
+
* 上下文,不声明时 `ctx.get('configForms')` 恒为 undefined,卡会渲染成 null。 */
|
|
31
|
+
const inject = ["slots", "configForms"];
|
|
32
|
+
/** 内联双语字典(zh/en;按 document.documentElement.lang → navigator.language 兜底)。 */
|
|
33
|
+
const STRINGS = {
|
|
34
|
+
zh: {
|
|
35
|
+
cardName: "补丁风格",
|
|
36
|
+
cardDesc: "apply_patch 接受的补丁语法",
|
|
37
|
+
unifiedTitle: "Git 风格补丁",
|
|
38
|
+
unifiedDesc: "接受 Git/unified diff 补丁语法(默认开)",
|
|
39
|
+
codexTitle: "Codex 风格补丁",
|
|
40
|
+
codexDesc: "接受 Codex apply_patch 语法(默认开)",
|
|
41
|
+
readonlyHint: "当前只读,无法修改。"
|
|
42
|
+
},
|
|
43
|
+
en: {
|
|
44
|
+
cardName: "Patch style",
|
|
45
|
+
cardDesc: "Patch syntax accepted by apply_patch",
|
|
46
|
+
unifiedTitle: "Git-style patches",
|
|
47
|
+
unifiedDesc: "Accept Git/unified diff patch syntax (default on)",
|
|
48
|
+
codexTitle: "Codex-style patches",
|
|
49
|
+
codexDesc: "Accept Codex apply_patch syntax (default on)",
|
|
50
|
+
readonlyHint: "Read-only right now; changes are not allowed."
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
/** 解析当前语言:document.documentElement.lang 优先,navigator.language 兜底,再退 zh。 */
|
|
54
|
+
function pickLang() {
|
|
55
|
+
try {
|
|
56
|
+
const docLang = typeof document !== "undefined" ? document.documentElement.lang : "";
|
|
57
|
+
const navLang = typeof navigator !== "undefined" ? navigator.language : "";
|
|
58
|
+
for (const tag of [docLang, navLang]) {
|
|
59
|
+
if (typeof tag === "string" && /^zh/i.test(tag)) return "zh";
|
|
60
|
+
if (typeof tag === "string" && /^en/i.test(tag)) return "en";
|
|
61
|
+
}
|
|
62
|
+
} catch {}
|
|
63
|
+
return "zh";
|
|
64
|
+
}
|
|
65
|
+
/** 卡片样式,注入一次(保持 bundle CSS-free;变量与 session-guard 卡同源)。 */
|
|
66
|
+
const CARD_CSS = `
|
|
67
|
+
.ppeCard{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;padding:4px 16px 8px}
|
|
68
|
+
.ppeCardName{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4;margin:10px 0 2px}
|
|
69
|
+
.ppeCardDesc{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5;margin:0 0 4px}
|
|
70
|
+
.ppeRow{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:center;gap:8px;padding:14px 0;display:flex}
|
|
71
|
+
.ppeRow:last-of-type{border-bottom:0}
|
|
72
|
+
.ppeRowText{flex-direction:column;flex:1;gap:4px;min-width:0;padding-right:48px;display:flex}
|
|
73
|
+
.ppeTitle{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:400;line-height:22px}
|
|
74
|
+
.ppeDesc{color:var(--dsw-alias-label-tertiary);font-size:12px;font-weight:400;line-height:18px}
|
|
75
|
+
.ppeSwitch{appearance:none;position:relative;width:40px;height:22px;flex:none;cursor:pointer;background:transparent;border:0;padding:0}
|
|
76
|
+
.ppeSwitch:disabled{cursor:not-allowed}
|
|
77
|
+
.ppeSwitchTrack{position:absolute;inset:0;border-radius:22px;background:var(--dsw-alias-interactive-bg-hover);transition:background .16s}
|
|
78
|
+
.ppeSwitch-on>.ppeSwitchTrack{background:var(--dsw-alias-button-primary-fill)}
|
|
79
|
+
.ppeSwitchThumb{position:absolute;top:2px;left:2px;width:18px;height:18px;border-radius:50%;background:#fff;box-shadow:0 1px 2px rgba(0,0,0,.25);transition:transform .16s}
|
|
80
|
+
.ppeSwitch-on>.ppeSwitchThumb{transform:translateX(18px)}
|
|
81
|
+
.ppeSwitch:disabled>.ppeSwitchTrack{opacity:.5}
|
|
82
|
+
.ppeReadonly{color:var(--dsw-alias-label-tertiary);font-size:12px;margin:8px 0 4px}
|
|
83
|
+
`;
|
|
84
|
+
/** 注入一次卡片样式。 */
|
|
85
|
+
function injectCss() {
|
|
86
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=\"patch-edit-plus-card\"]") === null) {
|
|
87
|
+
const tag = document.createElement("style");
|
|
88
|
+
tag.dataset.plugin = "dsh-patch-edit-plus";
|
|
89
|
+
tag.dataset.pluginCss = "patch-edit-plus-card";
|
|
90
|
+
tag.textContent = CARD_CSS;
|
|
91
|
+
document.head.appendChild(tag);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const NOOP_SNAPSHOT = {
|
|
95
|
+
status: "unavailable",
|
|
96
|
+
value: void 0,
|
|
97
|
+
writable: false
|
|
98
|
+
};
|
|
99
|
+
const unsubscribeNoop = () => {};
|
|
100
|
+
/** 一行 role="switch" 开关(aria-checked + data-* 便于测试选择器)。 */
|
|
101
|
+
function SwitchRow(props) {
|
|
102
|
+
const { field, title, description, checked, disabled, onToggle } = props;
|
|
103
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
104
|
+
className: "ppeRow",
|
|
105
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
106
|
+
className: "ppeRowText",
|
|
107
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
108
|
+
className: "ppeTitle",
|
|
109
|
+
children: title
|
|
110
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
111
|
+
className: "ppeDesc",
|
|
112
|
+
children: description
|
|
113
|
+
})]
|
|
114
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
115
|
+
type: "button",
|
|
116
|
+
role: "switch",
|
|
117
|
+
"aria-checked": checked,
|
|
118
|
+
"data-plugin": "dsh-patch-edit-plus",
|
|
119
|
+
"data-field": field,
|
|
120
|
+
"data-checked": checked ? "true" : "false",
|
|
121
|
+
className: "ppeSwitch" + (checked ? " ppeSwitch-on" : ""),
|
|
122
|
+
disabled,
|
|
123
|
+
onClick: () => onToggle(field, checked),
|
|
124
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "ppeSwitchTrack" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "ppeSwitchThumb" })]
|
|
125
|
+
})]
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* 插件页配置卡主体:两行补丁风格开关。
|
|
130
|
+
* - status === 'unavailable'(无 configForms / entry 未装配)→ 整卡隐藏(null);
|
|
131
|
+
* - status === 'loading' → 开关禁用;
|
|
132
|
+
* - 仅 snapshot.writable 时可写(写路径 `form.set(field, !cur)`)。
|
|
133
|
+
*/
|
|
134
|
+
function PatchStyleCard(props) {
|
|
135
|
+
const form = props.form;
|
|
136
|
+
const snapshot = (0, react.useSyncExternalStore)(form ? (listener) => form.subscribe(listener) : () => unsubscribeNoop, () => form?.getSnapshot() ?? NOOP_SNAPSHOT);
|
|
137
|
+
injectCss();
|
|
138
|
+
if (snapshot.status === "unavailable") return null;
|
|
139
|
+
const t = STRINGS[pickLang()];
|
|
140
|
+
const editable = snapshot.status === "ready" && snapshot.writable;
|
|
141
|
+
const value = snapshot.value ?? {};
|
|
142
|
+
const toggle = (field, current) => {
|
|
143
|
+
if (form === void 0 || !editable) return;
|
|
144
|
+
form.set(field, !current).catch(() => {});
|
|
145
|
+
};
|
|
146
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
147
|
+
className: "ppeCard",
|
|
148
|
+
"data-plugin": "dsh-patch-edit-plus",
|
|
149
|
+
children: [
|
|
150
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
151
|
+
className: "ppeCardName",
|
|
152
|
+
children: t.cardName
|
|
153
|
+
}),
|
|
154
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
155
|
+
className: "ppeCardDesc",
|
|
156
|
+
children: t.cardDesc
|
|
157
|
+
}),
|
|
158
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SwitchRow, {
|
|
159
|
+
field: "allowUnifiedDiff",
|
|
160
|
+
title: t.unifiedTitle,
|
|
161
|
+
description: t.unifiedDesc,
|
|
162
|
+
checked: value.allowUnifiedDiff ?? true,
|
|
163
|
+
disabled: !editable,
|
|
164
|
+
onToggle: toggle
|
|
165
|
+
}),
|
|
166
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SwitchRow, {
|
|
167
|
+
field: "allowCodexPatch",
|
|
168
|
+
title: t.codexTitle,
|
|
169
|
+
description: t.codexDesc,
|
|
170
|
+
checked: value.allowCodexPatch ?? true,
|
|
171
|
+
disabled: !editable,
|
|
172
|
+
onToggle: toggle
|
|
173
|
+
}),
|
|
174
|
+
snapshot.status === "ready" && !snapshot.writable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
175
|
+
className: "ppeReadonly",
|
|
176
|
+
children: t.readonlyHint
|
|
177
|
+
})
|
|
178
|
+
]
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* 客户端插件体:在 Plugins 页 bundle 详情页的 keyed 席位(key = 包名)下
|
|
183
|
+
* 注册配置卡。卡片自绑 `configForms.get('dsh-patch-edit-plus')`——form 在
|
|
184
|
+
* 渲染期解析(而非 apply 期固化),宿主晚于客户端半开始服务该 entry 时
|
|
185
|
+
* 也能追上;页面注入的 owner props(`{ view: 'page', form }`)不消费,
|
|
186
|
+
* 以本插件 entry 的作用域为唯一事实来源。
|
|
187
|
+
*/
|
|
188
|
+
function apply(ctx) {
|
|
189
|
+
const configForms = ctx.configForms ?? (typeof ctx.get === "function" ? ctx.get("configForms") : void 0);
|
|
190
|
+
ctx.slots.inject("plugins.bundle.config", () => ctx.slots.register({
|
|
191
|
+
name: "plugins.bundle.config",
|
|
192
|
+
key: "dsh-patch-edit-plus"
|
|
193
|
+
}, () => (0, react.createElement)(PatchStyleCard, { form: configForms?.get?.("dsh-patch-edit-plus") })));
|
|
194
|
+
}
|
|
195
|
+
//#endregion
|
|
196
|
+
exports.PatchStyleCard = PatchStyleCard;
|
|
197
|
+
exports.apply = apply;
|
|
198
|
+
exports.inject = inject;
|
|
199
|
+
return module.exports;
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","names":["useSyncExternalStore","createElement"],"sources":["../src/client/index.tsx"],"sourcesContent":["/**\n * dsh-patch-edit-plus — 浏览器 half(0.1.7 线;自 0.2.0 线移植)。\n *\n * 单一职责:在 Plugins 页 bundle 详情页的 `plugins.bundle.config` 席位\n * (key = 包名)下注册一张配置卡,暴露两个补丁风格开关——\n * - `allowUnifiedDiff`(Git/unified diff,默认开)\n * - `allowCodexPatch`(Codex apply_patch,默认开——本线两语法皆默认开启)\n *\n * 字段全部是 host 侧 `Config` 的 `.volatile()` 字段:卡片经\n * `configForms.get('dsh-patch-edit-plus')` 自绑本插件 entry(entry id 与\n * 包名一致,无错位),写入走 `form.set(field, !cur)`,提交后宿主经\n * `loader/volatile-update` 原地重注册工具(见 src/index.ts 的 rejudge)。\n *\n * 构建:tsdown → lib/client.js(__ModuleLoader__.load 闭包工厂,id = 包名)。\n * react 是唯一 value-import(平台 external);slots/configForms 经 cordis\n * 服务消费,configForms 缺席时(理论上仅剩极老宿主)卡片整卡隐藏,不阻塞\n * 客户端半。文案内联双语字典(zh/en),不建 locale 文件。\n */\nimport { createElement, useSyncExternalStore } from 'react'\nimport type { JSX } from 'react'\n\n/** 本插件消费的 configForms 值形状(与 host ResolvedConfig 的易变布尔对齐)。 */\nexport interface PatchEditPlusConfig {\n allowUnifiedDiff: boolean\n allowCodexPatch: boolean\n}\n\n/** configForms 作用域面(结构性子集;0.1.7+ 缝,避免硬 peer 类型)。 */\nexport interface PatchFormScope {\n getSnapshot(): {\n status: 'loading' | 'ready' | 'unavailable'\n value: Partial<PatchEditPlusConfig> | undefined\n writable: boolean\n }\n subscribe(listener: () => void): () => void\n set(field: string, value: unknown): Promise<boolean>\n}\n\n/** configForms 服务面(以 entry id 取句柄)。 */\ninterface ConfigFormsFace {\n get?(entryId: string): PatchFormScope | undefined\n}\n\n/** 客户端所需服务:slots(席位注册)+ configForms(entry 配置读写)。同族插件\n * (session-guard/perm-gate)同样显式声明——runner 只把声明了的服务提供给模块\n * 上下文,不声明时 `ctx.get('configForms')` 恒为 undefined,卡会渲染成 null。 */\nexport const inject = ['slots', 'configForms']\n\n/** 轻量 ctx 类型(仅本客户端用到的方法;构建时类型被剥离)。 */\ninterface SlotsFace {\n inject: (_name: string, _fn: () => unknown) => () => void\n register: (_options: Record<string, unknown>, _component: unknown) => () => void\n}\ninterface ClientCtx {\n slots: SlotsFace\n get?: (_name: string) => unknown\n}\n\n/** 内联双语字典(zh/en;按 document.documentElement.lang → navigator.language 兜底)。 */\nconst STRINGS = {\n zh: {\n cardName: '补丁风格',\n cardDesc: 'apply_patch 接受的补丁语法',\n unifiedTitle: 'Git 风格补丁',\n unifiedDesc: '接受 Git/unified diff 补丁语法(默认开)',\n codexTitle: 'Codex 风格补丁',\n codexDesc: '接受 Codex apply_patch 语法(默认开)',\n readonlyHint: '当前只读,无法修改。',\n },\n en: {\n cardName: 'Patch style',\n cardDesc: 'Patch syntax accepted by apply_patch',\n unifiedTitle: 'Git-style patches',\n unifiedDesc: 'Accept Git/unified diff patch syntax (default on)',\n codexTitle: 'Codex-style patches',\n codexDesc: 'Accept Codex apply_patch syntax (default on)',\n readonlyHint: 'Read-only right now; changes are not allowed.',\n },\n} as const\n\ntype Lang = keyof typeof STRINGS\n\n/** 解析当前语言:document.documentElement.lang 优先,navigator.language 兜底,再退 zh。 */\nfunction pickLang(): Lang {\n try {\n const docLang = typeof document !== 'undefined' ? document.documentElement.lang : ''\n const navLang = typeof navigator !== 'undefined' ? navigator.language : ''\n for (const tag of [docLang, navLang]) {\n if (typeof tag === 'string' && /^zh/i.test(tag)) return 'zh'\n if (typeof tag === 'string' && /^en/i.test(tag)) return 'en'\n }\n } catch {\n // 非 DOM 环境保底 zh\n }\n return 'zh'\n}\n\n/** 卡片样式,注入一次(保持 bundle CSS-free;变量与 session-guard 卡同源)。 */\nconst CARD_CSS = `\n.ppeCard{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;padding:4px 16px 8px}\n.ppeCardName{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4;margin:10px 0 2px}\n.ppeCardDesc{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5;margin:0 0 4px}\n.ppeRow{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:center;gap:8px;padding:14px 0;display:flex}\n.ppeRow:last-of-type{border-bottom:0}\n.ppeRowText{flex-direction:column;flex:1;gap:4px;min-width:0;padding-right:48px;display:flex}\n.ppeTitle{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:400;line-height:22px}\n.ppeDesc{color:var(--dsw-alias-label-tertiary);font-size:12px;font-weight:400;line-height:18px}\n.ppeSwitch{appearance:none;position:relative;width:40px;height:22px;flex:none;cursor:pointer;background:transparent;border:0;padding:0}\n.ppeSwitch:disabled{cursor:not-allowed}\n.ppeSwitchTrack{position:absolute;inset:0;border-radius:22px;background:var(--dsw-alias-interactive-bg-hover);transition:background .16s}\n.ppeSwitch-on>.ppeSwitchTrack{background:var(--dsw-alias-button-primary-fill)}\n.ppeSwitchThumb{position:absolute;top:2px;left:2px;width:18px;height:18px;border-radius:50%;background:#fff;box-shadow:0 1px 2px rgba(0,0,0,.25);transition:transform .16s}\n.ppeSwitch-on>.ppeSwitchThumb{transform:translateX(18px)}\n.ppeSwitch:disabled>.ppeSwitchTrack{opacity:.5}\n.ppeReadonly{color:var(--dsw-alias-label-tertiary);font-size:12px;margin:8px 0 4px}\n`\n\n/** 注入一次卡片样式。 */\nfunction injectCss(): void {\n if (typeof document !== 'undefined' && document.querySelector('style[data-plugin-css=\"patch-edit-plus-card\"]') === null) {\n const tag = document.createElement('style')\n tag.dataset.plugin = 'dsh-patch-edit-plus'\n tag.dataset.pluginCss = 'patch-edit-plus-card'\n tag.textContent = CARD_CSS\n document.head.appendChild(tag)\n }\n}\n\nconst NOOP_SNAPSHOT = { status: 'unavailable' as const, value: undefined, writable: false }\nconst unsubscribeNoop = (): void => {}\n\n/** 一行 role=\"switch\" 开关(aria-checked + data-* 便于测试选择器)。 */\nfunction SwitchRow(props: {\n field: 'allowUnifiedDiff' | 'allowCodexPatch'\n title: string\n description: string\n checked: boolean\n disabled: boolean\n onToggle: (_field: 'allowUnifiedDiff' | 'allowCodexPatch', _current: boolean) => void\n}): JSX.Element {\n const { field, title, description, checked, disabled, onToggle } = props\n return (\n <div className=\"ppeRow\">\n <div className=\"ppeRowText\">\n <div className=\"ppeTitle\">{title}</div>\n <div className=\"ppeDesc\">{description}</div>\n </div>\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={checked}\n data-plugin=\"dsh-patch-edit-plus\"\n data-field={field}\n data-checked={checked ? 'true' : 'false'}\n className={'ppeSwitch' + (checked ? ' ppeSwitch-on' : '')}\n disabled={disabled}\n onClick={() => onToggle(field, checked)}\n >\n <span className=\"ppeSwitchTrack\" />\n <span className=\"ppeSwitchThumb\" />\n </button>\n </div>\n )\n}\n\n/**\n * 插件页配置卡主体:两行补丁风格开关。\n * - status === 'unavailable'(无 configForms / entry 未装配)→ 整卡隐藏(null);\n * - status === 'loading' → 开关禁用;\n * - 仅 snapshot.writable 时可写(写路径 `form.set(field, !cur)`)。\n */\nexport function PatchStyleCard(props: { form?: PatchFormScope | undefined }): JSX.Element | null {\n const form = props.form\n const snapshot = useSyncExternalStore(\n form\n ? (listener) => form.subscribe(listener)\n : () => unsubscribeNoop,\n () => form?.getSnapshot() ?? NOOP_SNAPSHOT,\n )\n\n injectCss()\n\n if (snapshot.status === 'unavailable') return null\n const t = STRINGS[pickLang()]\n // loading / 只读都归约为「不可写」:开关禁用,仅 ready + writable 时可写。\n const editable = snapshot.status === 'ready' && snapshot.writable\n const value = snapshot.value ?? {}\n const toggle = (field: 'allowUnifiedDiff' | 'allowCodexPatch', current: boolean): void => {\n if (form === undefined || !editable) return\n void form.set(field, !current).catch(() => {})\n }\n return (\n <section className=\"ppeCard\" data-plugin=\"dsh-patch-edit-plus\">\n <div className=\"ppeCardName\">{t.cardName}</div>\n <p className=\"ppeCardDesc\">{t.cardDesc}</p>\n <SwitchRow\n field=\"allowUnifiedDiff\"\n title={t.unifiedTitle}\n description={t.unifiedDesc}\n checked={value.allowUnifiedDiff ?? true}\n disabled={!editable}\n onToggle={toggle}\n />\n <SwitchRow\n field=\"allowCodexPatch\"\n title={t.codexTitle}\n description={t.codexDesc}\n checked={value.allowCodexPatch ?? true}\n disabled={!editable}\n onToggle={toggle}\n />\n {snapshot.status === 'ready' && !snapshot.writable && <p className=\"ppeReadonly\">{t.readonlyHint}</p>}\n </section>\n )\n}\n\n/**\n * 客户端插件体:在 Plugins 页 bundle 详情页的 keyed 席位(key = 包名)下\n * 注册配置卡。卡片自绑 `configForms.get('dsh-patch-edit-plus')`——form 在\n * 渲染期解析(而非 apply 期固化),宿主晚于客户端半开始服务该 entry 时\n * 也能追上;页面注入的 owner props(`{ view: 'page', form }`)不消费,\n * 以本插件 entry 的作用域为唯一事实来源。\n */\nexport function apply(ctx: ClientCtx): void {\n const configForms = ((ctx as unknown as { configForms?: ConfigFormsFace }).configForms\n ?? (typeof ctx.get === 'function' ? ctx.get('configForms') : undefined)) as ConfigFormsFace | undefined\n ctx.slots.inject('plugins.bundle.config', () => ctx.slots.register(\n { name: 'plugins.bundle.config', key: 'dsh-patch-edit-plus' },\n () => createElement(PatchStyleCard, { form: configForms?.get?.('dsh-patch-edit-plus') }),\n ))\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8CA,MAAa,SAAS,CAAC,SAAS,aAAa;;EAa7C,MAAM,UAAU;GACd,IAAI;IACF,UAAU;IACV,UAAU;IACV,cAAc;IACd,aAAa;IACb,YAAY;IACZ,WAAW;IACX,cAAc;GAChB;GACA,IAAI;IACF,UAAU;IACV,UAAU;IACV,cAAc;IACd,aAAa;IACb,YAAY;IACZ,WAAW;IACX,cAAc;GAChB;EACF;;EAKA,SAAS,WAAiB;GACxB,IAAI;IACF,MAAM,UAAU,OAAO,aAAa,cAAc,SAAS,gBAAgB,OAAO;IAClF,MAAM,UAAU,OAAO,cAAc,cAAc,UAAU,WAAW;IACxE,KAAK,MAAM,OAAO,CAAC,SAAS,OAAO,GAAG;KACpC,IAAI,OAAO,QAAQ,YAAY,OAAO,KAAK,GAAG,GAAG,OAAO;KACxD,IAAI,OAAO,QAAQ,YAAY,OAAO,KAAK,GAAG,GAAG,OAAO;IAC1D;GACF,QAAQ,CAER;GACA,OAAO;EACT;;EAGA,MAAM,WAAW;;;;;;;;;;;;;;;;;;;EAoBjB,SAAS,YAAkB;GACzB,IAAI,OAAO,aAAa,eAAe,SAAS,cAAc,iDAA+C,MAAM,MAAM;IACvH,MAAM,MAAM,SAAS,cAAc,OAAO;IAC1C,IAAI,QAAQ,SAAS;IACrB,IAAI,QAAQ,YAAY;IACxB,IAAI,cAAc;IAClB,SAAS,KAAK,YAAY,GAAG;GAC/B;EACF;EAEA,MAAM,gBAAgB;GAAE,QAAQ;GAAwB,OAAO,KAAA;GAAW,UAAU;EAAM;EAC1F,MAAM,wBAA8B,CAAC;;EAGrC,SAAS,UAAU,OAOH;GACd,MAAM,EAAE,OAAO,OAAO,aAAa,SAAS,UAAU,aAAa;GACnE,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;KAAK,WAAU;KAAf,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MAAK,WAAU;MAAY,UAAA;KAAW,CAAA,GACtC,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MAAK,WAAU;MAAW,UAAA;KAAiB,CAAA,CACxC;IACL,CAAA,GAAA,iBAAA,GAAA,kBAAA,KAAA,CAAC,UAAD;KACE,MAAK;KACL,MAAK;KACL,gBAAc;KACd,eAAY;KACZ,cAAY;KACZ,gBAAc,UAAU,SAAS;KACjC,WAAW,eAAe,UAAU,kBAAkB;KAC5C;KACV,eAAe,SAAS,OAAO,OAAO;KATxC,UAAA,CAWE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD,EAAM,WAAU,iBAAkB,CAAA,GAClC,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD,EAAM,WAAU,iBAAkB,CAAA,CAC5B;IACL,CAAA,CAAA;;EAET;;;;;;;EAQA,SAAgB,eAAe,OAAkE;GAC/F,MAAM,OAAO,MAAM;GACnB,MAAM,YAAA,GAAWA,MAAAA,qBAAAA,CACf,QACK,aAAa,KAAK,UAAU,QAAQ,UAC/B,uBACJ,MAAM,YAAY,KAAK,aAC/B;GAEA,UAAU;GAEV,IAAI,SAAS,WAAW,eAAe,OAAO;GAC9C,MAAM,IAAI,QAAQ,SAAS;GAE3B,MAAM,WAAW,SAAS,WAAW,WAAW,SAAS;GACzD,MAAM,QAAQ,SAAS,SAAS,CAAC;GACjC,MAAM,UAAU,OAA+C,YAA2B;IACxF,IAAI,SAAS,KAAA,KAAa,CAAC,UAAU;IACrC,KAAU,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC;GAC/C;GACA,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,WAAD;IAAS,WAAU;IAAU,eAAY;IAAzC,UAAA;KACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MAAK,WAAU;MAAe,UAAA,EAAE;KAAc,CAAA;KAC9C,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;MAAG,WAAU;MAAe,UAAA,EAAE;KAAY,CAAA;KAC1C,iBAAA,GAAA,kBAAA,IAAA,CAAC,WAAD;MACE,OAAM;MACN,OAAO,EAAE;MACT,aAAa,EAAE;MACf,SAAS,MAAM,oBAAoB;MACnC,UAAU,CAAC;MACX,UAAU;KACX,CAAA;KACD,iBAAA,GAAA,kBAAA,IAAA,CAAC,WAAD;MACE,OAAM;MACN,OAAO,EAAE;MACT,aAAa,EAAE;MACf,SAAS,MAAM,mBAAmB;MAClC,UAAU,CAAC;MACX,UAAU;KACX,CAAA;KACA,SAAS,WAAW,WAAW,CAAC,SAAS,YAAY,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;MAAG,WAAU;MAAe,UAAA,EAAE;KAAgB,CAAA;IAC7F;;EAEb;;;;;;;;EASA,SAAgB,MAAM,KAAsB;GAC1C,MAAM,cAAgB,IAAqD,gBACrE,OAAO,IAAI,QAAQ,aAAa,IAAI,IAAI,aAAa,IAAI,KAAA;GAC/D,IAAI,MAAM,OAAO,+BAA+B,IAAI,MAAM,SACxD;IAAE,MAAM;IAAyB,KAAK;GAAsB,UAAA,GACtDC,MAAAA,cAAAA,CAAc,gBAAgB,EAAE,MAAM,aAAa,MAAM,qBAAqB,EAAE,CAAC,CACzF,CAAC;EACH"}
|
package/lib/config.js
CHANGED
|
@@ -11,8 +11,8 @@ export const Config = z.object({
|
|
|
11
11
|
toolName: z.string().default('apply_patch'),
|
|
12
12
|
conflictPolicy: z.union(['rename', 'skip', 'fail']).default('rename'),
|
|
13
13
|
renameSuffix: z.string().default('_1'),
|
|
14
|
-
allowUnifiedDiff: z.boolean().default(true),
|
|
15
|
-
allowCodexPatch: z.boolean().default(
|
|
14
|
+
allowUnifiedDiff: z.boolean().default(true).volatile(),
|
|
15
|
+
allowCodexPatch: z.boolean().default(true).volatile(),
|
|
16
16
|
deleteBackend: z.union(['shell', 'none']).default('shell'),
|
|
17
17
|
shellDialect: z.union(['auto', 'posix', 'pwsh']).default('auto'),
|
|
18
18
|
deleteCommand: z.string(),
|
|
@@ -23,6 +23,14 @@ export const Config = z.object({
|
|
|
23
23
|
maxPatchBytes: z.number().step(1).min(1024).default(512 * 1024),
|
|
24
24
|
maxDiffBytes: z.number().step(1).min(256).default(16 * 1024),
|
|
25
25
|
});
|
|
26
|
+
/** Read a `.volatile()` field: a live ref on 0.1.7+, a plain boolean otherwise. */
|
|
27
|
+
export function readVolatileBoolean(value, fallback) {
|
|
28
|
+
if (typeof value === 'boolean')
|
|
29
|
+
return value;
|
|
30
|
+
if (value && typeof value.get === 'function')
|
|
31
|
+
return Boolean(value.get());
|
|
32
|
+
return fallback;
|
|
33
|
+
}
|
|
26
34
|
/** Fill user config with defaults; `undefined`/null entries fall back too. */
|
|
27
35
|
export function resolveConfig(config) {
|
|
28
36
|
const c = config ?? {};
|
|
@@ -30,8 +38,8 @@ export function resolveConfig(config) {
|
|
|
30
38
|
toolName: typeof c.toolName === 'string' && c.toolName.trim() !== '' ? c.toolName : 'apply_patch',
|
|
31
39
|
conflictPolicy: c.conflictPolicy ?? 'rename',
|
|
32
40
|
renameSuffix: typeof c.renameSuffix === 'string' ? c.renameSuffix : '_1',
|
|
33
|
-
allowUnifiedDiff: c.allowUnifiedDiff
|
|
34
|
-
allowCodexPatch: c.allowCodexPatch
|
|
41
|
+
allowUnifiedDiff: readVolatileBoolean(c.allowUnifiedDiff, true),
|
|
42
|
+
allowCodexPatch: readVolatileBoolean(c.allowCodexPatch, true),
|
|
35
43
|
deleteBackend: c.deleteBackend ?? 'shell',
|
|
36
44
|
shellDialect: c.shellDialect ?? 'auto',
|
|
37
45
|
deleteCommand: typeof c.deleteCommand === 'string' && c.deleteCommand !== '' ? c.deleteCommand : undefined,
|
package/lib/index.js
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* dsh-patch-edit-plus — patch-style file editing for DeepSeek Harness.
|
|
3
3
|
*
|
|
4
|
-
* One `apply_patch` tool accepting git/unified diff
|
|
5
|
-
*
|
|
4
|
+
* One `apply_patch` tool accepting git/unified diff and Codex `apply_patch`
|
|
5
|
+
* syntax (both on by default; the patch-layer row in `cordis.patch.yml` ships
|
|
6
|
+
* the same defaults). All-or-nothing application through the
|
|
6
7
|
* official fs write-intent dance; delete/move through the sandbox-aware
|
|
7
|
-
* shell.
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* shell. Targets DSH 0.1.7+: settings are declarative — the fields marked
|
|
9
|
+
* `.volatile()` in `Config` render the settings form automatically (no
|
|
10
|
+
* registration call), and `loader/volatile-update` drives re-registration
|
|
11
|
+
* without a plugin remount. This line drops the pre-0.1.7 hosts; the
|
|
12
|
+
* 0.1.2-rc.1 ~ 0.1.5-rc.2 line stays on its maintenance branch.
|
|
11
13
|
*
|
|
12
14
|
* @module dsh-patch-edit-plus
|
|
13
15
|
*/
|
|
14
|
-
import {
|
|
16
|
+
import { resolveConfig } from './config.js';
|
|
15
17
|
import { registerApplyPatchTool } from './register.js';
|
|
16
18
|
/** Cordis plugin name used by Loader diagnostics. */
|
|
17
19
|
export const name = 'dsh-patch-edit-plus';
|
|
@@ -31,26 +33,21 @@ export const name = 'dsh-patch-edit-plus';
|
|
|
31
33
|
* in every profile that has no shell capability.
|
|
32
34
|
*/
|
|
33
35
|
export const inject = ['tools', 'fs'];
|
|
36
|
+
export { Config } from './config.js';
|
|
34
37
|
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
38
|
+
* Register the tool from the current config snapshot.
|
|
39
|
+
*
|
|
40
|
+
* 登记级 key。工具「描述」把可用语法固化进去了(tool.ts 拼 `Accepts ${styles}`),
|
|
41
|
+
* 所以判定必须覆盖整份 resolved config,而不是只看 allowCodexPatch。
|
|
39
42
|
*/
|
|
40
|
-
export const SETTINGS_NAMESPACE = 'patch-edit-plus';
|
|
41
|
-
export { Config };
|
|
42
|
-
/** Register the tool and the settings namespace. Every registration is scoped to this plugin. */
|
|
43
43
|
export function apply(ctx, config) {
|
|
44
|
+
// 组合条目(0.1.7 起 volatile 字段在其中是 live 引用):rejudge 每次整体
|
|
45
|
+
// 重解析一次,一次调用内是同一份快照;跨调用的最新值由事件驱动重取。
|
|
44
46
|
const compositionEntry = config ?? {};
|
|
45
|
-
// 权威来源:设置层挂载时是解析后的 scope,否则是组合条目。
|
|
46
|
-
// installSection 保证在每次匹配的 onChange 之前先调用 setSource(attach 与 detach 各一次)。
|
|
47
|
-
let readSource = () => compositionEntry;
|
|
48
47
|
let disposer = null;
|
|
49
|
-
// 登记级 key。工具「描述」把可用语法固化进去了(tool.ts 拼 `Accepts ${styles}`),
|
|
50
|
-
// 所以判定必须覆盖整份 resolved config,而不是只看 allowCodexPatch。
|
|
51
48
|
let registeredKey = null;
|
|
52
49
|
const rejudge = () => {
|
|
53
|
-
const next = resolveConfig(
|
|
50
|
+
const next = resolveConfig(compositionEntry);
|
|
54
51
|
const key = JSON.stringify(next);
|
|
55
52
|
if (key === registeredKey)
|
|
56
53
|
return;
|
|
@@ -66,23 +63,9 @@ export function apply(ctx, config) {
|
|
|
66
63
|
registeredKey = null;
|
|
67
64
|
};
|
|
68
65
|
});
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
settings.installSection(ctx, SETTINGS_NAMESPACE, Config, compositionEntry, {
|
|
74
|
-
setSource: (current) => { readSource = current; },
|
|
75
|
-
onChange: () => { rejudge(); },
|
|
76
|
-
});
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
if (typeof settings?.register === 'function') {
|
|
80
|
-
const scope = settings.register(SETTINGS_NAMESPACE, Config, { base: compositionEntry });
|
|
81
|
-
if (scope !== undefined) {
|
|
82
|
-
readSource = () => scope.get();
|
|
83
|
-
scope.watch(() => { rejudge(); });
|
|
84
|
-
rejudge();
|
|
85
|
-
}
|
|
86
|
-
}
|
|
66
|
+
// 0.1.7+: 只有 volatile 字段变更才走这里(不 remount);普通字段变更会整体
|
|
67
|
+
// remount 插件,新一次 `apply` 自然读到全部新值。等值变更宿主不通知。
|
|
68
|
+
ctx.on('loader/volatile-update', () => {
|
|
69
|
+
rejudge();
|
|
87
70
|
});
|
|
88
71
|
}
|
package/lib/shellops.js
CHANGED
|
@@ -36,7 +36,7 @@ export function moveTemplate(cfg) {
|
|
|
36
36
|
export function resolveShell(ctx) {
|
|
37
37
|
try {
|
|
38
38
|
const shell = ctx.shell;
|
|
39
|
-
return shell !== undefined && typeof shell.
|
|
39
|
+
return shell !== undefined && typeof shell.execute === 'function' && typeof shell.resolve === 'function' ? shell : undefined;
|
|
40
40
|
}
|
|
41
41
|
catch {
|
|
42
42
|
return undefined;
|
|
@@ -87,7 +87,8 @@ function requireShell(ctx, cfg) {
|
|
|
87
87
|
}
|
|
88
88
|
async function runShell(shell, request) {
|
|
89
89
|
try {
|
|
90
|
-
|
|
90
|
+
const execution = await shell.execute(shell.resolve(request));
|
|
91
|
+
return await execution.result();
|
|
91
92
|
}
|
|
92
93
|
catch (error) {
|
|
93
94
|
const detail = error instanceof Error ? error.message : String(error);
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { JSX } from 'react';
|
|
2
|
+
/** 本插件消费的 configForms 值形状(与 host ResolvedConfig 的易变布尔对齐)。 */
|
|
3
|
+
export interface PatchEditPlusConfig {
|
|
4
|
+
allowUnifiedDiff: boolean;
|
|
5
|
+
allowCodexPatch: boolean;
|
|
6
|
+
}
|
|
7
|
+
/** configForms 作用域面(结构性子集;0.1.7+ 缝,避免硬 peer 类型)。 */
|
|
8
|
+
export interface PatchFormScope {
|
|
9
|
+
getSnapshot(): {
|
|
10
|
+
status: 'loading' | 'ready' | 'unavailable';
|
|
11
|
+
value: Partial<PatchEditPlusConfig> | undefined;
|
|
12
|
+
writable: boolean;
|
|
13
|
+
};
|
|
14
|
+
subscribe(listener: () => void): () => void;
|
|
15
|
+
set(field: string, value: unknown): Promise<boolean>;
|
|
16
|
+
}
|
|
17
|
+
/** 客户端所需服务:slots(席位注册)+ configForms(entry 配置读写)。同族插件
|
|
18
|
+
* (session-guard/perm-gate)同样显式声明——runner 只把声明了的服务提供给模块
|
|
19
|
+
* 上下文,不声明时 `ctx.get('configForms')` 恒为 undefined,卡会渲染成 null。 */
|
|
20
|
+
export declare const inject: string[];
|
|
21
|
+
/** 轻量 ctx 类型(仅本客户端用到的方法;构建时类型被剥离)。 */
|
|
22
|
+
interface SlotsFace {
|
|
23
|
+
inject: (_name: string, _fn: () => unknown) => () => void;
|
|
24
|
+
register: (_options: Record<string, unknown>, _component: unknown) => () => void;
|
|
25
|
+
}
|
|
26
|
+
interface ClientCtx {
|
|
27
|
+
slots: SlotsFace;
|
|
28
|
+
get?: (_name: string) => unknown;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* 插件页配置卡主体:两行补丁风格开关。
|
|
32
|
+
* - status === 'unavailable'(无 configForms / entry 未装配)→ 整卡隐藏(null);
|
|
33
|
+
* - status === 'loading' → 开关禁用;
|
|
34
|
+
* - 仅 snapshot.writable 时可写(写路径 `form.set(field, !cur)`)。
|
|
35
|
+
*/
|
|
36
|
+
export declare function PatchStyleCard(props: {
|
|
37
|
+
form?: PatchFormScope | undefined;
|
|
38
|
+
}): JSX.Element | null;
|
|
39
|
+
/**
|
|
40
|
+
* 客户端插件体:在 Plugins 页 bundle 详情页的 keyed 席位(key = 包名)下
|
|
41
|
+
* 注册配置卡。卡片自绑 `configForms.get('dsh-patch-edit-plus')`——form 在
|
|
42
|
+
* 渲染期解析(而非 apply 期固化),宿主晚于客户端半开始服务该 entry 时
|
|
43
|
+
* 也能追上;页面注入的 owner props(`{ view: 'page', form }`)不消费,
|
|
44
|
+
* 以本插件 entry 的作用域为唯一事实来源。
|
|
45
|
+
*/
|
|
46
|
+
export declare function apply(ctx: ClientCtx): void;
|
|
47
|
+
export {};
|
package/lib/types/config.d.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* @module dsh-patch-edit-plus/config
|
|
7
7
|
*/
|
|
8
8
|
import z from '@deepseek-ai/schemastery';
|
|
9
|
+
import type { Volatile } from '@deepseek-ai/cosmokit';
|
|
9
10
|
/** What to do when the configured tool name is already taken at registration. */
|
|
10
11
|
export type ConflictPolicy = 'rename' | 'skip' | 'fail';
|
|
11
12
|
/** Backend used for Delete/Move operations (`ctx.fs` has no delete/move). */
|
|
@@ -20,10 +21,16 @@ export interface Config {
|
|
|
20
21
|
conflictPolicy?: ConflictPolicy;
|
|
21
22
|
/** Suffix appended on rename avoidance. Defaults to `_1`. */
|
|
22
23
|
renameSuffix?: string;
|
|
23
|
-
/** Accept git/unified diff patches. Defaults to true.
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
/** Accept git/unified diff patches. Defaults to true.
|
|
25
|
+
* 0.1.7 `.volatile()`: the loader hands `apply` a live `Volatile` ref for this
|
|
26
|
+
* field — always read it through `readVolatileBoolean`, never cache the ref. */
|
|
27
|
+
allowUnifiedDiff?: boolean | Volatile<boolean>;
|
|
28
|
+
/** Accept Codex `apply_patch` syntax. Defaults to true (both syntaxes on;
|
|
29
|
+
* the patch-layer row in `cordis.patch.yml` carries the same default). Set
|
|
30
|
+
* false to restore unified-diff-only.
|
|
31
|
+
* 0.1.7 `.volatile()`: the loader hands `apply` a live `Volatile` ref for this
|
|
32
|
+
* field — always read it through `readVolatileBoolean`, never cache the ref. */
|
|
33
|
+
allowCodexPatch?: boolean | Volatile<boolean>;
|
|
27
34
|
/** Delete/Move backend. Defaults to `shell`. `none` rejects Delete/Move with a structured error. */
|
|
28
35
|
deleteBackend?: DeleteBackend;
|
|
29
36
|
/** Shell dialect for the constant delete/move templates. Defaults to `auto` (pwsh on win32). */
|
|
@@ -44,7 +51,37 @@ export interface Config {
|
|
|
44
51
|
maxDiffBytes?: number;
|
|
45
52
|
}
|
|
46
53
|
/** Schemastery schema for Loader defaults and generated configuration docs. */
|
|
47
|
-
export declare const Config: z<
|
|
54
|
+
export declare const Config: z<Schemastery.ObjectS<NoInfer<{
|
|
55
|
+
toolName: z<string, string, "defined">;
|
|
56
|
+
conflictPolicy: z<"rename" | "skip" | "fail", "rename" | "skip" | "fail", "defined">;
|
|
57
|
+
renameSuffix: z<string, string, "defined">;
|
|
58
|
+
allowUnifiedDiff: z<boolean, boolean, "volatile-defined">;
|
|
59
|
+
allowCodexPatch: z<boolean, boolean, "volatile-defined">;
|
|
60
|
+
deleteBackend: z<"shell" | "none", "shell" | "none", "defined">;
|
|
61
|
+
shellDialect: z<"auto" | "posix" | "pwsh", "auto" | "posix" | "pwsh", "defined">;
|
|
62
|
+
deleteCommand: z<string, string, "plain">;
|
|
63
|
+
moveCommand: z<string, string, "plain">;
|
|
64
|
+
dryRunByDefault: z<boolean, boolean, "defined">;
|
|
65
|
+
followSymlinks: z<boolean, boolean, "defined">;
|
|
66
|
+
maxFiles: z<number, number, "defined">;
|
|
67
|
+
maxPatchBytes: z<number, number, "defined">;
|
|
68
|
+
maxDiffBytes: z<number, number, "defined">;
|
|
69
|
+
}>>, Schemastery.ObjectT<NoInfer<{
|
|
70
|
+
toolName: z<string, string, "defined">;
|
|
71
|
+
conflictPolicy: z<"rename" | "skip" | "fail", "rename" | "skip" | "fail", "defined">;
|
|
72
|
+
renameSuffix: z<string, string, "defined">;
|
|
73
|
+
allowUnifiedDiff: z<boolean, boolean, "volatile-defined">;
|
|
74
|
+
allowCodexPatch: z<boolean, boolean, "volatile-defined">;
|
|
75
|
+
deleteBackend: z<"shell" | "none", "shell" | "none", "defined">;
|
|
76
|
+
shellDialect: z<"auto" | "posix" | "pwsh", "auto" | "posix" | "pwsh", "defined">;
|
|
77
|
+
deleteCommand: z<string, string, "plain">;
|
|
78
|
+
moveCommand: z<string, string, "plain">;
|
|
79
|
+
dryRunByDefault: z<boolean, boolean, "defined">;
|
|
80
|
+
followSymlinks: z<boolean, boolean, "defined">;
|
|
81
|
+
maxFiles: z<number, number, "defined">;
|
|
82
|
+
maxPatchBytes: z<number, number, "defined">;
|
|
83
|
+
maxDiffBytes: z<number, number, "defined">;
|
|
84
|
+
}>>, "plain">;
|
|
48
85
|
/** Fully-resolved configuration used by the tool body. */
|
|
49
86
|
export interface ResolvedConfig {
|
|
50
87
|
toolName: string;
|
|
@@ -62,5 +99,7 @@ export interface ResolvedConfig {
|
|
|
62
99
|
maxPatchBytes: number;
|
|
63
100
|
maxDiffBytes: number;
|
|
64
101
|
}
|
|
102
|
+
/** Read a `.volatile()` field: a live ref on 0.1.7+, a plain boolean otherwise. */
|
|
103
|
+
export declare function readVolatileBoolean(value: boolean | Volatile<boolean> | undefined, fallback: boolean): boolean;
|
|
65
104
|
/** Fill user config with defaults; `undefined`/null entries fall back too. */
|
|
66
105
|
export declare function resolveConfig(config: Config | undefined): ResolvedConfig;
|
package/lib/types/index.d.ts
CHANGED
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* dsh-patch-edit-plus — patch-style file editing for DeepSeek Harness.
|
|
3
3
|
*
|
|
4
|
-
* One `apply_patch` tool accepting git/unified diff
|
|
5
|
-
*
|
|
4
|
+
* One `apply_patch` tool accepting git/unified diff and Codex `apply_patch`
|
|
5
|
+
* syntax (both on by default; the patch-layer row in `cordis.patch.yml` ships
|
|
6
|
+
* the same defaults). All-or-nothing application through the
|
|
6
7
|
* official fs write-intent dance; delete/move through the sandbox-aware
|
|
7
|
-
* shell.
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* shell. Targets DSH 0.1.7+: settings are declarative — the fields marked
|
|
9
|
+
* `.volatile()` in `Config` render the settings form automatically (no
|
|
10
|
+
* registration call), and `loader/volatile-update` drives re-registration
|
|
11
|
+
* without a plugin remount. This line drops the pre-0.1.7 hosts; the
|
|
12
|
+
* 0.1.2-rc.1 ~ 0.1.5-rc.2 line stays on its maintenance branch.
|
|
11
13
|
*
|
|
12
14
|
* @module dsh-patch-edit-plus
|
|
13
15
|
*/
|
|
14
16
|
import type { Context } from '@deepseek-ai/cordis';
|
|
15
|
-
import { Config } from './config.js';
|
|
16
17
|
import type { Config as PluginConfig } from './config.js';
|
|
18
|
+
declare module '@deepseek-ai/cordis' {
|
|
19
|
+
interface Events {
|
|
20
|
+
'loader/volatile-update': (paths: string[]) => void;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
17
23
|
/** Cordis plugin name used by Loader diagnostics. */
|
|
18
24
|
export declare const name = "dsh-patch-edit-plus";
|
|
19
25
|
/**
|
|
@@ -32,13 +38,11 @@ export declare const name = "dsh-patch-edit-plus";
|
|
|
32
38
|
* in every profile that has no shell capability.
|
|
33
39
|
*/
|
|
34
40
|
export declare const inject: string[];
|
|
41
|
+
export { Config } from './config.js';
|
|
35
42
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
43
|
+
* Register the tool from the current config snapshot.
|
|
44
|
+
*
|
|
45
|
+
* 登记级 key。工具「描述」把可用语法固化进去了(tool.ts 拼 `Accepts ${styles}`),
|
|
46
|
+
* 所以判定必须覆盖整份 resolved config,而不是只看 allowCodexPatch。
|
|
40
47
|
*/
|
|
41
|
-
export declare const SETTINGS_NAMESPACE = "patch-edit-plus";
|
|
42
|
-
export { Config };
|
|
43
|
-
/** Register the tool and the settings namespace. Every registration is scoped to this plugin. */
|
|
44
48
|
export declare function apply(ctx: Context, config?: PluginConfig): void;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-patch-edit-plus",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "Patch-style file editing for DeepSeek Harness: one apply_patch tool accepting git/unified diff
|
|
3
|
+
"version": "0.2.7",
|
|
4
|
+
"description": "Patch-style file editing for DeepSeek Harness: one apply_patch tool accepting git/unified diff and Codex apply_patch syntax (both enabled by default), with all-or-nothing application, read-before-write intent dance, sandbox-aware delete/move, with a DSH 0.1.7+ line (declarative volatile settings).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -14,6 +14,10 @@
|
|
|
14
14
|
"types": "./lib/types/index.d.ts",
|
|
15
15
|
"default": "./lib/index.js"
|
|
16
16
|
},
|
|
17
|
+
"./client": {
|
|
18
|
+
"types": "./lib/types/client/index.d.ts",
|
|
19
|
+
"default": "./lib/client.js"
|
|
20
|
+
},
|
|
17
21
|
"./package.json": "./package.json"
|
|
18
22
|
},
|
|
19
23
|
"files": [
|
|
@@ -31,10 +35,16 @@
|
|
|
31
35
|
"dsh": {
|
|
32
36
|
"bundle": {
|
|
33
37
|
"patch": "./cordis.patch.yml"
|
|
38
|
+
},
|
|
39
|
+
"client": {
|
|
40
|
+
"platform": "web",
|
|
41
|
+
"inject": [
|
|
42
|
+
"@deepseek-ai/dsh-client-ui-settings"
|
|
43
|
+
]
|
|
34
44
|
}
|
|
35
45
|
},
|
|
36
46
|
"scripts": {
|
|
37
|
-
"build": "tsc -p tsconfig.build.json",
|
|
47
|
+
"build": "tsc -p tsconfig.build.json && tsdown",
|
|
38
48
|
"prepublishOnly": "npm run build",
|
|
39
49
|
"typecheck": "tsc -p tsconfig.json",
|
|
40
50
|
"typecheck:0.1.5": "node scripts/typecheck-0.1.5.mjs",
|
|
@@ -47,7 +57,7 @@
|
|
|
47
57
|
},
|
|
48
58
|
"engines": {
|
|
49
59
|
"node": ">=20",
|
|
50
|
-
"dsh": ">=0.1.
|
|
60
|
+
"dsh": ">=0.1.7-rc.1 <0.1.8-0"
|
|
51
61
|
},
|
|
52
62
|
"license": "MIT",
|
|
53
63
|
"keywords": [
|
|
@@ -62,15 +72,24 @@
|
|
|
62
72
|
],
|
|
63
73
|
"peerDependencies": {
|
|
64
74
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
65
|
-
"@deepseek-ai/dsh-
|
|
66
|
-
"@deepseek-ai/dsh-
|
|
67
|
-
"@deepseek-ai/dsh-
|
|
68
|
-
"@deepseek-ai/dsh-
|
|
75
|
+
"@deepseek-ai/dsh-client-ui-settings": ">=0.1.7-rc.1 <0.1.8-0",
|
|
76
|
+
"@deepseek-ai/dsh-client-ui-slots": ">=0.1.7-rc.1 <0.1.8-0",
|
|
77
|
+
"@deepseek-ai/dsh-fs": ">=0.1.7-rc.1 <0.1.8-0",
|
|
78
|
+
"@deepseek-ai/dsh-sandbox": ">=0.1.7-rc.1 <0.1.8-0",
|
|
79
|
+
"@deepseek-ai/dsh-shell": ">=0.1.7-rc.1 <0.1.8-0",
|
|
80
|
+
"@deepseek-ai/dsh-tools": ">=0.1.7-rc.1 <0.1.8-0",
|
|
81
|
+
"react": "^18.2.0"
|
|
69
82
|
},
|
|
70
83
|
"peerDependenciesMeta": {
|
|
71
84
|
"@deepseek-ai/cordis": {
|
|
72
85
|
"optional": true
|
|
73
86
|
},
|
|
87
|
+
"@deepseek-ai/dsh-client-ui-settings": {
|
|
88
|
+
"optional": true
|
|
89
|
+
},
|
|
90
|
+
"@deepseek-ai/dsh-client-ui-slots": {
|
|
91
|
+
"optional": true
|
|
92
|
+
},
|
|
74
93
|
"@deepseek-ai/dsh-fs": {
|
|
75
94
|
"optional": true
|
|
76
95
|
},
|
|
@@ -85,19 +104,26 @@
|
|
|
85
104
|
}
|
|
86
105
|
},
|
|
87
106
|
"dependencies": {
|
|
88
|
-
"@deepseek-ai/schemastery": "^3.18.
|
|
107
|
+
"@deepseek-ai/schemastery": "^3.18.4"
|
|
89
108
|
},
|
|
90
109
|
"devDependencies": {
|
|
91
110
|
"@deepseek-ai/cordis": "^4.0.2",
|
|
92
|
-
"@deepseek-ai/dsh-fs": "0.1.
|
|
93
|
-
"@deepseek-ai/dsh-sandbox": "0.1.
|
|
94
|
-
"@deepseek-ai/dsh-shell": "0.1.
|
|
95
|
-
"@deepseek-ai/dsh-tools": "0.1.
|
|
111
|
+
"@deepseek-ai/dsh-fs": "0.1.7-rc.2",
|
|
112
|
+
"@deepseek-ai/dsh-sandbox": "0.1.7-rc.2",
|
|
113
|
+
"@deepseek-ai/dsh-shell": "0.1.7-rc.2",
|
|
114
|
+
"@deepseek-ai/dsh-tools": "0.1.7-rc.2",
|
|
96
115
|
"@eslint/js": "^9.0.0",
|
|
97
116
|
"@types/node": "^22.0.0",
|
|
117
|
+
"@types/react": "~18.3.1",
|
|
98
118
|
"eslint": "^9.0.0",
|
|
119
|
+
"react": "^18.2.0",
|
|
120
|
+
"tsdown": "^0.22.2",
|
|
99
121
|
"typescript": "^5.6.0",
|
|
100
122
|
"typescript-eslint": "^8.0.0",
|
|
101
|
-
"vitest": "^3.0.0"
|
|
123
|
+
"vitest": "^3.0.0",
|
|
124
|
+
"@deepseek-ai/schemastery": "^3.18.4"
|
|
125
|
+
},
|
|
126
|
+
"publishConfig": {
|
|
127
|
+
"tag": "dsh-0.1.7"
|
|
102
128
|
}
|
|
103
129
|
}
|