dsh-bash-terminal-ts 0.7.1 → 0.7.2-compat.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.core.js +127 -95
- package/dist/client.js +127 -95
- package/dsh.plugin.json +22 -22
- package/lib/client.js +127 -95
- package/lib/index.js +101 -0
- package/package.json +121 -125
- package/src/client.tsx +153 -147
- package/src/index.ts +105 -0
package/dsh.plugin.json
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
{
|
|
2
|
-
"id": "dsh-bash-terminal-ts",
|
|
3
|
-
"name": "dsh-bash-terminal-ts",
|
|
4
|
-
"version": "0.7.
|
|
5
|
-
"description": {
|
|
6
|
-
"en": "One shell tool that runs commands through PowerShell, Git Bash, MSYS2 or WSL on Windows, with a user-chosen default terminal in the Web UI settings.",
|
|
7
|
-
"zh": "一个 shell 工具,可在 Windows 上通过 PowerShell / Git Bash / MSYS2 / WSL 执行命令,默认终端在 Web UI 设置中选择。"
|
|
8
|
-
},
|
|
9
|
-
"homepage": "https://github.com/drscrewdriver/dsh-bash-terminal-ts",
|
|
10
|
-
"repository": {
|
|
11
|
-
"type": "git",
|
|
12
|
-
"url": "https://github.com/drscrewdriver/dsh-bash-terminal-ts.git"
|
|
13
|
-
},
|
|
14
|
-
"license": "MIT",
|
|
15
|
-
"engines": {
|
|
16
|
-
"dsh": ">=0.
|
|
17
|
-
},
|
|
18
|
-
"components": {
|
|
19
|
-
"host": "lib/index.js",
|
|
20
|
-
"client": "dist/client.js"
|
|
21
|
-
}
|
|
22
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"id": "dsh-bash-terminal-ts",
|
|
3
|
+
"name": "dsh-bash-terminal-ts",
|
|
4
|
+
"version": "0.7.2-compat.2",
|
|
5
|
+
"description": {
|
|
6
|
+
"en": "One shell tool that runs commands through PowerShell, Git Bash, MSYS2 or WSL on Windows, with a user-chosen default terminal in the Web UI settings.",
|
|
7
|
+
"zh": "一个 shell 工具,可在 Windows 上通过 PowerShell / Git Bash / MSYS2 / WSL 执行命令,默认终端在 Web UI 设置中选择。"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://github.com/drscrewdriver/dsh-bash-terminal-ts",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/drscrewdriver/dsh-bash-terminal-ts.git"
|
|
13
|
+
},
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"engines": {
|
|
16
|
+
"dsh": ">=0.1.0-rc.8 <0.2.1-0"
|
|
17
|
+
},
|
|
18
|
+
"components": {
|
|
19
|
+
"host": "lib/index.js",
|
|
20
|
+
"client": "dist/client.js"
|
|
21
|
+
}
|
|
22
|
+
}
|
package/lib/client.js
CHANGED
|
@@ -24,18 +24,14 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
24
24
|
// src/client.tsx
|
|
25
25
|
var client_exports = {};
|
|
26
26
|
__export(client_exports, {
|
|
27
|
-
apply: () => apply
|
|
28
|
-
inject: () => inject
|
|
27
|
+
apply: () => apply
|
|
29
28
|
});
|
|
30
29
|
module.exports = __toCommonJS(client_exports);
|
|
31
30
|
var import_react = require("react");
|
|
32
|
-
var import_dsh_client_store = require("@deepseek-ai/dsh-client-store");
|
|
33
|
-
var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
34
31
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
35
32
|
var SETTINGS_NS = "settings.bash-terminal";
|
|
36
|
-
var ENTRY_ID = "tool-bash-terminal";
|
|
37
33
|
var SHELLS = ["powershell", "gitbash", "msys2", "wsl"];
|
|
38
|
-
var ROW_CSS = ".btRow{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:center;gap:8px;padding:16px 0;display:flex}.btRowText{flex-direction:column;flex:1;gap:4px;min-width:0;padding-right:48px;display:flex}.btTitle{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:400;line-height:22px}.btDesc{color:var(--dsw-alias-label-tertiary);font-size:12px;font-weight:400;line-height:18px}.btSelector{background:var(--dsw-alias-bg-module-platform);height:36px;font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;border:none;border-radius:18px;align-items:center;gap:12px;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.btSelector:hover{background:var(--dsw-alias-interactive-bg-hover)}.
|
|
34
|
+
var ROW_CSS = ".btRow{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:center;gap:8px;padding:16px 0;display:flex}.btRowText{flex-direction:column;flex:1;gap:4px;min-width:0;padding-right:48px;display:flex}.btTitle{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:400;line-height:22px}.btDesc{color:var(--dsw-alias-label-tertiary);font-size:12px;font-weight:400;line-height:18px}.btSelector{background:var(--dsw-alias-bg-module-platform);height:36px;font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;border:none;border-radius:18px;align-items:center;gap:12px;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.btSelector:hover{background:var(--dsw-alias-interactive-bg-hover)}.btGear{position:fixed;right:16px;bottom:110px;z-index:10000;width:40px;height:40px;border-radius:50%;border:1px solid rgba(127,127,127,.4);background:rgba(30,30,32,.85);color:inherit;cursor:pointer;font-size:17px;box-shadow:0 4px 16px rgba(0,0,0,.35)}.btOverlay{position:fixed;inset:0;z-index:10000;background:rgba(0,0,0,.45)}.btPanel{position:fixed;top:6vh;left:50%;transform:translateX(-50%);width:min(760px,92vw);max-height:84vh;overflow-y:auto;z-index:10001;background:var(--dsw-alias-bg-module-platform,rgba(30,30,32,.92));border:1px solid var(--dsw-alias-border-l2,rgba(127,127,127,.3));border-radius:14px;padding:20px;color:var(--dsw-alias-label-primary)}";
|
|
39
35
|
if (typeof document !== "undefined" && document.querySelector('style[data-plugin-css="bash-terminal-row"]') === null) {
|
|
40
36
|
const tag = document.createElement("style");
|
|
41
37
|
tag.dataset.plugin = "dsh-bash-terminal-ts";
|
|
@@ -50,7 +46,7 @@ var zh = {
|
|
|
50
46
|
"shell.gitbash": "Git Bash",
|
|
51
47
|
"shell.msys2": "MSYS2",
|
|
52
48
|
"shell.wsl": "WSL",
|
|
53
|
-
"shell.unavailable": "\u8BBE\u7F6E\u9762\u4E0D\u53EF\u7528
|
|
49
|
+
"shell.unavailable": "\u8BBE\u7F6E\u9762\u4E0D\u53EF\u7528:\u5BBF\u4E3B\u914D\u7F6E\u6865\u672A\u54CD\u5E94(\u68C0\u67E5\u5BBF\u4E3B\u662F\u5426\u4E3A compat \u7248)"
|
|
54
50
|
};
|
|
55
51
|
var en = {
|
|
56
52
|
"shell.title": "Default terminal",
|
|
@@ -59,7 +55,7 @@ var en = {
|
|
|
59
55
|
"shell.gitbash": "Git Bash",
|
|
60
56
|
"shell.msys2": "MSYS2",
|
|
61
57
|
"shell.wsl": "WSL",
|
|
62
|
-
"shell.unavailable": "Settings unavailable: the host
|
|
58
|
+
"shell.unavailable": "Settings unavailable: the host config bridge did not respond (is the host a compat build?)"
|
|
63
59
|
};
|
|
64
60
|
var fr = {
|
|
65
61
|
"shell.title": "Terminal par d\xE9faut",
|
|
@@ -68,136 +64,172 @@ var fr = {
|
|
|
68
64
|
"shell.gitbash": "Git Bash",
|
|
69
65
|
"shell.msys2": "MSYS2",
|
|
70
66
|
"shell.wsl": "WSL",
|
|
71
|
-
"shell.unavailable": "Param\xE8tres indisponibles
|
|
67
|
+
"shell.unavailable": "Param\xE8tres indisponibles: le pont de configuration de l'h\xF4te n'a pas r\xE9pondu"
|
|
72
68
|
};
|
|
73
69
|
var de = {
|
|
74
|
-
"shell.title": "
|
|
75
|
-
"shell.description": "Terminal
|
|
70
|
+
"shell.title": "Standardterminal",
|
|
71
|
+
"shell.description": "Terminal f\xFCr den Shell-Befehl",
|
|
76
72
|
"shell.powershell": "PowerShell",
|
|
77
73
|
"shell.gitbash": "Git Bash",
|
|
78
74
|
"shell.msys2": "MSYS2",
|
|
79
75
|
"shell.wsl": "WSL",
|
|
80
|
-
"shell.unavailable": "Einstellungen nicht verf\xFCgbar:
|
|
76
|
+
"shell.unavailable": "Einstellungen nicht verf\xFCgbar: Die Konfigurationsbr\xFCcke des Hosts hat nicht geantwortet"
|
|
81
77
|
};
|
|
82
78
|
var it = {
|
|
83
79
|
"shell.title": "Terminale predefinito",
|
|
84
|
-
"shell.description": "Terminale usato
|
|
80
|
+
"shell.description": "Terminale usato dal comando shell",
|
|
85
81
|
"shell.powershell": "PowerShell",
|
|
86
82
|
"shell.gitbash": "Git Bash",
|
|
87
83
|
"shell.msys2": "MSYS2",
|
|
88
84
|
"shell.wsl": "WSL",
|
|
89
|
-
"shell.unavailable": "Impostazioni non disponibili:
|
|
85
|
+
"shell.unavailable": "Impostazioni non disponibili: il bridge di configurazione dell'host non ha risposto"
|
|
90
86
|
};
|
|
91
87
|
var ru = {
|
|
92
88
|
"shell.title": "\u0422\u0435\u0440\u043C\u0438\u043D\u0430\u043B \u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E",
|
|
93
|
-
"shell.description": "\u0422\u0435\u0440\u043C\u0438\u043D\u0430\u043B
|
|
89
|
+
"shell.description": "\u0422\u0435\u0440\u043C\u0438\u043D\u0430\u043B \u0434\u043B\u044F \u043A\u043E\u043C\u0430\u043D\u0434 \u043E\u0431\u043E\u043B\u043E\u0447\u043A\u0438",
|
|
94
90
|
"shell.powershell": "PowerShell",
|
|
95
91
|
"shell.gitbash": "Git Bash",
|
|
96
92
|
"shell.msys2": "MSYS2",
|
|
97
93
|
"shell.wsl": "WSL",
|
|
98
|
-
"shell.unavailable": "\u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438 \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B: \
|
|
94
|
+
"shell.unavailable": "\u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438 \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B: \u043C\u043E\u0441\u0442 \u043A\u043E\u043D\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u0445\u043E\u0441\u0442\u0430 \u043D\u0435 \u043E\u0442\u0432\u0435\u0442\u0438\u043B"
|
|
99
95
|
};
|
|
100
96
|
var es = {
|
|
101
|
-
"shell.title": "Terminal
|
|
102
|
-
"shell.description": "Terminal
|
|
97
|
+
"shell.title": "Terminal predeterminada",
|
|
98
|
+
"shell.description": "Terminal usada por el comando shell",
|
|
103
99
|
"shell.powershell": "PowerShell",
|
|
104
100
|
"shell.gitbash": "Git Bash",
|
|
105
101
|
"shell.msys2": "MSYS2",
|
|
106
102
|
"shell.wsl": "WSL",
|
|
107
|
-
"shell.unavailable": "Configuraci\xF3n no disponible: el
|
|
103
|
+
"shell.unavailable": "Configuraci\xF3n no disponible: el puente de configuraci\xF3n del host no respondi\xF3"
|
|
108
104
|
};
|
|
109
|
-
var
|
|
110
|
-
function
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
105
|
+
var zhDictTitle = () => zh["shell.title"] ?? "Terminal";
|
|
106
|
+
async function bridgeConfig() {
|
|
107
|
+
try {
|
|
108
|
+
const r = await fetch("/api/bash-terminal/config", {
|
|
109
|
+
method: "POST",
|
|
110
|
+
headers: { "content-type": "application/json" },
|
|
111
|
+
body: "{}"
|
|
112
|
+
});
|
|
113
|
+
const d = await r.json();
|
|
114
|
+
return d && d.ok ? d.value : null;
|
|
115
|
+
} catch {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
async function bridgeWrite(value) {
|
|
120
|
+
try {
|
|
121
|
+
const r = await fetch("/api/bash-terminal/config", {
|
|
122
|
+
method: "POST",
|
|
123
|
+
headers: { "content-type": "application/json" },
|
|
124
|
+
body: JSON.stringify({ defaultShell: value })
|
|
125
|
+
});
|
|
126
|
+
const d = await r.json();
|
|
127
|
+
return !!(d && d.ok);
|
|
128
|
+
} catch {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function ShellPreferenceRow() {
|
|
133
|
+
const [shell, setShell] = (0, import_react.useState)("powershell");
|
|
134
|
+
const [status, setStatus] = (0, import_react.useState)("loading");
|
|
135
|
+
(0, import_react.useEffect)(() => {
|
|
136
|
+
let alive = true;
|
|
137
|
+
bridgeConfig().then((v) => {
|
|
138
|
+
if (!alive) return;
|
|
139
|
+
if (v && v.defaultShell) {
|
|
140
|
+
setShell(v.defaultShell);
|
|
141
|
+
setStatus("ready");
|
|
142
|
+
} else {
|
|
143
|
+
setStatus("unavailable");
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
return () => {
|
|
147
|
+
alive = false;
|
|
148
|
+
};
|
|
149
|
+
}, []);
|
|
150
|
+
const change = (value) => {
|
|
151
|
+
setShell(value);
|
|
152
|
+
void bridgeWrite(value);
|
|
153
|
+
};
|
|
116
154
|
if (status === "unavailable") {
|
|
117
155
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "btRow", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "btRowText", children: [
|
|
118
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "btTitle", children:
|
|
119
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "btDesc", children:
|
|
156
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "btTitle", children: zh["shell.title"] }),
|
|
157
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "btDesc", children: zh["shell.unavailable"] })
|
|
120
158
|
] }) });
|
|
121
159
|
}
|
|
122
160
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "btRow", children: [
|
|
123
161
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "btRowText", children: [
|
|
124
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "btTitle", children:
|
|
125
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "btDesc", children:
|
|
162
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "btTitle", children: zh["shell.title"] }),
|
|
163
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "btDesc", children: zh["shell.description"] })
|
|
126
164
|
] }),
|
|
127
165
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
128
|
-
|
|
166
|
+
"select",
|
|
129
167
|
{
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
onSelect: (id) => {
|
|
135
|
-
setOpen(false);
|
|
136
|
-
setShell(id);
|
|
137
|
-
},
|
|
138
|
-
align: "end",
|
|
139
|
-
portal: true,
|
|
140
|
-
anchor: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
141
|
-
"button",
|
|
142
|
-
{
|
|
143
|
-
type: "button",
|
|
144
|
-
className: "btSelector",
|
|
145
|
-
"aria-haspopup": "menu",
|
|
146
|
-
"aria-expanded": open,
|
|
147
|
-
disabled: !writable,
|
|
148
|
-
onClick: () => setOpen(!open),
|
|
149
|
-
style: !writable ? { opacity: 0.5, cursor: "not-allowed" } : void 0,
|
|
150
|
-
children: [
|
|
151
|
-
t("shell." + shell),
|
|
152
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconChevronDownOutlineMedium, { className: "btChevron" })
|
|
153
|
-
]
|
|
154
|
-
}
|
|
155
|
-
)
|
|
168
|
+
className: "btSelector",
|
|
169
|
+
value: shell,
|
|
170
|
+
onChange: (e) => change(e.target.value),
|
|
171
|
+
children: SHELLS.map((id) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: id, children: zh["shell." + id] ?? id }, id))
|
|
156
172
|
}
|
|
157
173
|
)
|
|
158
174
|
] });
|
|
159
175
|
}
|
|
160
|
-
|
|
176
|
+
function SettingsOverlayEntry() {
|
|
177
|
+
const [open, setOpen] = (0, import_react.useState)(false);
|
|
178
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { position: "fixed", right: "16px", bottom: "110px", zIndex: 1e4 }, children: [
|
|
179
|
+
open ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "btOverlay", onClick: () => setOpen(false) }) : null,
|
|
180
|
+
open ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "btPanel", children: [
|
|
181
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
182
|
+
"button",
|
|
183
|
+
{
|
|
184
|
+
type: "button",
|
|
185
|
+
className: "btSelector",
|
|
186
|
+
style: { float: "right", height: "30px", fontSize: "12px", marginBottom: "8px" },
|
|
187
|
+
onClick: () => setOpen(false),
|
|
188
|
+
children: "\u2715"
|
|
189
|
+
}
|
|
190
|
+
),
|
|
191
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ShellPreferenceRow, {})
|
|
192
|
+
] }) : null,
|
|
193
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "btGear", title: zhDictTitle(), onClick: () => setOpen(!open), children: "\u2699" })
|
|
194
|
+
] });
|
|
195
|
+
}
|
|
161
196
|
function apply(ctx) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
sync: (d, shell, revision, writable, status) => {
|
|
168
|
-
if (revision !== void 0 && revision <= d.revision) return;
|
|
169
|
-
if (shell !== void 0) d.shell = shell;
|
|
170
|
-
if (revision !== void 0) d.revision = revision;
|
|
171
|
-
if (writable !== void 0) d.writable = writable;
|
|
172
|
-
if (status !== void 0) d.status = status;
|
|
173
|
-
}
|
|
197
|
+
const safe = (tag, fn) => {
|
|
198
|
+
try {
|
|
199
|
+
fn();
|
|
200
|
+
} catch (e) {
|
|
201
|
+
console.warn(`[dsh-bash-terminal-ts] client surface '${tag}' failed:`, e);
|
|
174
202
|
}
|
|
175
|
-
});
|
|
176
|
-
let bound;
|
|
177
|
-
const push = (snap) => {
|
|
178
|
-
bound?.sync(snap.value?.defaultShell, snap.revision, snap.writable, snap.status);
|
|
179
203
|
};
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
204
|
+
safe("locale", () => {
|
|
205
|
+
ctx.effect(
|
|
206
|
+
() => ctx.locale.register(SETTINGS_NS, { zh, en, fr, de, it, ru, es }),
|
|
207
|
+
"bash-terminal: settings dictionaries"
|
|
208
|
+
);
|
|
209
|
+
});
|
|
210
|
+
safe("dsh-family.tab", () => {
|
|
211
|
+
ctx.slots.inject(
|
|
212
|
+
"dsh-family.tab",
|
|
213
|
+
() => ctx.slots.register(
|
|
214
|
+
{
|
|
215
|
+
name: "dsh-family.tab",
|
|
216
|
+
id: "bash-terminal-shell",
|
|
217
|
+
order: 60,
|
|
218
|
+
label: zhDictTitle,
|
|
219
|
+
locale: SETTINGS_NS,
|
|
220
|
+
inject: () => ({})
|
|
221
|
+
},
|
|
222
|
+
() => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ShellPreferenceRow, {})
|
|
223
|
+
),
|
|
224
|
+
"bash-terminal: settings row"
|
|
225
|
+
);
|
|
226
|
+
});
|
|
227
|
+
safe("shell.overlay", () => {
|
|
228
|
+
ctx.slots.inject(
|
|
229
|
+
"shell.overlay",
|
|
230
|
+
() => ctx.slots.register({ name: "shell.overlay", id: "bash-terminal-settings" }, () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SettingsOverlayEntry, {}))
|
|
231
|
+
);
|
|
232
|
+
});
|
|
201
233
|
}
|
|
202
234
|
|
|
203
235
|
return module.exports;
|
package/lib/index.js
CHANGED
|
@@ -27,6 +27,8 @@ import { ESCALATION_TARGETS, TOOL_ABORTED, approveEscalation, defineTool, escala
|
|
|
27
27
|
import { createTerminalRegistry, terminalTool } from "./terminal.js";
|
|
28
28
|
/** Stable Cordis plugin name. */
|
|
29
29
|
export const name = "bash-terminal";
|
|
30
|
+
/** Composition entry id (the `tool-bash-terminal` row the bundle patch declares). */
|
|
31
|
+
const ENTRY_ID = "tool-bash-terminal";
|
|
30
32
|
/** Services required before the tool can register. */
|
|
31
33
|
export const inject = ["tools", "systemPrompt", "shellEnv", "subprocess", "settings", "sandbox", "sandboxPolicy"];
|
|
32
34
|
/** The terminal backends this tool exposes, in catalog order. */
|
|
@@ -501,7 +503,13 @@ export function apply(ctx, config = {}) {
|
|
|
501
503
|
// 0.1.7: `defaultShell` is a `.volatile()` config field — the loader hands
|
|
502
504
|
// `apply` a live ref and the latest value is resolved on every read; there
|
|
503
505
|
// is no settings registration call anymore (removed with the 0.1.7 host).
|
|
506
|
+
// compat-legacy: ≤0.1.6 的宿主没有配置热重载,bridge 写入走这个覆盖值
|
|
507
|
+
// (rc.1+ 上 volatile 与覆盖值同源同写,二者一致)。
|
|
508
|
+
let shellOverride;
|
|
504
509
|
const currentShell = () => {
|
|
510
|
+
if (typeof shellOverride === "string" && SHELLS.includes(shellOverride)) {
|
|
511
|
+
return shellOverride;
|
|
512
|
+
}
|
|
505
513
|
const v = config.defaultShell;
|
|
506
514
|
if (typeof v === "string")
|
|
507
515
|
return v;
|
|
@@ -533,6 +541,99 @@ export function apply(ctx, config = {}) {
|
|
|
533
541
|
order: 105,
|
|
534
542
|
text: "Use the shell tool for terminal commands: it runs in the terminal the user chose in Settings -> General -> Default terminal (PowerShell, Git Bash, MSYS2, or WSL) and honors the DSH sandbox. Prefer it over the pwsh tool for everyday commands; keep the pwsh tool for cases that specifically need the sandboxed PowerShell surface."
|
|
535
543
|
});
|
|
544
|
+
// compat-legacy: 六线统一配置通道(≤0.1.6 无 client-store 模块表,client 走本
|
|
545
|
+
// bridge 读写 defaultShell)。写路径 = settings.mutate;≤0.1.6 的 loader 不会自动
|
|
546
|
+
// 注册命名空间,复用 perm-gate 自门控(not registered → register → 重试);
|
|
547
|
+
// rc.1+ 命名空间由 loader 占用,该分支自然不命中。GET 带回当前值与可选集。
|
|
548
|
+
// 宿主 context 类型未收录 inject/局部服务形状,compat 桥按宽化面调用。
|
|
549
|
+
const compatCtx = ctx;
|
|
550
|
+
compatCtx.inject(["webServer", "settings"], (svcCtx) => {
|
|
551
|
+
const svc = svcCtx.settings;
|
|
552
|
+
const writeShell = async (value) => {
|
|
553
|
+
if (typeof svc.mutate !== "function") {
|
|
554
|
+
// 显式降级:该线 settings 服务无 mutate(理论仅最老线),覆盖值仅本进程
|
|
555
|
+
// 生效;持久化请改 cordis.patch.yml 的 defaultShell。
|
|
556
|
+
throw new Error("settings service exposes no mutate on this host line - defaultShell applies for this process only; persist via cordis.patch.yml");
|
|
557
|
+
}
|
|
558
|
+
shellOverride = value;
|
|
559
|
+
try {
|
|
560
|
+
await svc.mutate(ENTRY_ID, [{ op: "set", path: ["defaultShell"], value }]);
|
|
561
|
+
}
|
|
562
|
+
catch (e) {
|
|
563
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
564
|
+
if (!/not registered/i.test(message) || typeof svc.register !== "function") {
|
|
565
|
+
throw e;
|
|
566
|
+
}
|
|
567
|
+
console.warn(`[dsh-bash-terminal-ts] legacy line: registering settings namespace ${ENTRY_ID}`);
|
|
568
|
+
svc.register(ENTRY_ID, Config, { base: { defaultShell: value } });
|
|
569
|
+
await svc.mutate(ENTRY_ID, [{ op: "set", path: ["defaultShell"], value }]);
|
|
570
|
+
console.warn("[dsh-bash-terminal-ts] legacy settings namespace registered");
|
|
571
|
+
}
|
|
572
|
+
};
|
|
573
|
+
svcCtx.effect(() => {
|
|
574
|
+
const json = (res, status, body) => {
|
|
575
|
+
res.writeHead(status, { "content-type": "application/json; charset=utf-8" });
|
|
576
|
+
res.end(JSON.stringify(body));
|
|
577
|
+
};
|
|
578
|
+
const handler = (req, res) => {
|
|
579
|
+
const addr = req.socket?.remoteAddress ?? "";
|
|
580
|
+
if (addr !== "127.0.0.1" && addr !== "::1" && addr !== "::ffff:127.0.0.1") {
|
|
581
|
+
json(res, 403, { ok: false, code: "loopback-only" });
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
if (req.method === "GET") {
|
|
585
|
+
// 缝内省:compat 验证用——当前线 ctx.subprocess 的真实能力面
|
|
586
|
+
const seam = ctx;
|
|
587
|
+
const sp = seam.subprocess;
|
|
588
|
+
json(res, 200, {
|
|
589
|
+
ok: true,
|
|
590
|
+
value: {
|
|
591
|
+
defaultShell: currentShell(),
|
|
592
|
+
shells: SHELLS,
|
|
593
|
+
seam: { spawn: typeof sp?.spawn, spawnTerminal: typeof sp?.spawnTerminal },
|
|
594
|
+
},
|
|
595
|
+
});
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
if (req.method !== "POST") {
|
|
599
|
+
json(res, 405, { ok: false, code: "method-not-allowed" });
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
const chunks = [];
|
|
603
|
+
let size = 0;
|
|
604
|
+
req.on("data", (c) => {
|
|
605
|
+
size += c.length;
|
|
606
|
+
if (size <= 65536)
|
|
607
|
+
chunks.push(c);
|
|
608
|
+
});
|
|
609
|
+
req.on("end", () => {
|
|
610
|
+
let body = {};
|
|
611
|
+
try {
|
|
612
|
+
body = JSON.parse(Buffer.concat(chunks).toString("utf8") || "{}");
|
|
613
|
+
}
|
|
614
|
+
catch {
|
|
615
|
+
json(res, 400, { ok: false, code: "bad-json" });
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
const value = typeof body.defaultShell === "string" ? body.defaultShell : "";
|
|
619
|
+
if (!SHELLS.includes(value)) {
|
|
620
|
+
json(res, 400, { ok: false, code: "invalid-shell" });
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
void writeShell(value)
|
|
624
|
+
.then(() => json(res, 200, { ok: true, value: { defaultShell: value } }))
|
|
625
|
+
.catch((e) => json(res, 500, { ok: false, code: "write-failed", message: e instanceof Error ? e.message : String(e) }));
|
|
626
|
+
});
|
|
627
|
+
};
|
|
628
|
+
const registered = [
|
|
629
|
+
svcCtx.webServer.register({ kind: "exact", path: "/api/bash-terminal/config", handler }),
|
|
630
|
+
];
|
|
631
|
+
return () => {
|
|
632
|
+
for (const dispose of registered)
|
|
633
|
+
dispose();
|
|
634
|
+
};
|
|
635
|
+
}, "bash-terminal: compat config bridge");
|
|
636
|
+
});
|
|
536
637
|
const toolName = "shell";
|
|
537
638
|
const terminalRegistry = createTerminalRegistry(ctx);
|
|
538
639
|
ctx.tools.register(terminalTool(ctx, terminalRegistry, paths, () => currentShell()));
|