dsh-bash-terminal-ts 0.6.4 → 0.7.1
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.de.md +205 -0
- package/README.en.md +27 -7
- package/README.es.md +205 -0
- package/README.fr.md +205 -0
- package/README.it.md +205 -0
- package/README.md +29 -8
- package/README.ru.md +205 -0
- package/dist/client.core.js +46 -1
- package/dist/client.js +46 -1
- package/dsh.plugin.json +22 -22
- package/lib/client.js +46 -1
- package/lib/terminal.js +18 -0
- package/package.json +125 -119
- package/src/client.tsx +249 -207
- package/src/terminal.ts +16 -2
- package/template/README.md +82 -82
- package/template/terminal-adapter.ts +343 -343
package/src/client.tsx
CHANGED
|
@@ -1,207 +1,249 @@
|
|
|
1
|
-
// dsh-bash-terminal-ts client plugin: a "Default terminal" preference row in the
|
|
2
|
-
// Web UI General settings, mirroring the shipped EnterBehaviorRow grammar
|
|
3
|
-
// (row layout, capsule selector with chevron, --dsw-* tokens).
|
|
4
|
-
//
|
|
5
|
-
// The row offers one entry per host shell id — powershell / gitbash / msys2 /
|
|
6
|
-
// wsl — in the same order as SHELLS in ./index.ts.
|
|
7
|
-
//
|
|
8
|
-
// DSH >= 0.1.5: the browser module table (PLATFORM_MODULES) seeds react,
|
|
9
|
-
// @deepseek-ai/cordis, @deepseek-ai/dsh-client-store, @deepseek-ai/dsh-client-ui-slots,
|
|
10
|
-
// @deepseek-ai/dsh-client-ui-primitives and @deepseek-ai/dsh-client-ui-dockkit by
|
|
11
|
-
// exact bare specifier. The old dsh-client-runtime name is gone from that table
|
|
12
|
-
// (and so is its /client subpath), so this bundle requests dsh-client-store.
|
|
13
|
-
|
|
14
|
-
import { useState } from "react";
|
|
15
|
-
import { defineStore } from "@deepseek-ai/dsh-client-store";
|
|
16
|
-
import { IconChevronDownOutlineMedium, Menu } from "@deepseek-ai/dsh-client-ui-primitives";
|
|
17
|
-
|
|
18
|
-
const SETTINGS_NS = "settings.bash-terminal";
|
|
19
|
-
/** Entry id of this plugin in the active profile (cordis.patch.yml). */
|
|
20
|
-
const ENTRY_ID = "tool-bash-terminal";
|
|
21
|
-
const SHELLS = ["powershell", "gitbash", "msys2", "wsl"] as const;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
".
|
|
28
|
-
".
|
|
29
|
-
".
|
|
30
|
-
".
|
|
31
|
-
".
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
tag.
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
"shell.
|
|
44
|
-
"shell.
|
|
45
|
-
"shell.
|
|
46
|
-
"shell.
|
|
47
|
-
"shell.
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
"shell.
|
|
53
|
-
"shell.
|
|
54
|
-
"shell.
|
|
55
|
-
"shell.
|
|
56
|
-
"shell.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
shell:
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
)
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
)
|
|
206
|
-
|
|
207
|
-
}
|
|
1
|
+
// dsh-bash-terminal-ts client plugin: a "Default terminal" preference row in the
|
|
2
|
+
// Web UI General settings, mirroring the shipped EnterBehaviorRow grammar
|
|
3
|
+
// (row layout, capsule selector with chevron, --dsw-* tokens).
|
|
4
|
+
//
|
|
5
|
+
// The row offers one entry per host shell id — powershell / gitbash / msys2 /
|
|
6
|
+
// wsl — in the same order as SHELLS in ./index.ts.
|
|
7
|
+
//
|
|
8
|
+
// DSH >= 0.1.5: the browser module table (PLATFORM_MODULES) seeds react,
|
|
9
|
+
// @deepseek-ai/cordis, @deepseek-ai/dsh-client-store, @deepseek-ai/dsh-client-ui-slots,
|
|
10
|
+
// @deepseek-ai/dsh-client-ui-primitives and @deepseek-ai/dsh-client-ui-dockkit by
|
|
11
|
+
// exact bare specifier. The old dsh-client-runtime name is gone from that table
|
|
12
|
+
// (and so is its /client subpath), so this bundle requests dsh-client-store.
|
|
13
|
+
|
|
14
|
+
import { useState } from "react";
|
|
15
|
+
import { defineStore } from "@deepseek-ai/dsh-client-store";
|
|
16
|
+
import { IconChevronDownOutlineMedium, Menu } from "@deepseek-ai/dsh-client-ui-primitives";
|
|
17
|
+
|
|
18
|
+
const SETTINGS_NS = "settings.bash-terminal";
|
|
19
|
+
/** Entry id of this plugin in the active profile (cordis.patch.yml). */
|
|
20
|
+
const ENTRY_ID = "tool-bash-terminal";
|
|
21
|
+
const SHELLS = ["powershell", "gitbash", "msys2", "wsl"] as const;
|
|
22
|
+
|
|
23
|
+
// Injected once when the browser loads the bundle (node tests guard on document).
|
|
24
|
+
const ROW_CSS =
|
|
25
|
+
".btRow{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:center;gap:8px;padding:16px 0;display:flex}" +
|
|
26
|
+
".btRowText{flex-direction:column;flex:1;gap:4px;min-width:0;padding-right:48px;display:flex}" +
|
|
27
|
+
".btTitle{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:400;line-height:22px}" +
|
|
28
|
+
".btDesc{color:var(--dsw-alias-label-tertiary);font-size:12px;font-weight:400;line-height:18px}" +
|
|
29
|
+
".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}" +
|
|
30
|
+
".btSelector:hover{background:var(--dsw-alias-interactive-bg-hover)}" +
|
|
31
|
+
".btChevron{flex:none}";
|
|
32
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=\"bash-terminal-row\"]") === null) {
|
|
33
|
+
const tag = document.createElement("style");
|
|
34
|
+
tag.dataset.plugin = "dsh-bash-terminal-ts";
|
|
35
|
+
tag.dataset.pluginCss = "bash-terminal-row";
|
|
36
|
+
tag.textContent = ROW_CSS;
|
|
37
|
+
document.head.appendChild(tag);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const zh: Record<string, string> = {
|
|
41
|
+
"shell.title": "默认终端",
|
|
42
|
+
"shell.description": "shell 工具执行命令时使用的终端",
|
|
43
|
+
"shell.powershell": "PowerShell",
|
|
44
|
+
"shell.gitbash": "Git Bash",
|
|
45
|
+
"shell.msys2": "MSYS2",
|
|
46
|
+
"shell.wsl": "WSL",
|
|
47
|
+
"shell.unavailable": "设置面不可用:宿主未提供本插件的 configForms 条目(检查 cordis.patch.yml 的 entry id)"
|
|
48
|
+
};
|
|
49
|
+
const en: Record<string, string> = {
|
|
50
|
+
"shell.title": "Default terminal",
|
|
51
|
+
"shell.description": "Terminal used by the shell tool",
|
|
52
|
+
"shell.powershell": "PowerShell",
|
|
53
|
+
"shell.gitbash": "Git Bash",
|
|
54
|
+
"shell.msys2": "MSYS2",
|
|
55
|
+
"shell.wsl": "WSL",
|
|
56
|
+
"shell.unavailable": "Settings unavailable: the host exposes no configForms entry for this plugin (check the cordis.patch.yml entry id)"
|
|
57
|
+
};
|
|
58
|
+
const fr: Record<string, string> = {
|
|
59
|
+
"shell.title": "Terminal par défaut",
|
|
60
|
+
"shell.description": "Terminal utilisé par l'outil shell",
|
|
61
|
+
"shell.powershell": "PowerShell",
|
|
62
|
+
"shell.gitbash": "Git Bash",
|
|
63
|
+
"shell.msys2": "MSYS2",
|
|
64
|
+
"shell.wsl": "WSL",
|
|
65
|
+
"shell.unavailable": "Paramètres indisponibles : l'hôte n'expose aucune entrée configForms pour ce plugin (vérifiez l'entry id du cordis.patch.yml)"
|
|
66
|
+
};
|
|
67
|
+
const de: Record<string, string> = {
|
|
68
|
+
"shell.title": "Standard-Terminal",
|
|
69
|
+
"shell.description": "Terminal, das vom Shell-Tool verwendet wird",
|
|
70
|
+
"shell.powershell": "PowerShell",
|
|
71
|
+
"shell.gitbash": "Git Bash",
|
|
72
|
+
"shell.msys2": "MSYS2",
|
|
73
|
+
"shell.wsl": "WSL",
|
|
74
|
+
"shell.unavailable": "Einstellungen nicht verfügbar: Der Host stellt keinen configForms-Eintrag für dieses Plugin bereit (Entry-ID in cordis.patch.yml prüfen)"
|
|
75
|
+
};
|
|
76
|
+
const it: Record<string, string> = {
|
|
77
|
+
"shell.title": "Terminale predefinito",
|
|
78
|
+
"shell.description": "Terminale usato dallo strumento shell",
|
|
79
|
+
"shell.powershell": "PowerShell",
|
|
80
|
+
"shell.gitbash": "Git Bash",
|
|
81
|
+
"shell.msys2": "MSYS2",
|
|
82
|
+
"shell.wsl": "WSL",
|
|
83
|
+
"shell.unavailable": "Impostazioni non disponibili: l'host non espone alcuna voce configForms per questo plugin (verifica l'entry id in cordis.patch.yml)"
|
|
84
|
+
};
|
|
85
|
+
const ru: Record<string, string> = {
|
|
86
|
+
"shell.title": "Терминал по умолчанию",
|
|
87
|
+
"shell.description": "Терминал, используемый инструментом shell",
|
|
88
|
+
"shell.powershell": "PowerShell",
|
|
89
|
+
"shell.gitbash": "Git Bash",
|
|
90
|
+
"shell.msys2": "MSYS2",
|
|
91
|
+
"shell.wsl": "WSL",
|
|
92
|
+
"shell.unavailable": "Настройки недоступны: хост не предоставляет запись configForms для этого плагина (проверьте entry id в cordis.patch.yml)"
|
|
93
|
+
};
|
|
94
|
+
const es: Record<string, string> = {
|
|
95
|
+
"shell.title": "Terminal predeterminado",
|
|
96
|
+
"shell.description": "Terminal que usa la herramienta shell",
|
|
97
|
+
"shell.powershell": "PowerShell",
|
|
98
|
+
"shell.gitbash": "Git Bash",
|
|
99
|
+
"shell.msys2": "MSYS2",
|
|
100
|
+
"shell.wsl": "WSL",
|
|
101
|
+
"shell.unavailable": "Configuración no disponible: el host no expone ninguna entrada configForms para este plugin (comprueba el entry id de cordis.patch.yml)"
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export const inject = ["slots", "locale", "configForms"];
|
|
105
|
+
|
|
106
|
+
/** Row state snapshot served through the settings store. */
|
|
107
|
+
interface RowState {
|
|
108
|
+
shell: string;
|
|
109
|
+
revision: number;
|
|
110
|
+
writable: boolean;
|
|
111
|
+
status: "loading" | "ready" | "unavailable";
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
interface ShellPreferenceRowProps {
|
|
115
|
+
t: (key: string) => string;
|
|
116
|
+
useStore: <S>(selector: (state: RowState) => S) => S;
|
|
117
|
+
setShell: (id: string) => void;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function ShellPreferenceRow({ t, useStore, setShell }: ShellPreferenceRowProps) {
|
|
121
|
+
const shell = useStore((s) => s.shell);
|
|
122
|
+
const writable = useStore((s) => s.writable);
|
|
123
|
+
const status = useStore((s) => s.status);
|
|
124
|
+
const [open, setOpen] = useState(false);
|
|
125
|
+
const items = SHELLS.map((id) => ({ id, label: t("shell." + id) }));
|
|
126
|
+
if (status === "unavailable") {
|
|
127
|
+
return (
|
|
128
|
+
<div className="btRow">
|
|
129
|
+
<div className="btRowText">
|
|
130
|
+
<div className="btTitle">{t("shell.title")}</div>
|
|
131
|
+
<div className="btDesc">{t("shell.unavailable")}</div>
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
return (
|
|
137
|
+
<div className="btRow">
|
|
138
|
+
<div className="btRowText">
|
|
139
|
+
<div className="btTitle">{t("shell.title")}</div>
|
|
140
|
+
<div className="btDesc">{t("shell.description")}</div>
|
|
141
|
+
</div>
|
|
142
|
+
<Menu
|
|
143
|
+
open={open}
|
|
144
|
+
onClose={() => setOpen(false)}
|
|
145
|
+
items={items}
|
|
146
|
+
selectedId={shell}
|
|
147
|
+
onSelect={(id) => {
|
|
148
|
+
setOpen(false);
|
|
149
|
+
setShell(id);
|
|
150
|
+
}}
|
|
151
|
+
align="end"
|
|
152
|
+
portal
|
|
153
|
+
anchor={
|
|
154
|
+
<button
|
|
155
|
+
type="button"
|
|
156
|
+
className="btSelector"
|
|
157
|
+
aria-haspopup="menu"
|
|
158
|
+
aria-expanded={open}
|
|
159
|
+
disabled={!writable}
|
|
160
|
+
onClick={() => setOpen(!open)}
|
|
161
|
+
style={!writable ? { opacity: 0.5, cursor: "not-allowed" } : undefined}
|
|
162
|
+
>
|
|
163
|
+
{t("shell." + shell)}
|
|
164
|
+
<IconChevronDownOutlineMedium className="btChevron" />
|
|
165
|
+
</button>
|
|
166
|
+
}
|
|
167
|
+
/>
|
|
168
|
+
</div>
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
interface SettingsSnapshot {
|
|
173
|
+
status: "loading" | "ready" | "unavailable";
|
|
174
|
+
value?: { defaultShell?: string };
|
|
175
|
+
revision: number | undefined;
|
|
176
|
+
writable: boolean;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
interface ConfigFormBinding {
|
|
180
|
+
getSnapshot(): SettingsSnapshot;
|
|
181
|
+
subscribe(fn: () => void): () => void;
|
|
182
|
+
set(field: string, value: string): Promise<boolean>;
|
|
183
|
+
unset(field: string): Promise<boolean>;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
interface ClientContext {
|
|
187
|
+
effect(fn: () => unknown, name?: string): unknown;
|
|
188
|
+
locale: { register(ns: string, dicts: Record<string, Record<string, string>>): unknown };
|
|
189
|
+
configForms: { get(entryId: string): ConfigFormBinding };
|
|
190
|
+
slots: {
|
|
191
|
+
inject(slot: string, fn: () => unknown, name?: string): void;
|
|
192
|
+
register(options: Record<string, unknown>, Component: unknown): Record<string, unknown>;
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
interface RowStoreActions {
|
|
197
|
+
sync(shell?: string, revision?: number, writable?: boolean, status?: RowState["status"]): void;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** 账本 label:从 zh/en 字典取「默认终端」标题(无宿主 locale 依赖)。 */
|
|
201
|
+
const zhDictTitle = (): string => (zh as Record<string, string>)["shell.title"] ?? "终端";
|
|
202
|
+
|
|
203
|
+
export function apply(ctx: ClientContext): void {
|
|
204
|
+
// Single call for every locale: the registry rejects a duplicate
|
|
205
|
+
// (namespace, locale) pair, so one bundle keeps the namespace atomic
|
|
206
|
+
// (fr/de/it/ru/es ride the untyped dicts overload; only zh/en are built in).
|
|
207
|
+
ctx.effect(() => ctx.locale.register(SETTINGS_NS, { zh, en, fr, de, it, ru, es }), "bash-terminal: settings dictionaries");
|
|
208
|
+
const scope = ctx.configForms.get(ENTRY_ID);
|
|
209
|
+
const store = defineStore<RowState, { sync: (draft: RowState, shell?: string, revision?: number, writable?: boolean, status?: RowState["status"]) => void }>({
|
|
210
|
+
init: (): RowState => ({ shell: "powershell", revision: -1, writable: false, status: "loading" }),
|
|
211
|
+
actions: {
|
|
212
|
+
sync: (d, shell, revision, writable, status) => {
|
|
213
|
+
if (revision !== undefined && revision <= d.revision) return;
|
|
214
|
+
if (shell !== undefined) d.shell = shell;
|
|
215
|
+
if (revision !== undefined) d.revision = revision;
|
|
216
|
+
if (writable !== undefined) d.writable = writable;
|
|
217
|
+
if (status !== undefined) d.status = status;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
let bound: RowStoreActions | undefined;
|
|
222
|
+
const push = (snap: SettingsSnapshot): void => {
|
|
223
|
+
bound?.sync(snap.value?.defaultShell, snap.revision, snap.writable, snap.status);
|
|
224
|
+
};
|
|
225
|
+
// 插件族共用设置 tab(dsh-thinking-levels 顶级「起子插件设置」节声明该子席位)。
|
|
226
|
+
// thinking-levels 缺席时本 inject 静默等待,不阻塞客户端半。
|
|
227
|
+
ctx.slots.inject(
|
|
228
|
+
"dsh-family.tab",
|
|
229
|
+
() =>
|
|
230
|
+
ctx.slots.register(
|
|
231
|
+
{
|
|
232
|
+
name: "dsh-family.tab",
|
|
233
|
+
id: "bash-terminal-shell",
|
|
234
|
+
order: 60,
|
|
235
|
+
label: () => zhDictTitle(),
|
|
236
|
+
store,
|
|
237
|
+
locale: SETTINGS_NS,
|
|
238
|
+
inject: (actions: unknown) => {
|
|
239
|
+
bound = actions as RowStoreActions;
|
|
240
|
+
push(scope.getSnapshot());
|
|
241
|
+
return { setShell: (value: string) => void scope.set("defaultShell", value) };
|
|
242
|
+
}
|
|
243
|
+
},
|
|
244
|
+
ShellPreferenceRow
|
|
245
|
+
),
|
|
246
|
+
"bash-terminal: settings row"
|
|
247
|
+
);
|
|
248
|
+
ctx.effect(() => scope.subscribe(() => push(scope.getSnapshot())), "bash-terminal: settings watch");
|
|
249
|
+
}
|
package/src/terminal.ts
CHANGED
|
@@ -22,8 +22,7 @@ import type {
|
|
|
22
22
|
JobsRegistry,
|
|
23
23
|
ResolvedPaths,
|
|
24
24
|
TerminalHandle,
|
|
25
|
-
TerminalSpawnSpec
|
|
26
|
-
ToolRunContext
|
|
25
|
+
TerminalSpawnSpec
|
|
27
26
|
} from "./dsh-types.js";
|
|
28
27
|
|
|
29
28
|
const MAX_BUFFER_BYTES = 1024 * 1024;
|
|
@@ -140,6 +139,20 @@ type NodePtySpawn = (
|
|
|
140
139
|
options: { name: string; cols: number; rows: number; cwd: string; env: Record<string, string | undefined> }
|
|
141
140
|
) => NodePtyTerm;
|
|
142
141
|
|
|
142
|
+
/**
|
|
143
|
+
* node-pty >=1.2.0-beta (ConPTY dll path) assigns the inner pid only after the
|
|
144
|
+
* agent connects its data pipe; it reads 0 before that. The 1.1.x line reports
|
|
145
|
+
* it synchronously, so waiting is a no-op there.
|
|
146
|
+
*/
|
|
147
|
+
async function waitForPid(term: NodePtyTerm, timeoutMs = 5000): Promise<void> {
|
|
148
|
+
const deadline = Date.now() + timeoutMs;
|
|
149
|
+
while (term.pid <= 0 && Date.now() < deadline) await delay(25);
|
|
150
|
+
if (term.pid <= 0) {
|
|
151
|
+
try { term.kill(); } catch { /* already dead */ }
|
|
152
|
+
throw new Error("terminal: node-pty did not report a process id within " + timeoutMs + "ms (ConPTY agent did not connect)");
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
143
156
|
/**
|
|
144
157
|
* Direct node-pty handle with the same surface the official seam returns:
|
|
145
158
|
* output (stream), pid, done, write / signalForeground / terminate. Used on
|
|
@@ -156,6 +169,7 @@ async function spawnPtyHandle(spec: TerminalSpawnSpec): Promise<TerminalHandle>
|
|
|
156
169
|
cwd,
|
|
157
170
|
env: { ...env, TERM: "dumb", NO_COLOR: "1" }
|
|
158
171
|
});
|
|
172
|
+
await waitForPid(term);
|
|
159
173
|
const output = new PassThrough();
|
|
160
174
|
term.onData((chunk) => output.write(Buffer.from(chunk, "utf8")));
|
|
161
175
|
const done = new Promise<{ exitCode: number; signal: string | null }>((resolveDone) => {
|