dsh-gitbash-shell 0.13.2 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -3
- package/README_EN.md +50 -4
- package/dsh.plugin.json +1 -1
- package/package.json +1 -1
- package/src/client.js +352 -11
- package/src/index.js +206 -7
- package/src/shell.js +57 -15
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://awesome-dsh-plugin.com)
|
|
4
4
|
|
|
5
|
+
简体中文 | [English](README_EN.md)
|
|
6
|
+
|
|
5
7
|
> 让 DeepSeek Harness (dsh) 在 Windows 上**全部使用 Git Bash** 的官方风格插件
|
|
6
8
|
> —— 以 Git for Windows 的 `bash.exe` 替换 PowerShell 执行器,并为所有模式
|
|
7
9
|
> 物化对应的 Git Bash 版 agent preset。
|
|
@@ -37,6 +39,19 @@ era。**preset id 保持 `code-gitbash` 不变**(会话钉在 id 上,改名会
|
|
|
37
39
|
会话报 preset not found)。无论先升级插件还是先升级 dsh,都会自动收敛;用户改
|
|
38
40
|
过的目录照旧不碰。
|
|
39
41
|
|
|
42
|
+
### dsh 0.1.6 适配(工作流引擎行改名)
|
|
43
|
+
|
|
44
|
+
dsh 0.1.6-alpha.1 把内置预设的工作流引擎行 `workflow-worker-thread` 改名为
|
|
45
|
+
`workflow-ptc`,并**删除**了旧包。组合里一行 import 失败会拒绝**整棵 preset 挂载**,
|
|
46
|
+
所以把旧名钉死在资产里的 preset 在新版上会直接不可用。本插件两个拼法都不钉:物化时
|
|
47
|
+
**从宿主自己的内置 preset 现场抄**那一行的 id、包名与 `disabled` 状态
|
|
48
|
+
(`rowFormsOf` / `alignEngineRow`),并让 `tool-ralph` 跟随新版默认的 `disabled: true`。
|
|
49
|
+
改写是纯字符串手术(不解析 YAML,`!!js` 安全)且幂等;探测失败(旧宿主、无 roster)时
|
|
50
|
+
资产保持逐字节原样——**一份资产通吃两个 era,升级顺序无关**。
|
|
51
|
+
|
|
52
|
+
同一版还把 `LocalBashExecutor` 的受保护钩子改成了异步:插件的 `runArgv` 结果解包、
|
|
53
|
+
`start` 的返回形态与 `confine` 的取消信号都按**加载期探测**自适应,新旧宿主行为一致。
|
|
54
|
+
|
|
40
55
|
## 安装(公开 npm 插件,推荐)
|
|
41
56
|
|
|
42
57
|
npm: [dsh-gitbash-shell](https://www.npmjs.com/package/dsh-gitbash-shell),
|
|
@@ -129,9 +144,30 @@ preset 会被插件自动清理;宿主 shell 回退为 PowerShell。
|
|
|
129
144
|
> 切换生效后若 `ptc-cordis` 目录已存在且被旧版本物化,删除
|
|
130
145
|
> `~/.dsh/.agent-presets/ptc-cordis` 并重启,即由新逻辑重新物化。
|
|
131
146
|
|
|
132
|
-
## POSIX
|
|
147
|
+
## POSIX 路径方言(v0.7.0 引入,受 `posixPaths` 开关门控)
|
|
148
|
+
|
|
149
|
+
Windows 上本插件把宿主 shell 换成 Git Bash 的同时,让**模型看到的路径**统一成 MSYS 盘根
|
|
150
|
+
POSIX 形式(`/c/Users/...`、`/c/Program Files/...`)。开关是 settings 命名空间
|
|
151
|
+
`gitbash-shell` 的布尔字段 `posixPaths`,**默认开启**(v0.10.0 起),可在 **设置 → 插件**
|
|
152
|
+
里本插件的「Git Bash 路径方言」卡片上随时切换;不修改任何 preset / 组合文件,标准/极简/
|
|
153
|
+
PTC/创造及用户自建模式一律覆盖。
|
|
154
|
+
|
|
155
|
+
开启时(仅 Win32),`posixPaths` 门控以下全部行为:
|
|
156
|
+
|
|
157
|
+
- **提示词源头替换**:组装期(`system-prompt/assemble`)把官方提示词 sections / contexts /
|
|
158
|
+
variables 里的 Windows 绝对路径**原位**改写成 `/c/...`——不增删任何内容、不动工具 schema;
|
|
159
|
+
- **一句话指示**:经 `systemPrompt.context`(order 126)注入全局指示——shell 是 Git for
|
|
160
|
+
Windows bash,路径用 MSYS 盘根,所有工具都直接接受这种写法;
|
|
161
|
+
- **参数翻译**:`tools/execute` 上把文件工具的路径参数(`file_path` / `path` / `workdir`)由
|
|
162
|
+
`/c/...` 翻回 `C:/...` 交给 Node 文件工具;bash 的 `command` 字段不动——那是 Git Bash 母语;
|
|
163
|
+
- **结果回流**:成功结果里的路径元数据(`read`/`write`/`edit` 的 `path`、`glob` 的 `paths[]`、
|
|
164
|
+
`grep` 的 `matches[].path`)改写回 MSYS 形式;文件内容与错误结果不动;
|
|
165
|
+
- **运行时事实**:向官方 `dsh-shell-env` 注册表贡献 `DSH_PATH_DIALECT=msys`,模型可在执行时
|
|
166
|
+
核验(随开关实时生效)。
|
|
133
167
|
|
|
134
|
-
|
|
168
|
+
关闭后回到 dsh 原生行为:指示文本为空(组装期直接丢弃,零提示噪声),路径参数与结果元数据
|
|
169
|
+
都不再改写,文件工具收 Windows 路径。**bash 始终是 Git Bash,不受此开关影响**——它只决定
|
|
170
|
+
跨工具的路径方言。
|
|
135
171
|
|
|
136
172
|
## 与 dsh-better-sidebar 联动
|
|
137
173
|
|
|
@@ -143,4 +179,4 @@ Windows 上本插件把宿主 shell 换成 Git Bash 的同时,会向**每个会
|
|
|
143
179
|
|
|
144
180
|
## 许可
|
|
145
181
|
|
|
146
|
-
MIT © KannaKuron。参考与致敬:[dsh-ptc-cordis-preset](https://github.com/KannaKuron/dsh-ptc-cordis-preset)。
|
|
182
|
+
MIT © KannaKuron。参考与致敬:[dsh-ptc-cordis-preset](https://github.com/KannaKuron/dsh-ptc-cordis-preset)。
|
package/README_EN.md
CHANGED
|
@@ -2,10 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://awesome-dsh-plugin.com)
|
|
4
4
|
|
|
5
|
+
[简体中文](README.md) | English
|
|
6
|
+
|
|
5
7
|
> Run **every agent shell command through Git Bash** on Windows with DeepSeek
|
|
6
8
|
> Harness (dsh) — replaces the PowerShell executor and materializes Git Bash
|
|
7
9
|
> variants of all four agent presets.
|
|
8
10
|
|
|
11
|
+
### dsh 0.1.6: the workflow-engine row was renamed
|
|
12
|
+
|
|
13
|
+
dsh 0.1.6-alpha.1 renamed the built-in presets' workflow-engine row from
|
|
14
|
+
`workflow-worker-thread` to `workflow-ptc` and **deleted** the old package. One row
|
|
15
|
+
that fails to import rejects the **whole preset mount**, so a composition pinning the
|
|
16
|
+
old name simply stops working on the new host. This plugin pins neither spelling: at
|
|
17
|
+
materialization it copies that row — id, package and `disabled` state — straight out
|
|
18
|
+
of the host's own built-in preset (`rowFormsOf` / `alignEngineRow`), and aligns
|
|
19
|
+
`tool-ralph` with the new `disabled: true` default. The rewrite is plain string
|
|
20
|
+
surgery (no YAML round-trip, so `!!js` stays safe) and idempotent; a failed probe (old
|
|
21
|
+
host, no roster) leaves the assets byte-for-byte untouched — **one set of assets serves
|
|
22
|
+
both eras, in either upgrade order**.
|
|
23
|
+
|
|
24
|
+
The same release made `LocalBashExecutor`'s protected hooks asynchronous: the
|
|
25
|
+
`runArgv` result unwrapping, the `start` return shape and the `confine` cancellation
|
|
26
|
+
signal are all probed at load time, so both host generations behave identically.
|
|
27
|
+
|
|
9
28
|
## Install (public npm package)
|
|
10
29
|
|
|
11
30
|
```sh
|
|
@@ -64,9 +83,36 @@ the PTC preset is materialized as Git Bash automatically (tool-bash on,
|
|
|
64
83
|
tool-pwsh off) — no extra mode, no manual edits. Without this plugin the PTC
|
|
65
84
|
preset stays as its own plugin manages it.
|
|
66
85
|
|
|
67
|
-
## POSIX path
|
|
68
|
-
|
|
69
|
-
While replacing the host shell with Git Bash, this plugin
|
|
86
|
+
## POSIX path dialect (introduced in v0.7.0, gated by the `posixPaths` switch)
|
|
87
|
+
|
|
88
|
+
While replacing the host shell with Git Bash on Windows, this plugin makes every path the
|
|
89
|
+
model sees use the MSYS drive-root POSIX form (/c/Users/..., /c/Program Files/...). The switch
|
|
90
|
+
is the `posixPaths` boolean in the `gitbash-shell` settings namespace; it is **on by default**
|
|
91
|
+
(since v0.10.0) and can be toggled any time on this plugin's "Git Bash path dialect" card in
|
|
92
|
+
**Settings → Plugins**. No preset or composition file is touched, and every mode —
|
|
93
|
+
standard/minimal/PTC/creation and user-authored presets — is covered.
|
|
94
|
+
|
|
95
|
+
While it is on (Windows only), `posixPaths` gates all of the following:
|
|
96
|
+
|
|
97
|
+
- **Source-level rewrite**: during assembly (`system-prompt/assemble`) every Windows absolute
|
|
98
|
+
path in the official prompt's sections, contexts, and variables is rewritten in place to the
|
|
99
|
+
/c/... form — nothing is added or removed, and tool schemas stay untouched;
|
|
100
|
+
- **One-sentence directive**: a global directive via `systemPrompt.context` (order 126) — the
|
|
101
|
+
shell is Git for Windows bash, paths use MSYS drive roots, and every tool accepts that form
|
|
102
|
+
directly;
|
|
103
|
+
- **Argument translation**: on `tools/execute` the file tools' path arguments (`file_path` /
|
|
104
|
+
`path` / `workdir`) are translated from /c/... back to C:/... for the Node-backed file tools;
|
|
105
|
+
a bash command's `command` field is left alone — that is Git Bash's native form;
|
|
106
|
+
- **Result round-trip**: path metadata in successful results (`path` of read/write/edit,
|
|
107
|
+
`paths[]` of glob, `matches[].path` of grep) flows back in the MSYS form; file contents and
|
|
108
|
+
error results are untouched;
|
|
109
|
+
- **Runtime fact**: `DSH_PATH_DIALECT=msys` is contributed to the official `dsh-shell-env`
|
|
110
|
+
registry, so the model can verify the dialect at execution time (it follows the live switch).
|
|
111
|
+
|
|
112
|
+
With the switch off, dsh-native behavior returns: the directive text is empty (dropped at
|
|
113
|
+
assembly, so zero prompt noise), path arguments and result metadata are no longer rewritten, and
|
|
114
|
+
the file tools receive Windows paths. **Bash stays Git Bash either way** — the switch only
|
|
115
|
+
governs the cross-tool path dialect.
|
|
70
116
|
|
|
71
117
|
## Cooperation with dsh-better-sidebar
|
|
72
118
|
|
|
@@ -79,4 +125,4 @@ When [dsh-better-sidebar](https://github.com/omdsh-dev/DSH-better-sidebar) (v0.1
|
|
|
79
125
|
## License
|
|
80
126
|
|
|
81
127
|
MIT © KannaKuron. Inspired by
|
|
82
|
-
[dsh-ptc-cordis-preset](https://github.com/KannaKuron/dsh-ptc-cordis-preset).
|
|
128
|
+
[dsh-ptc-cordis-preset](https://github.com/KannaKuron/dsh-ptc-cordis-preset).
|
package/dsh.plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "dsh-external/dsh-gitbash-shell",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"main": "./src/index.js",
|
|
5
5
|
"description": "Windows 全模式 Git Bash:用 Git for Windows bash 替换 PowerShell 执行器,并物化 standard/minimal/code/cordis 的 Git Bash 变体 preset。双 era 适配 dsh <= 0.1.1(内置 id 'code')与 >= 0.1.2(改名 'ptc'),启动探测自动选择,preset id 永不变。联动:dsh-ptc-cordis-preset(两者同装时 PTC 创造模式自动物化为 Git Bash 版)+ dsh-better-sidebar(终端 UI 标签与模型 terminal_* 工具自动切到 Git Bash)。",
|
|
6
6
|
"engines": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-gitbash-shell",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"description": "DSH plugin: run every agent shell command through Git for Windows bash on Windows instead of PowerShell. Replaces the pwsh executor with a Git Bash ctx.shell provider and materializes Git Bash variants of the standard/minimal/code/cordis agent presets into your user preset root at startup. On Windows it also adopts dsh-better-sidebar's terminal shell (UI tabs and the model-facing terminal_* tools) through the sidebar's official settings seam, and pairs with dsh-ptc-cordis-preset so the PTC Creation-mode preset materializes as its Git Bash variant. Ships both composition eras for dsh <= 0.1.1 (built-in preset id 'code') and dsh >= 0.1.2 (renamed id 'ptc') — the era is probed per boot and re-materialized automatically after a dsh upgrade, in either upgrade order; preset ids never change so pinned sessions stay valid.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
package/src/client.js
CHANGED
|
@@ -21,7 +21,11 @@
|
|
|
21
21
|
* (react and @deepseek-ai/dsh-client-ui-primitives only, smoke-enforced);
|
|
22
22
|
* - plain React.createElement, no JSX/TS; components at module level;
|
|
23
23
|
* - dsh.client.inject in package.json lists the packages that must load
|
|
24
|
-
* first so locale / settingsScope / slots exist when this applies
|
|
24
|
+
* first so locale / settingsScope / slots exist when this applies;
|
|
25
|
+
* - copy ships in three layers: zh/en inline, one LOCALES entry per third
|
|
26
|
+
* language (each preceded by a locale marker line), every dictionary
|
|
27
|
+
* key-aligned with zh (smoke-enforced) and read through a live lookup —
|
|
28
|
+
* DSH's language preference switches without a page reload.
|
|
25
29
|
*/
|
|
26
30
|
window.__ModuleLoader__.load({
|
|
27
31
|
id: "dsh-gitbash-shell",
|
|
@@ -35,6 +39,7 @@ window.__ModuleLoader__.load({
|
|
|
35
39
|
|
|
36
40
|
var E = React.createElement;
|
|
37
41
|
var useState = React.useState;
|
|
42
|
+
var useEffect = React.useEffect;
|
|
38
43
|
|
|
39
44
|
var TAG = "[gitbash-shell/client]";
|
|
40
45
|
/** Dictionary namespace (the plugin's own t seat). */
|
|
@@ -42,11 +47,15 @@ window.__ModuleLoader__.load({
|
|
|
42
47
|
/** The settings namespace the HOST half serves. */
|
|
43
48
|
var SETTINGS_NAMESPACE = "gitbash-shell";
|
|
44
49
|
|
|
45
|
-
// ── dictionaries
|
|
50
|
+
// ── dictionaries ────────────────────────────────────────────────────────
|
|
51
|
+
// zh/en stay inline; the third languages live in LOCALES below, one entry
|
|
52
|
+
// per language behind a locale marker line. EVERY shipped dictionary MUST
|
|
53
|
+
// carry zh's exact key set: a missing key falls back to English at lookup
|
|
54
|
+
// time, which reads as a half-translated card (smoke-enforced).
|
|
46
55
|
|
|
47
56
|
var zh = {
|
|
48
57
|
"title": "Git Bash 路径方言",
|
|
49
|
-
"cardDesc": "所有工具统一 /c/ POSIX 路径形式(
|
|
58
|
+
"cardDesc": "所有工具统一 /c/ POSIX 路径形式(默认开启)",
|
|
50
59
|
"state.label": "当前状态",
|
|
51
60
|
"state.on": "已启用",
|
|
52
61
|
"state.off": "已关闭",
|
|
@@ -58,7 +67,7 @@ window.__ModuleLoader__.load({
|
|
|
58
67
|
|
|
59
68
|
var en = {
|
|
60
69
|
"title": "Git Bash path dialect",
|
|
61
|
-
"cardDesc": "One /c/ POSIX path style for every tool (
|
|
70
|
+
"cardDesc": "One /c/ POSIX path style for every tool (on by default)",
|
|
62
71
|
"state.label": "Current state",
|
|
63
72
|
"state.on": "Enabled",
|
|
64
73
|
"state.off": "Disabled",
|
|
@@ -68,6 +77,302 @@ window.__ModuleLoader__.load({
|
|
|
68
77
|
"hint": "Enabled: every tool (bash commands and workdir, read/write/edit/read_image/glob/grep path arguments) uses MSYS drive-root POSIX paths (/c/Users/...); the host translates path fields for the file tools automatically. Disabled restores dsh-native behavior (Windows paths for file tools). Bash is always Git Bash while the plugin is installed; this switch only governs the cross-tool path dialect.",
|
|
69
78
|
};
|
|
70
79
|
|
|
80
|
+
/* Third-language dictionaries: one entry per language, each preceded
|
|
81
|
+
by a locale marker line the smoke test slices on. Adding a language is
|
|
82
|
+
one entry here and nothing else — the dictionaries also ride the
|
|
83
|
+
ctx.locale.register call below, so host-side consumers read the same
|
|
84
|
+
copy the card does. Every key set MUST equal zh's exactly: a missing
|
|
85
|
+
key falls back to English silently and the card looks half-translated.
|
|
86
|
+
zh-hk and zh-mo ship one Cantonese-flavoured copy; zh-tw carries the
|
|
87
|
+
Taiwan wording. */
|
|
88
|
+
var LOCALES = {
|
|
89
|
+
/* locale: ar */
|
|
90
|
+
"ar": {
|
|
91
|
+
"title": "لهجة مسارات Git Bash",
|
|
92
|
+
"cardDesc": "صيغة مسار POSIX موحّدة /c/ لكل الأدوات (مُفعَّلة افتراضيًا)",
|
|
93
|
+
"state.label": "الحالة الحالية",
|
|
94
|
+
"state.on": "مُفعَّلة",
|
|
95
|
+
"state.off": "مُعطَّلة",
|
|
96
|
+
"switch.on": "تفعيل",
|
|
97
|
+
"switch.off": "تعطيل",
|
|
98
|
+
"error": "فشل الكتابة",
|
|
99
|
+
"hint": "عند التفعيل: تستخدم كل الأدوات (أوامر bash و workdir، ومعاملات المسار في read/write/edit/read_image/glob/grep) مسارات POSIX من جذر قرص MSYS (/c/Users/...)؛ ويتولّى المضيف ترجمة حقول المسار لأدوات الملفات تلقائيًا. عند التعطيل يعود السلوك الأصلي لـ dsh (مسارات Windows لأدوات الملفات). يبقى bash دائمًا Git Bash ما دامت الإضافة مثبَّتة؛ هذا المفتاح لا يحكم سوى لهجة المسارات بين الأدوات.",
|
|
100
|
+
},
|
|
101
|
+
/* locale: de */
|
|
102
|
+
"de": {
|
|
103
|
+
"title": "Git-Bash-Pfaddialekt",
|
|
104
|
+
"cardDesc": "Eine einheitliche /c/-POSIX-Pfadform für alle Tools (standardmäßig an)",
|
|
105
|
+
"state.label": "Aktueller Status",
|
|
106
|
+
"state.on": "Aktiviert",
|
|
107
|
+
"state.off": "Deaktiviert",
|
|
108
|
+
"switch.on": "Aktivieren",
|
|
109
|
+
"switch.off": "Deaktivieren",
|
|
110
|
+
"error": "Schreiben fehlgeschlagen",
|
|
111
|
+
"hint": "Aktiviert: Alle Tools (bash-Befehle und workdir sowie Pfadargumente von read/write/edit/read_image/glob/grep) verwenden POSIX-Pfade ab der MSYS-Laufwerkswurzel (/c/Users/...); der Host übersetzt die Pfadfelder der Dateitools automatisch. Deaktiviert stellt das dsh-native Verhalten wieder her (Windows-Pfade für Dateitools). Bash ist bei installiertem Plugin immer Git Bash; dieser Schalter steuert nur den Pfaddialekt zwischen den Tools.",
|
|
112
|
+
},
|
|
113
|
+
/* locale: fr */
|
|
114
|
+
"fr": {
|
|
115
|
+
"title": "Dialecte de chemins Git Bash",
|
|
116
|
+
"cardDesc": "Une forme de chemin POSIX /c/ unique pour tous les outils (activée par défaut)",
|
|
117
|
+
"state.label": "État actuel",
|
|
118
|
+
"state.on": "Activé",
|
|
119
|
+
"state.off": "Désactivé",
|
|
120
|
+
"switch.on": "Activer",
|
|
121
|
+
"switch.off": "Désactiver",
|
|
122
|
+
"error": "échec de l'écriture",
|
|
123
|
+
"hint": "Activé : tous les outils (commandes bash et workdir, arguments de chemin de read/write/edit/read_image/glob/grep) utilisent des chemins POSIX à racine de lecteur MSYS (/c/Users/...) ; l'hôte traduit automatiquement les champs de chemin des outils de fichiers. Désactivé rétablit le comportement natif de dsh (chemins Windows pour les outils de fichiers). Bash reste toujours Git Bash tant que le plugin est installé ; ce commutateur ne régit que le dialecte de chemins entre outils.",
|
|
124
|
+
},
|
|
125
|
+
/* locale: hi */
|
|
126
|
+
"hi": {
|
|
127
|
+
"title": "Git Bash पथ शैली",
|
|
128
|
+
"cardDesc": "सभी टूल के लिए एक ही /c/ POSIX पथ रूप (डिफ़ॉल्ट रूप से सक्षम)",
|
|
129
|
+
"state.label": "वर्तमान स्थिति",
|
|
130
|
+
"state.on": "सक्षम",
|
|
131
|
+
"state.off": "अक्षम",
|
|
132
|
+
"switch.on": "सक्षम करें",
|
|
133
|
+
"switch.off": "अक्षम करें",
|
|
134
|
+
"error": "लिखना विफल रहा",
|
|
135
|
+
"hint": "सक्षम होने पर: सभी टूल (bash कमांड और workdir, तथा read/write/edit/read_image/glob/grep के पथ तर्क) MSYS ड्राइव-रूट POSIX पथ (/c/Users/...) का उपयोग करते हैं; फ़ाइल टूल के पथ फ़ील्ड होस्ट स्वतः अनुवादित करता है। अक्षम करने पर dsh का मूल व्यवहार बहाल होता है (फ़ाइल टूल के लिए Windows पथ)। जब तक प्लगइन इंस्टॉल है bash हमेशा Git Bash रहता है; यह स्विच केवल टूल के बीच पथ शैली नियंत्रित करता है।",
|
|
136
|
+
},
|
|
137
|
+
/* locale: id */
|
|
138
|
+
"id": {
|
|
139
|
+
"title": "Dialek path Git Bash",
|
|
140
|
+
"cardDesc": "Satu bentuk path POSIX /c/ untuk semua alat (aktif secara bawaan)",
|
|
141
|
+
"state.label": "Status saat ini",
|
|
142
|
+
"state.on": "Aktif",
|
|
143
|
+
"state.off": "Nonaktif",
|
|
144
|
+
"switch.on": "Aktifkan",
|
|
145
|
+
"switch.off": "Nonaktifkan",
|
|
146
|
+
"error": "gagal menulis",
|
|
147
|
+
"hint": "Saat aktif: semua alat (perintah bash dan workdir, serta argumen path read/write/edit/read_image/glob/grep) memakai path POSIX berakar drive MSYS (/c/Users/...); host menerjemahkan bidang path untuk alat berkas secara otomatis. Saat nonaktif, perilaku asli dsh dipulihkan (path Windows untuk alat berkas). Bash selalu Git Bash selama plugin terpasang; sakelar ini hanya mengatur dialek path antaralat.",
|
|
148
|
+
},
|
|
149
|
+
/* locale: it */
|
|
150
|
+
"it": {
|
|
151
|
+
"title": "Dialetto dei percorsi Git Bash",
|
|
152
|
+
"cardDesc": "Un'unica forma di percorso POSIX /c/ per tutti gli strumenti (attivata per impostazione predefinita)",
|
|
153
|
+
"state.label": "Stato attuale",
|
|
154
|
+
"state.on": "Attivato",
|
|
155
|
+
"state.off": "Disattivato",
|
|
156
|
+
"switch.on": "Attiva",
|
|
157
|
+
"switch.off": "Disattiva",
|
|
158
|
+
"error": "scrittura non riuscita",
|
|
159
|
+
"hint": "Attivato: tutti gli strumenti (comandi bash e workdir, argomenti di percorso di read/write/edit/read_image/glob/grep) usano percorsi POSIX con radice di unità MSYS (/c/Users/...); l'host traduce automaticamente i campi di percorso degli strumenti per i file. Disattivato ripristina il comportamento nativo di dsh (percorsi Windows per gli strumenti per i file). Bash resta sempre Git Bash finché il plugin è installato; questo interruttore governa solo il dialetto dei percorsi tra gli strumenti.",
|
|
160
|
+
},
|
|
161
|
+
/* locale: ja */
|
|
162
|
+
"ja": {
|
|
163
|
+
"title": "Git Bash パス方言",
|
|
164
|
+
"cardDesc": "すべてのツールで /c/ POSIX パス形式に統一(既定ではオン)",
|
|
165
|
+
"state.label": "現在の状態",
|
|
166
|
+
"state.on": "有効",
|
|
167
|
+
"state.off": "無効",
|
|
168
|
+
"switch.on": "有効にする",
|
|
169
|
+
"switch.off": "無効にする",
|
|
170
|
+
"error": "書き込みに失敗しました",
|
|
171
|
+
"hint": "有効にすると、すべてのツール(bash コマンドと workdir、read/write/edit/read_image/glob/grep のパス引数)が MSYS ドライブルートの POSIX パス(/c/Users/...)を使います。ファイルツールのパスフィールドはホストが自動的に変換します。無効にすると dsh 本来の動作(ファイルツールは Windows パス)に戻ります。bash はプラグインがインストールされている限り常に Git Bash で、このスイッチの影響を受けません。",
|
|
172
|
+
},
|
|
173
|
+
/* locale: ko */
|
|
174
|
+
"ko": {
|
|
175
|
+
"title": "Git Bash 경로 방언",
|
|
176
|
+
"cardDesc": "모든 도구에 하나의 /c/ POSIX 경로 형식 사용(기본값 켜짐)",
|
|
177
|
+
"state.label": "현재 상태",
|
|
178
|
+
"state.on": "사용",
|
|
179
|
+
"state.off": "사용 안 함",
|
|
180
|
+
"switch.on": "사용",
|
|
181
|
+
"switch.off": "사용 안 함",
|
|
182
|
+
"error": "쓰기 실패",
|
|
183
|
+
"hint": "켜면 모든 도구(bash 명령과 workdir, read/write/edit/read_image/glob/grep의 경로 인수)가 MSYS 드라이브 루트 POSIX 경로(/c/Users/...)를 사용합니다. 파일 도구의 경로 필드는 호스트가 자동으로 변환합니다. 끄면 dsh 기본 동작(파일 도구에 Windows 경로 사용)으로 돌아갑니다. 플러그인이 설치되어 있는 동안 bash는 항상 Git Bash이며 이 스위치의 영향을 받지 않습니다.",
|
|
184
|
+
},
|
|
185
|
+
/* locale: nl */
|
|
186
|
+
"nl": {
|
|
187
|
+
"title": "Git Bash-paddialect",
|
|
188
|
+
"cardDesc": "Eén /c/ POSIX-padvorm voor alle tools (standaard aan)",
|
|
189
|
+
"state.label": "Huidige status",
|
|
190
|
+
"state.on": "Ingeschakeld",
|
|
191
|
+
"state.off": "Uitgeschakeld",
|
|
192
|
+
"switch.on": "Inschakelen",
|
|
193
|
+
"switch.off": "Uitschakelen",
|
|
194
|
+
"error": "schrijven mislukt",
|
|
195
|
+
"hint": "Ingeschakeld: alle tools (bash-opdrachten en workdir, padargumenten van read/write/edit/read_image/glob/grep) gebruiken POSIX-paden vanaf de MSYS-schijfwortel (/c/Users/...); de host vertaalt de padvelden voor de bestandstools automatisch. Uitgeschakeld herstelt het oorspronkelijke dsh-gedrag (Windows-paden voor bestandstools). Bash is altijd Git Bash zolang de plugin is geïnstalleerd; deze schakelaar bepaalt alleen het paddialect tussen de tools.",
|
|
196
|
+
},
|
|
197
|
+
/* locale: pl */
|
|
198
|
+
"pl": {
|
|
199
|
+
"title": "Dialekt ścieżek Git Bash",
|
|
200
|
+
"cardDesc": "Jedna postać ścieżki POSIX /c/ dla wszystkich narzędzi (domyślnie włączone)",
|
|
201
|
+
"state.label": "Bieżący stan",
|
|
202
|
+
"state.on": "Włączone",
|
|
203
|
+
"state.off": "Wyłączone",
|
|
204
|
+
"switch.on": "Włącz",
|
|
205
|
+
"switch.off": "Wyłącz",
|
|
206
|
+
"error": "zapis nie powiódł się",
|
|
207
|
+
"hint": "Po włączeniu: wszystkie narzędzia (polecenia bash i workdir oraz argumenty ścieżek w read/write/edit/read_image/glob/grep) używają ścieżek POSIX od korzenia dysku MSYS (/c/Users/...); host automatycznie tłumaczy pola ścieżek narzędzi plikowych. Wyłączenie przywraca natywne zachowanie dsh (ścieżki Windows dla narzędzi plikowych). Bash jest zawsze Git Bash, dopóki wtyczka jest zainstalowana; ten przełącznik reguluje wyłącznie dialekt ścieżek między narzędziami.",
|
|
208
|
+
},
|
|
209
|
+
/* locale: pt */
|
|
210
|
+
"pt": {
|
|
211
|
+
"title": "Dialeto de caminhos do Git Bash",
|
|
212
|
+
"cardDesc": "Uma única forma de caminho POSIX /c/ para todas as ferramentas (ativada por padrão)",
|
|
213
|
+
"state.label": "Estado atual",
|
|
214
|
+
"state.on": "Ativada",
|
|
215
|
+
"state.off": "Desativada",
|
|
216
|
+
"switch.on": "Ativar",
|
|
217
|
+
"switch.off": "Desativar",
|
|
218
|
+
"error": "falha ao gravar",
|
|
219
|
+
"hint": "Ativada: todas as ferramentas (comandos bash e workdir, argumentos de caminho de read/write/edit/read_image/glob/grep) usam caminhos POSIX a partir da raiz da unidade MSYS (/c/Users/...); o host traduz automaticamente os campos de caminho das ferramentas de arquivo. Desativada restaura o comportamento nativo do dsh (caminhos Windows para as ferramentas de arquivo). O bash é sempre Git Bash enquanto o plugin estiver instalado; este interruptor rege apenas o dialeto de caminhos entre ferramentas.",
|
|
220
|
+
},
|
|
221
|
+
/* locale: ru */
|
|
222
|
+
"ru": {
|
|
223
|
+
"title": "Диалект путей Git Bash",
|
|
224
|
+
"cardDesc": "Единый POSIX-формат путей /c/ для всех инструментов (по умолчанию включено)",
|
|
225
|
+
"state.label": "Текущее состояние",
|
|
226
|
+
"state.on": "Включено",
|
|
227
|
+
"state.off": "Выключено",
|
|
228
|
+
"switch.on": "Включить",
|
|
229
|
+
"switch.off": "Выключить",
|
|
230
|
+
"error": "не удалось записать",
|
|
231
|
+
"hint": "Включено: все инструменты (команды bash и workdir, аргументы путей read/write/edit/read_image/glob/grep) используют POSIX-пути от корня диска MSYS (/c/Users/...); хост автоматически преобразует поля путей для файловых инструментов. Выключение возвращает исходное поведение dsh (пути Windows для файловых инструментов). Пока плагин установлен, bash всегда остаётся Git Bash; этот переключатель управляет только диалектом путей между инструментами.",
|
|
232
|
+
},
|
|
233
|
+
/* locale: sv */
|
|
234
|
+
"sv": {
|
|
235
|
+
"title": "Git Bash-sökvägsdialekt",
|
|
236
|
+
"cardDesc": "En /c/ POSIX-sökvägsform för alla verktyg (på som standard)",
|
|
237
|
+
"state.label": "Aktuell status",
|
|
238
|
+
"state.on": "Aktiverad",
|
|
239
|
+
"state.off": "Inaktiverad",
|
|
240
|
+
"switch.on": "Aktivera",
|
|
241
|
+
"switch.off": "Inaktivera",
|
|
242
|
+
"error": "skrivning misslyckades",
|
|
243
|
+
"hint": "Aktiverad: alla verktyg (bash-kommandon och workdir, sökvägsargument för read/write/edit/read_image/glob/grep) använder POSIX-sökvägar från MSYS-enhetsroten (/c/Users/...); värden översätter sökvägsfälten för filverktygen automatiskt. Inaktiverad återställer dsh:s ursprungliga beteende (Windows-sökvägar för filverktyg). Bash är alltid Git Bash så länge insticksprogrammet är installerat; den här växeln styr bara sökvägsdialekten mellan verktygen.",
|
|
244
|
+
},
|
|
245
|
+
/* locale: th */
|
|
246
|
+
"th": {
|
|
247
|
+
"title": "รูปแบบพาธของ Git Bash",
|
|
248
|
+
"cardDesc": "ใช้รูปแบบพาธ POSIX /c/ แบบเดียวกันกับทุกเครื่องมือ (เปิดไว้เป็นค่าเริ่มต้น)",
|
|
249
|
+
"state.label": "สถานะปัจจุบัน",
|
|
250
|
+
"state.on": "เปิดใช้งาน",
|
|
251
|
+
"state.off": "ปิดใช้งาน",
|
|
252
|
+
"switch.on": "เปิดใช้งาน",
|
|
253
|
+
"switch.off": "ปิดใช้งาน",
|
|
254
|
+
"error": "เขียนไม่สำเร็จ",
|
|
255
|
+
"hint": "เมื่อเปิดใช้งาน: เครื่องมือทั้งหมด (คำสั่ง bash และ workdir รวมถึงอาร์กิวเมนต์พาธของ read/write/edit/read_image/glob/grep) ใช้พาธ POSIX จากรากไดรฟ์ของ MSYS (/c/Users/...) โฮสต์จะแปลฟิลด์พาธของเครื่องมือจัดการไฟล์ให้อัตโนมัติ เมื่อปิดใช้งานจะกลับสู่พฤติกรรมดั้งเดิมของ dsh (เครื่องมือจัดการไฟล์ใช้พาธ Windows) bash เป็น Git Bash เสมอเมื่อติดตั้งปลั๊กอินนี้ สวิตช์นี้ควบคุมเฉพาะรูปแบบพาธระหว่างเครื่องมือเท่านั้น",
|
|
256
|
+
},
|
|
257
|
+
/* locale: tr */
|
|
258
|
+
"tr": {
|
|
259
|
+
"title": "Git Bash yol lehçesi",
|
|
260
|
+
"cardDesc": "Tüm araçlar için tek /c/ POSIX yol biçimi (varsayılan olarak açık)",
|
|
261
|
+
"state.label": "Geçerli durum",
|
|
262
|
+
"state.on": "Etkin",
|
|
263
|
+
"state.off": "Devre dışı",
|
|
264
|
+
"switch.on": "Etkinleştir",
|
|
265
|
+
"switch.off": "Devre dışı bırak",
|
|
266
|
+
"error": "yazma başarısız",
|
|
267
|
+
"hint": "Etkinleştirildiğinde: tüm araçlar (bash komutları ve workdir ile read/write/edit/read_image/glob/grep yol bağımsız değişkenleri) MSYS sürücü kökünden POSIX yolları (/c/Users/...) kullanır; ana süreç dosya araçlarının yol alanlarını otomatik olarak çevirir. Devre dışı bırakıldığında dsh yerel davranışına döner (dosya araçları için Windows yolları). Eklenti kurulu olduğu sürece bash her zaman Git Bash olarak kalır; bu anahtar yalnızca araçlar arası yol lehçesini yönetir.",
|
|
268
|
+
},
|
|
269
|
+
/* locale: vi */
|
|
270
|
+
"vi": {
|
|
271
|
+
"title": "Phương ngữ đường dẫn Git Bash",
|
|
272
|
+
"cardDesc": "Một dạng đường dẫn POSIX /c/ thống nhất cho mọi công cụ (mặc định bật)",
|
|
273
|
+
"state.label": "Trạng thái hiện tại",
|
|
274
|
+
"state.on": "Đã bật",
|
|
275
|
+
"state.off": "Đã tắt",
|
|
276
|
+
"switch.on": "Bật",
|
|
277
|
+
"switch.off": "Tắt",
|
|
278
|
+
"error": "ghi thất bại",
|
|
279
|
+
"hint": "Khi bật: mọi công cụ (lệnh bash và workdir, tham số đường dẫn của read/write/edit/read_image/glob/grep) dùng đường dẫn POSIX từ gốc ổ đĩa MSYS (/c/Users/...); máy chủ tự động chuyển đổi trường đường dẫn cho các công cụ tệp. Khi tắt, hành vi gốc của dsh được khôi phục (công cụ tệp dùng đường dẫn Windows). bash luôn là Git Bash khi plugin được cài đặt; công tắc này chỉ điều khiển phương ngữ đường dẫn giữa các công cụ.",
|
|
280
|
+
},
|
|
281
|
+
/* locale: zh-hk */
|
|
282
|
+
"zh-hk": {
|
|
283
|
+
"title": "Git Bash 路徑方言",
|
|
284
|
+
"cardDesc": "所有工具統一用 /c/ POSIX 路徑格式(預設開啟)",
|
|
285
|
+
"state.label": "目前狀態",
|
|
286
|
+
"state.on": "已啟用",
|
|
287
|
+
"state.off": "已關閉",
|
|
288
|
+
"switch.on": "啟用",
|
|
289
|
+
"switch.off": "停用",
|
|
290
|
+
"error": "寫入失敗",
|
|
291
|
+
"hint": "啟用後:所有工具(bash 指令同 workdir、read/write/edit/read_image/glob/grep 嘅路徑參數)統一使用 MSYS 磁碟根 POSIX 路徑(/c/Users/...);檔案工具嘅路徑參數由宿主自動翻譯,模型無感。停用後回復 dsh 原生行為(檔案工具用 Windows 路徑)。bash 永遠係 Git Bash,唔受呢個開關影響。",
|
|
292
|
+
},
|
|
293
|
+
/* locale: zh-mo */
|
|
294
|
+
"zh-mo": {
|
|
295
|
+
"title": "Git Bash 路徑方言",
|
|
296
|
+
"cardDesc": "所有工具統一用 /c/ POSIX 路徑格式(預設開啟)",
|
|
297
|
+
"state.label": "目前狀態",
|
|
298
|
+
"state.on": "已啟用",
|
|
299
|
+
"state.off": "已關閉",
|
|
300
|
+
"switch.on": "啟用",
|
|
301
|
+
"switch.off": "停用",
|
|
302
|
+
"error": "寫入失敗",
|
|
303
|
+
"hint": "啟用後:所有工具(bash 指令同 workdir、read/write/edit/read_image/glob/grep 嘅路徑參數)統一使用 MSYS 磁碟根 POSIX 路徑(/c/Users/...);檔案工具嘅路徑參數由宿主自動翻譯,模型無感。停用後回復 dsh 原生行為(檔案工具用 Windows 路徑)。bash 永遠係 Git Bash,唔受呢個開關影響。",
|
|
304
|
+
},
|
|
305
|
+
/* locale: zh-tw */
|
|
306
|
+
"zh-tw": {
|
|
307
|
+
"title": "Git Bash 路徑方言",
|
|
308
|
+
"cardDesc": "所有工具統一採用 /c/ POSIX 路徑格式(預設開啟)",
|
|
309
|
+
"state.label": "目前狀態",
|
|
310
|
+
"state.on": "已啟用",
|
|
311
|
+
"state.off": "已關閉",
|
|
312
|
+
"switch.on": "啟用",
|
|
313
|
+
"switch.off": "停用",
|
|
314
|
+
"error": "寫入失敗",
|
|
315
|
+
"hint": "啟用後:所有工具(bash 指令與 workdir,以及 read/write/edit/read_image/glob/grep 的路徑參數)統一使用 MSYS 磁碟根 POSIX 路徑(/c/Users/...);檔案工具的路徑參數由宿主自動轉譯,模型無感。停用後恢復 dsh 原生行為(檔案工具使用 Windows 路徑)。bash 一律是 Git Bash,不受此開關影響。",
|
|
316
|
+
},
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
// ── locale resolution (live: read per lookup, cached per tag) ───────────
|
|
320
|
+
|
|
321
|
+
function hasOwnKey(bag, key) {
|
|
322
|
+
return Object.prototype.hasOwnProperty.call(bag, key);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/* Resolve one locale tag against what this plugin ships: the exact tag,
|
|
326
|
+
then the primary subtag, then English. Chinese is special-cased because
|
|
327
|
+
the traditional variants are shipped while a bare zh-Hant-* is not
|
|
328
|
+
spelled out. */
|
|
329
|
+
function dictionaryFor(active) {
|
|
330
|
+
var raw = active === undefined || active === null || active === "" ? "en" : String(active);
|
|
331
|
+
var tag = raw.toLowerCase().replace(/_/g, "-");
|
|
332
|
+
var primary = tag.split("-")[0];
|
|
333
|
+
if (primary === "zh") {
|
|
334
|
+
if (hasOwnKey(LOCALES, tag)) return LOCALES[tag];
|
|
335
|
+
if (tag.indexOf("hant") >= 0 || tag === "zh-hk" || tag === "zh-mo" || tag === "zh-tw") {
|
|
336
|
+
return hasOwnKey(LOCALES, "zh-hk") ? LOCALES["zh-hk"] : zh;
|
|
337
|
+
}
|
|
338
|
+
return zh;
|
|
339
|
+
}
|
|
340
|
+
if (hasOwnKey(LOCALES, primary)) return LOCALES[primary];
|
|
341
|
+
return en;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/* The active locale tag, read fresh every time: DSH's language preference
|
|
345
|
+
switches live, so a value captured once at activation would keep answering
|
|
346
|
+
in the language that happened to be active when the card loaded. The
|
|
347
|
+
host-backed preference wins; the browser is the fallback. */
|
|
348
|
+
function activeLocaleOf(ctx) {
|
|
349
|
+
var active = "";
|
|
350
|
+
try {
|
|
351
|
+
var locale = ctx.get("locale");
|
|
352
|
+
if (locale !== undefined && typeof locale.getSnapshot === "function") {
|
|
353
|
+
var snapshot = locale.getSnapshot();
|
|
354
|
+
if (snapshot !== null && typeof snapshot === "object" && typeof snapshot.active === "string") active = snapshot.active;
|
|
355
|
+
}
|
|
356
|
+
} catch (error) { /* fall through to the browser */ }
|
|
357
|
+
if (active === "" && typeof navigator === "object" && navigator !== null && typeof navigator.language === "string") active = navigator.language;
|
|
358
|
+
return active;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/* A live lookup: the dictionary is picked per call and cached under the tag
|
|
362
|
+
it was picked for, so a language switch costs one string compare per
|
|
363
|
+
string. Falling back to EN — kept key-complete on purpose — means an
|
|
364
|
+
unsupported language shows English, never a raw key. */
|
|
365
|
+
function translatorOf(ctx) {
|
|
366
|
+
var cachedTag = null;
|
|
367
|
+
var cachedDict = null;
|
|
368
|
+
return function (key) {
|
|
369
|
+
var tag = activeLocaleOf(ctx);
|
|
370
|
+
if (tag !== cachedTag) { cachedTag = tag; cachedDict = dictionaryFor(tag); }
|
|
371
|
+
if (hasOwnKey(cachedDict, key)) return cachedDict[key];
|
|
372
|
+
return hasOwnKey(en, key) ? en[key] : key;
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
|
|
71
376
|
// ── styles (gb- prefixed; tokens mirror PluginCard.module.css) ─────────
|
|
72
377
|
|
|
73
378
|
var STYLE_ID = "dsh-gitbash-shell-style";
|
|
@@ -140,13 +445,40 @@ window.__ModuleLoader__.load({
|
|
|
140
445
|
return this.props.children;
|
|
141
446
|
};
|
|
142
447
|
|
|
448
|
+
// ── live locale (repaint the card on a language switch) ─────────────────
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Wraps the card so a language switch repaints it instead of waiting for a
|
|
452
|
+
* page load. DSH's own components re-render on the locale snapshot, but this
|
|
453
|
+
* card belongs to a plugin: it subscribes for itself, and it hands the card
|
|
454
|
+
* THIS plugin's live lookup (translatorOf) — whose resolution covers the
|
|
455
|
+
* third languages the DSH catalog does not carry. The card stays inside the
|
|
456
|
+
* same quiet boundary as before.
|
|
457
|
+
*/
|
|
458
|
+
function LocaleLive(props) {
|
|
459
|
+
var tickState = useState(0);
|
|
460
|
+
var tick = tickState[0];
|
|
461
|
+
var setTick = tickState[1];
|
|
462
|
+
void tick;
|
|
463
|
+
|
|
464
|
+
useEffect(function () {
|
|
465
|
+
var locale = props.ctx && typeof props.ctx.get === "function" ? props.ctx.get("locale") : undefined;
|
|
466
|
+
if (locale === undefined || typeof locale.subscribe !== "function") return undefined;
|
|
467
|
+
var unsubscribe = locale.subscribe(function () { setTick(function (value) { return value + 1; }); });
|
|
468
|
+
return typeof unsubscribe === "function" ? unsubscribe : undefined;
|
|
469
|
+
}, []);
|
|
470
|
+
|
|
471
|
+
return E(QuietBoundary, null, E(GitBashCard, Object.assign({}, props.cardProps, { t: props.t })));
|
|
472
|
+
}
|
|
473
|
+
|
|
143
474
|
// ── settings card (module-level component) ───────────────────────────────
|
|
144
475
|
|
|
145
476
|
/**
|
|
146
|
-
* The Settings → Plugins card. t arrives
|
|
147
|
-
* locale
|
|
148
|
-
* Snapshots are read per render; each
|
|
149
|
-
* card re-reads without an external-store
|
|
477
|
+
* The Settings → Plugins card. t arrives from LocaleLive (the plugin's own
|
|
478
|
+
* live lookup, keyed on the active locale at call time); scope arrives as a
|
|
479
|
+
* PLAIN prop from the inject factory. Snapshots are read per render; each
|
|
480
|
+
* write bumps a local tick so the card re-reads without an external-store
|
|
481
|
+
* hook adapter.
|
|
150
482
|
*/
|
|
151
483
|
function GitBashCard(props) {
|
|
152
484
|
var t = typeof props.t === "function" ? props.t : function (key) { return key; };
|
|
@@ -235,11 +567,17 @@ window.__ModuleLoader__.load({
|
|
|
235
567
|
|
|
236
568
|
exports.apply = function (ctx) {
|
|
237
569
|
var scope = ctx.settingsScope.bind({ namespace: SETTINGS_NAMESPACE });
|
|
570
|
+
/* The card's only copy entry point: a live lookup, never a captured
|
|
571
|
+
dictionary — the language preference switches without a reload. */
|
|
572
|
+
var t = translatorOf(ctx);
|
|
238
573
|
|
|
239
574
|
ctx.effect(function () {
|
|
240
575
|
var disposers = [ensureStyles()];
|
|
241
576
|
try {
|
|
242
|
-
|
|
577
|
+
/* Every shipped dictionary rides the DSH locale registry, so host-side
|
|
578
|
+
consumers read the same copy the card does; a missing service simply
|
|
579
|
+
means the card is the only reader. */
|
|
580
|
+
var disposeDict = ctx.locale.register(NS, Object.assign({ zh: zh, en: en }, LOCALES));
|
|
243
581
|
if (typeof disposeDict === "function") disposers.push(disposeDict);
|
|
244
582
|
} catch (error) {
|
|
245
583
|
console.warn(TAG + " dictionary registration failed:", error && error.message ? error.message : error);
|
|
@@ -270,8 +608,11 @@ window.__ModuleLoader__.load({
|
|
|
270
608
|
// props: the bound settings scope rides here as a PLAIN member
|
|
271
609
|
// (top-level options fields do NOT reach the component).
|
|
272
610
|
inject: function () { return { scope: scope }; },
|
|
273
|
-
}, function
|
|
274
|
-
|
|
611
|
+
}, function CardWithLocale(props) {
|
|
612
|
+
/* The registration's locale field also hands out the framework's own
|
|
613
|
+
t seat; the plugin's lookup wins (21 locale tags against the catalog's
|
|
614
|
+
zh/en), passed last so no prop merge order can override it. */
|
|
615
|
+
return E(LocaleLive, { ctx: ctx, t: t, cardProps: props });
|
|
275
616
|
});
|
|
276
617
|
});
|
|
277
618
|
} catch (error) {
|
package/src/index.js
CHANGED
|
@@ -231,6 +231,183 @@ async function detectPresentSupport(agentPresets) {
|
|
|
231
231
|
}
|
|
232
232
|
}
|
|
233
233
|
|
|
234
|
+
/** Row ids the shipped workflow-engine row has answered to (dsh 0.1.6 renamed it). */
|
|
235
|
+
const ENGINE_ROW_IDS = ['workflow-worker-thread', 'workflow-ptc']
|
|
236
|
+
|
|
237
|
+
/** Which built-in preset each of this plugin's variants mirrors, for row-form lookup. */
|
|
238
|
+
export const ROW_SOURCE = {
|
|
239
|
+
'standard-gitbash': 'standard',
|
|
240
|
+
'cordis-gitbash': 'cordis',
|
|
241
|
+
'code-gitbash': 'ptc',
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/** Leading whitespace of one line (pure). */
|
|
245
|
+
function indentOf(line) {
|
|
246
|
+
return line.slice(0, line.length - line.trimStart().length)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/** The unquoted value of a name line, or undefined when the line is not one (pure). */
|
|
250
|
+
function quotedName(line) {
|
|
251
|
+
const trimmed = line.trim()
|
|
252
|
+
if (!trimmed.startsWith('name: ')) return undefined
|
|
253
|
+
const raw = trimmed.slice(6).trim()
|
|
254
|
+
if (raw.length > 1 && raw.charAt(0) === "'" && raw.charAt(raw.length - 1) === "'") return raw.slice(1, -1)
|
|
255
|
+
return raw
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Read ONE row's spelling out of a composition text (pure): the name value
|
|
260
|
+
* that follows its id line and whether the row's own block turns it off before
|
|
261
|
+
* the next row starts. Returns undefined when the row is absent, and never
|
|
262
|
+
* throws, so a composition that legitimately lacks the row (minimal) is simply
|
|
263
|
+
* left alone.
|
|
264
|
+
* @param text - composition text.
|
|
265
|
+
* @param rowId - exact id value to locate.
|
|
266
|
+
* @returns the row's spelling, or undefined.
|
|
267
|
+
*/
|
|
268
|
+
export function rowFormOf(text, rowId) {
|
|
269
|
+
const lines = text.split('\n')
|
|
270
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
271
|
+
if (lines[i].trim() !== '- id: ' + rowId) continue
|
|
272
|
+
const nameLine = i + 1 < lines.length ? lines[i + 1] : ''
|
|
273
|
+
const name = quotedName(nameLine)
|
|
274
|
+
if (name === undefined) continue
|
|
275
|
+
let disabled = false
|
|
276
|
+
let disabledIndent = indentOf(nameLine)
|
|
277
|
+
for (let j = i + 2; j < lines.length; j += 1) {
|
|
278
|
+
if (lines[j].trim().startsWith('- id: ')) break
|
|
279
|
+
if (lines[j].trim() === 'disabled: true') {
|
|
280
|
+
disabled = true
|
|
281
|
+
disabledIndent = indentOf(lines[j])
|
|
282
|
+
break
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return { id: rowId, name, indent: indentOf(lines[i]), nameIndent: indentOf(nameLine), disabled, disabledIndent }
|
|
286
|
+
}
|
|
287
|
+
return undefined
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Read the two rows this plugin's committed compositions must track out of a
|
|
292
|
+
* SHIPPED composition text (pure). The built-in preset is the authority: it
|
|
293
|
+
* mounts on this host by construction, so whatever it spells is mountable
|
|
294
|
+
* here, on every era, without shipping one asset variant per rename.
|
|
295
|
+
* @param text - a shipped agent.cordis.yml.
|
|
296
|
+
* @returns an object with the engine row and the tool-ralph row; either may be undefined.
|
|
297
|
+
*/
|
|
298
|
+
export function rowFormsOf(text) {
|
|
299
|
+
let engine
|
|
300
|
+
for (const id of ENGINE_ROW_IDS) {
|
|
301
|
+
engine = rowFormOf(text, id)
|
|
302
|
+
if (engine !== undefined) break
|
|
303
|
+
}
|
|
304
|
+
return { engine, ralph: rowFormOf(text, 'tool-ralph') }
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Align the workflow-engine row with the host's own spelling (pure,
|
|
309
|
+
* idempotent, plain string surgery — never a YAML round-trip, so !!js literals
|
|
310
|
+
* survive).
|
|
311
|
+
*
|
|
312
|
+
* dsh 0.1.6-alpha.1 renamed this row workflow-worker-thread to workflow-ptc
|
|
313
|
+
* AND deleted the old package. A composition row whose module fails to import
|
|
314
|
+
* rejects the WHOLE preset mount (agent-presets mount.ts), so a committed old
|
|
315
|
+
* spelling breaks every Git Bash preset on the new host while the new spelling
|
|
316
|
+
* breaks them on hosts that still ship only the old package. The materializer
|
|
317
|
+
* therefore copies the row out of the host's own built-in preset instead of
|
|
318
|
+
* pinning either spelling.
|
|
319
|
+
* @param text - composition text.
|
|
320
|
+
* @param form - the host's engine row from rowFormsOf, or undefined to leave the text alone.
|
|
321
|
+
* @param options - engineEnabled forces the row on regardless of the host's own state.
|
|
322
|
+
* @returns the aligned text.
|
|
323
|
+
*/
|
|
324
|
+
export function alignEngineRow(text, form, options) {
|
|
325
|
+
if (!form) return text
|
|
326
|
+
const engineEnabled = Boolean(options && options.engineEnabled)
|
|
327
|
+
let current
|
|
328
|
+
for (const id of ENGINE_ROW_IDS) {
|
|
329
|
+
current = rowFormOf(text, id)
|
|
330
|
+
if (current !== undefined) break
|
|
331
|
+
}
|
|
332
|
+
if (current === undefined) return text
|
|
333
|
+
const head = '- id: ' + current.id + '\n' + current.nameIndent + "name: '" + current.name + "'"
|
|
334
|
+
const wanted = '- id: ' + form.id + '\n' + current.nameIndent + "name: '" + form.name + "'"
|
|
335
|
+
const wantOff = engineEnabled ? false : form.disabled
|
|
336
|
+
let out = text.replace(head, wanted)
|
|
337
|
+
if (wantOff && !current.disabled) {
|
|
338
|
+
out = out.replace(wanted + '\n', wanted + '\n' + current.nameIndent + 'disabled: true\n')
|
|
339
|
+
} else if (!wantOff && current.disabled) {
|
|
340
|
+
out = out.replace(wanted + '\n' + current.disabledIndent + 'disabled: true\n', wanted + '\n')
|
|
341
|
+
}
|
|
342
|
+
return out
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Align the tool-ralph row with the host's own default (pure, idempotent).
|
|
347
|
+
* dsh 0.1.6-alpha.1 ships ralph disabled in every built-in preset: its tool
|
|
348
|
+
* description restricts it to runs the human explicitly asked for. The row
|
|
349
|
+
* still imports on older hosts, so this is a semantics alignment rather than a
|
|
350
|
+
* mount fix — but a composition claiming to mirror the built-in preset should
|
|
351
|
+
* not silently re-enable a tool the deployment turned off.
|
|
352
|
+
* @param text - composition text.
|
|
353
|
+
* @param form - the host's tool-ralph row from rowFormsOf, or undefined.
|
|
354
|
+
* @returns the aligned text.
|
|
355
|
+
*/
|
|
356
|
+
export function alignRalphRow(text, form) {
|
|
357
|
+
if (!form) return text
|
|
358
|
+
const current = rowFormOf(text, 'tool-ralph')
|
|
359
|
+
if (current === undefined) return text
|
|
360
|
+
const head = '- id: tool-ralph\n' + current.nameIndent + "name: '" + current.name + "'"
|
|
361
|
+
if (form.disabled && !current.disabled) {
|
|
362
|
+
return text.replace(head + '\n', head + '\n' + current.nameIndent + 'disabled: true\n')
|
|
363
|
+
}
|
|
364
|
+
if (!form.disabled && current.disabled) {
|
|
365
|
+
return text.replace(head + '\n' + current.disabledIndent + 'disabled: true\n', head + '\n')
|
|
366
|
+
}
|
|
367
|
+
return text
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Probe every built-in preset this plugin's variants mirror for those row
|
|
372
|
+
* spellings (never throws). The live roster is the authority — package
|
|
373
|
+
* resolution alone would lie on linked development trees and on CLI installs
|
|
374
|
+
* whose first-party packages live outside the profile.
|
|
375
|
+
* @param agentPresets - the roster service.
|
|
376
|
+
* @returns a map of row forms per available built-in id, or undefined.
|
|
377
|
+
*/
|
|
378
|
+
async function detectRowForms(agentPresets) {
|
|
379
|
+
try {
|
|
380
|
+
const list = await agentPresets.list()
|
|
381
|
+
const entries = Array.isArray(list) ? list : []
|
|
382
|
+
const out = {}
|
|
383
|
+
for (const id of ['ptc', 'standard', 'cordis']) {
|
|
384
|
+
const entry = entries.find((p) => p && p.id === id && typeof p.path === 'string')
|
|
385
|
+
if (!entry) continue
|
|
386
|
+
const file = /\.yml$/.test(entry.path) ? entry.path : join(entry.path, 'agent.cordis.yml')
|
|
387
|
+
out[id] = rowFormsOf(readFileSync(file, 'utf8'))
|
|
388
|
+
}
|
|
389
|
+
return Object.keys(out).length > 0 ? out : undefined
|
|
390
|
+
} catch (error) {
|
|
391
|
+
console.log(TAG + ' row-form probe failed (' + (error && error.message ? error.message : error) + ') — keeping the committed spellings')
|
|
392
|
+
return undefined
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Marker fingerprint of the row spellings one variant is aligned to, so a host
|
|
398
|
+
* that renames (or re-defaults) them re-materializes on the next boot.
|
|
399
|
+
* @param rows - detectRowForms result.
|
|
400
|
+
* @param presetId - the variant being materialized.
|
|
401
|
+
* @returns a short stable string, '' when nothing was probed.
|
|
402
|
+
*/
|
|
403
|
+
function rowFingerprint(rows, presetId) {
|
|
404
|
+
const source = ROW_SOURCE[presetId]
|
|
405
|
+
const form = rows && source ? rows[source] : undefined
|
|
406
|
+
if (!form || !form.engine) return ''
|
|
407
|
+
const ralph = form.ralph === undefined ? 'na' : (form.ralph.disabled ? 'off' : 'on')
|
|
408
|
+
return form.engine.id + ':' + (form.engine.disabled ? 'off' : 'on') + ':' + ralph
|
|
409
|
+
}
|
|
410
|
+
|
|
234
411
|
/**
|
|
235
412
|
* Pick the committed composition asset inside one variant directory (pure).
|
|
236
413
|
* The ptc-era twin is preferred when the roster says `ptc`; the persona-split
|
|
@@ -250,11 +427,17 @@ function pickComposition(base, persona, available) {
|
|
|
250
427
|
}
|
|
251
428
|
|
|
252
429
|
/** Startup decision for an existing unmodified tree: 'refresh' or 'idle'. */
|
|
253
|
-
function syncDecision({ state, marker, version, sourceHashes, base = 'code', persona = 'text', present = false }) {
|
|
430
|
+
function syncDecision({ state, marker, version, sourceHashes, base = 'code', persona = 'text', present = false, rows = '' }) {
|
|
254
431
|
if (state !== 'unmodified' || !marker) return 'refresh'
|
|
255
432
|
if (marker.version !== version) return 'refresh'
|
|
256
433
|
if (marker.base !== base) return 'refresh'
|
|
257
434
|
if ((marker.persona ?? 'text') !== persona) return 'refresh'
|
|
435
|
+
// Host row-form flip: dsh 0.1.6-alpha.1 renamed the workflow-engine row and
|
|
436
|
+
// deleted the package behind the old spelling. A composition row that cannot
|
|
437
|
+
// be imported rejects the WHOLE preset mount, so a host reporting a
|
|
438
|
+
// different spelling must re-materialize (same marker-dimension pattern as
|
|
439
|
+
// persona and present).
|
|
440
|
+
if ((marker.rows ?? '') !== rows) return 'refresh'
|
|
258
441
|
// Host-capability flip: @deepseek-ai/dsh-tool-present first shipped with dsh
|
|
259
442
|
// 0.1.5-alpha.2. A preset composed with the row on a host that lacks the
|
|
260
443
|
// package REJECTS THE WHOLE MOUNT (mount.ts: a row whose module failed to
|
|
@@ -315,7 +498,7 @@ async function hostHasToolPresent() {
|
|
|
315
498
|
}
|
|
316
499
|
|
|
317
500
|
/** Write one preset directory from scratch. Returns 'ok' or 'no-skills-source'. */
|
|
318
|
-
function materialize({ target, presetId, skillsSource, version, base = 'code', persona = 'text', present = false }) {
|
|
501
|
+
function materialize({ target, presetId, skillsSource, version, base = 'code', persona = 'text', present = false, rows }) {
|
|
319
502
|
rmSync(target, { recursive: true, force: true })
|
|
320
503
|
mkdirSync(target, { recursive: true })
|
|
321
504
|
|
|
@@ -323,6 +506,17 @@ function materialize({ target, presetId, skillsSource, version, base = 'code', p
|
|
|
323
506
|
const compositionFile = pickComposition(base, persona, available)
|
|
324
507
|
let composition = readFileSync(join(pkgDir, 'assets', presetId, compositionFile), 'utf8')
|
|
325
508
|
if (present && compositionFile.includes('.ptc.')) composition = injectPresentRow(composition)
|
|
509
|
+
// Row-form alignment (v0.14.0): the committed assets pin one spelling of the
|
|
510
|
+
// workflow-engine row, but a host renames it out from under them (dsh
|
|
511
|
+
// 0.1.6-alpha.1) and a row whose module fails to import rejects the whole
|
|
512
|
+
// mount. Copy the host's own spelling rather than shipping a third asset
|
|
513
|
+
// variant per era. Absent probe = leave the frozen history untouched.
|
|
514
|
+
const rowSource = ROW_SOURCE[presetId]
|
|
515
|
+
const rowForm = rows && rowSource ? rows[rowSource] : undefined
|
|
516
|
+
if (rowForm) {
|
|
517
|
+
composition = alignEngineRow(composition, rowForm.engine)
|
|
518
|
+
composition = alignRalphRow(composition, rowForm.ralph)
|
|
519
|
+
}
|
|
326
520
|
writeFileSync(join(target, 'agent.cordis.yml'), composition)
|
|
327
521
|
writeFileSync(join(target, 'preset.yml'), readFileSync(join(pkgDir, 'assets', presetId, 'preset.yml')))
|
|
328
522
|
|
|
@@ -337,7 +531,7 @@ function materialize({ target, presetId, skillsSource, version, base = 'code', p
|
|
|
337
531
|
}
|
|
338
532
|
}
|
|
339
533
|
|
|
340
|
-
const marker = { managedBy: MANAGED_BY, version, presetId, base, persona, present, files: hashTree(target) }
|
|
534
|
+
const marker = { managedBy: MANAGED_BY, version, presetId, base, persona, present, rows: rowFingerprint(rows, presetId), files: hashTree(target) }
|
|
341
535
|
writeFileSync(join(target, MARKER_FILE), JSON.stringify(marker, null, 2) + '\n')
|
|
342
536
|
return skills
|
|
343
537
|
}
|
|
@@ -729,7 +923,7 @@ export async function apply(ctx, config = {}) {
|
|
|
729
923
|
const disposeGitBash = ctx.provide('gitBash', gitBashCapability)
|
|
730
924
|
ctx.effect(() => disposeGitBash, 'dsh-gitbash-shell: gitBash capability')
|
|
731
925
|
|
|
732
|
-
// ── settings namespace: the posixPaths switch (default
|
|
926
|
+
// ── settings namespace: the posixPaths switch (default ON, v0.10.0) ──────
|
|
733
927
|
// Served on the host so the Plugins tab pairs it with the browser card
|
|
734
928
|
// (the tab dispatches Host-served namespaces ∩ registered cards). Dynamic
|
|
735
929
|
// imports keep the zero-dependency smoke path importable; the schema MUST
|
|
@@ -951,6 +1145,10 @@ export async function apply(ctx, config = {}) {
|
|
|
951
1145
|
// resolving from this plugin's own tree (covers hosts where the roster
|
|
952
1146
|
// probe is unavailable).
|
|
953
1147
|
const present = (await detectPresentSupport(ctx.agentPresets)) || (await hostHasToolPresent())
|
|
1148
|
+
// Row spellings of the built-in presets these variants mirror: the
|
|
1149
|
+
// workflow-engine row was renamed and its old package deleted in dsh
|
|
1150
|
+
// 0.1.6-alpha.1, so the materializer copies whatever the host itself ships.
|
|
1151
|
+
const rows = await detectRowForms(ctx.agentPresets)
|
|
954
1152
|
const userRootPath = userRoot.path
|
|
955
1153
|
purgeOrphans(userRootPath, presetIds)
|
|
956
1154
|
|
|
@@ -980,12 +1178,13 @@ export async function apply(ctx, config = {}) {
|
|
|
980
1178
|
|
|
981
1179
|
const sourceHashes = presetId === 'cordis-gitbash' ? skillsHashes(skillsSource) : null
|
|
982
1180
|
const marker = readMarker(target)
|
|
983
|
-
|
|
1181
|
+
const markerRows = rowFingerprint(rows, presetId)
|
|
1182
|
+
if (state === 'unmodified' && syncDecision({ state, marker, version, sourceHashes, base, persona, present, rows: markerRows }) === 'idle') {
|
|
984
1183
|
ctx.logger?.('gitbash-shell')?.debug?.( `preset '${presetId}' up to date (v${version}, ${base}-era${persona === 'split' ? ', persona-split' : ''}) — idle`)
|
|
985
1184
|
continue
|
|
986
1185
|
}
|
|
987
1186
|
|
|
988
|
-
const skills = materialize({ target, presetId, skillsSource, version, base, persona, present })
|
|
1187
|
+
const skills = materialize({ target, presetId, skillsSource, version, base, persona, present, rows })
|
|
989
1188
|
const verb = state === 'absent' ? 'materialized' : 'refreshed'
|
|
990
1189
|
console.log(
|
|
991
1190
|
`${TAG} ${verb} preset '${presetId}' into ${userRootPath} (v${version}, ${base}-era composition${persona === 'split' ? ', persona-split' : ''})` +
|
|
@@ -995,4 +1194,4 @@ export async function apply(ctx, config = {}) {
|
|
|
995
1194
|
}
|
|
996
1195
|
|
|
997
1196
|
// Test surface: pure helpers, no Cordis context required.
|
|
998
|
-
export const _internal = { PRESET_IDS, translateMsysPath, translatePathArguments, readPosixPaths, windowsToMsys, rewriteResultPaths, MARKER_FILE, classify, materialize, cleanupOnDispose, firstUserRoot, hashTree, skillsHashes, syncDecision, installRegisterShim, baseForRoster, detectBase, pickComposition, personaEraForText, detectPersonaEra, injectPresentRow, hostHasToolPresent, detectPresentSupport }
|
|
1197
|
+
export const _internal = { PRESET_IDS, translateMsysPath, translatePathArguments, readPosixPaths, windowsToMsys, rewriteResultPaths, MARKER_FILE, classify, materialize, cleanupOnDispose, firstUserRoot, hashTree, skillsHashes, syncDecision, installRegisterShim, baseForRoster, detectBase, pickComposition, personaEraForText, detectPersonaEra, injectPresentRow, hostHasToolPresent, detectPresentSupport, rowFormOf, rowFormsOf, alignEngineRow, alignRalphRow, ROW_SOURCE }
|
package/src/shell.js
CHANGED
|
@@ -70,12 +70,52 @@ export class GitBashSandboxExecutor extends SandboxBashExecutor {
|
|
|
70
70
|
/**
|
|
71
71
|
* Wrap one shell command via the ctx.sandbox provider, substituting Git Bash
|
|
72
72
|
* for the shipped bare `bash` argv.
|
|
73
|
+
*
|
|
74
|
+
* Signature spans both dsh eras: 0.1.6-alpha.1 made the provider's confine
|
|
75
|
+
* async and handed it the caller's abort signal (the base class now calls
|
|
76
|
+
* `this.confine(command, policy, signal)` and awaits it). The predecessor
|
|
77
|
+
* was synchronous and took two arguments. Forwarding the third argument is
|
|
78
|
+
* safe on both — the old provider ignores it and its synchronous return is
|
|
79
|
+
* what the old base class still consumes — while dropping it on the new host
|
|
80
|
+
* would silently detach sandbox preparation from cancellation.
|
|
73
81
|
* @param command - shell source for the confined inner `bash -c`.
|
|
74
82
|
* @param policy - resolved confined execution policy.
|
|
75
|
-
* @
|
|
83
|
+
* @param signal - caller deadline/cancellation, forwarded when the host provides one.
|
|
84
|
+
* @returns the provider's exact argv and settlement-classification facts (a promise on dsh >= 0.1.6).
|
|
76
85
|
*/
|
|
77
|
-
confine(command, policy) {
|
|
78
|
-
return this.ctx.sandbox.confine([this.bashPath, '-c', command], policy)
|
|
86
|
+
confine(command, policy, signal) {
|
|
87
|
+
return this.ctx.sandbox.confine([this.bashPath, '-c', command], policy, signal)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Whether the LOADED base class declares an async `start`, probed once
|
|
92
|
+
* (pure). dsh 0.1.6-alpha.1 turned LocalBashExecutor.start into an async
|
|
93
|
+
* method returning Promise<ShellProcess>; before that it returned the live
|
|
94
|
+
* handle directly. This override has to match whichever contract the host
|
|
95
|
+
* actually carries: handing a plain handle to a host that awaits is fine,
|
|
96
|
+
* but handing a Promise to a synchronous host would give it a thenable
|
|
97
|
+
* instead of a process. Shape is probed, never inferred from a version.
|
|
98
|
+
* @returns true when the base class start is an async function.
|
|
99
|
+
*/
|
|
100
|
+
static baseStartIsAsync() {
|
|
101
|
+
return SandboxBashExecutor.prototype.start.constructor.name === 'AsyncFunction'
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Unwrap `runArgv`'s settlement across dsh eras (pure). 0.1.6-alpha.1
|
|
106
|
+
* changed the protected hook's return value from the bare ShellRunResult to
|
|
107
|
+
* `{ result, spawnRequested }`, where spawnRequested is false when
|
|
108
|
+
* preparation was cancelled before any spawn. Spreading the wrapper as if it
|
|
109
|
+
* were the result would hand the caller an object carrying no exitCode and
|
|
110
|
+
* no streams at all, so it is unwrapped by shape — never by version.
|
|
111
|
+
* @param raw - whatever the loaded `runArgv` resolved to.
|
|
112
|
+
* @returns the bare result plus whether argv ever reached the provider.
|
|
113
|
+
*/
|
|
114
|
+
static unwrapRunArgv(raw) {
|
|
115
|
+
if (raw !== null && typeof raw === 'object' && typeof raw.spawnRequested === 'boolean' && raw.result !== undefined) {
|
|
116
|
+
return { result: raw.result, spawnRequested: raw.spawnRequested }
|
|
117
|
+
}
|
|
118
|
+
return { result: raw, spawnRequested: true }
|
|
79
119
|
}
|
|
80
120
|
|
|
81
121
|
/**
|
|
@@ -93,13 +133,17 @@ export class GitBashSandboxExecutor extends SandboxBashExecutor {
|
|
|
93
133
|
if (policy === undefined) return super.run(spec)
|
|
94
134
|
const { mode } = policy
|
|
95
135
|
if (mode === 'danger-full-access') {
|
|
96
|
-
const result =
|
|
136
|
+
const { result } = GitBashSandboxExecutor.unwrapRunArgv(
|
|
137
|
+
await this.runArgv(spec, [this.bashPath, '-c', spec.command]))
|
|
97
138
|
return { ...result, sandbox: { mode, denied: false } }
|
|
98
139
|
}
|
|
99
140
|
if (process.platform === 'win32') {
|
|
100
141
|
this.warnConfinedUnconfined(mode)
|
|
101
|
-
const result =
|
|
102
|
-
|
|
142
|
+
const { result, spawnRequested } = GitBashSandboxExecutor.unwrapRunArgv(
|
|
143
|
+
await this.runArgv(spec, [this.bashPath, '-c', spec.command]))
|
|
144
|
+
// No spawn means no argv ran, so there is nothing to label as
|
|
145
|
+
// unconfined; the parent reports the same bare sandbox facts there.
|
|
146
|
+
return { ...result, sandbox: spawnRequested ? { mode, denied: false, enforcement: 'unconfined' } : { mode, denied: false } }
|
|
103
147
|
}
|
|
104
148
|
return super.run(spec)
|
|
105
149
|
}
|
|
@@ -108,16 +152,14 @@ export class GitBashSandboxExecutor extends SandboxBashExecutor {
|
|
|
108
152
|
const policy = spec.sandboxPolicy
|
|
109
153
|
if (policy === undefined) return super.start(spec)
|
|
110
154
|
const { mode } = policy
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
return proc
|
|
115
|
-
}
|
|
116
|
-
if (process.platform === 'win32') {
|
|
117
|
-
this.warnConfinedUnconfined(mode)
|
|
155
|
+
const fullAccess = mode === 'danger-full-access'
|
|
156
|
+
if (fullAccess || process.platform === 'win32') {
|
|
157
|
+
if (!fullAccess) this.warnConfinedUnconfined(mode)
|
|
118
158
|
const proc = this.startArgv(spec, [this.bashPath, '-c', spec.command])
|
|
119
|
-
proc.sandbox = { mode, denied: false, enforcement: 'unconfined' }
|
|
120
|
-
|
|
159
|
+
proc.sandbox = fullAccess ? { mode, denied: false } : { mode, denied: false, enforcement: 'unconfined' }
|
|
160
|
+
// Match the loaded base contract: the async one (dsh >= 0.1.6) is
|
|
161
|
+
// awaited by its callers, the synchronous one is consumed directly.
|
|
162
|
+
return GitBashSandboxExecutor.baseStartIsAsync() ? Promise.resolve(proc) : proc
|
|
121
163
|
}
|
|
122
164
|
return super.start(spec)
|
|
123
165
|
}
|