@particle-academy/agent-integrations 0.11.1 → 0.12.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 +6 -0
- package/dist/bridges/terminal.d.cts +56 -24
- package/dist/bridges/terminal.d.ts +56 -24
- package/dist/bridges-terminal.cjs +116 -53
- package/dist/bridges-terminal.cjs.map +1 -1
- package/dist/bridges-terminal.js +1 -1
- package/dist/chunk-57KAMBAR.js +275 -0
- package/dist/chunk-57KAMBAR.js.map +1 -0
- package/dist/index.cjs +116 -53
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-FZ7Q5NS3.js +0 -212
- package/dist/chunk-FZ7Q5NS3.js.map +0 -1
package/README.md
CHANGED
|
@@ -215,3 +215,9 @@ Agents (Claude Desktop, Cline, custom) connect to the same channel via your auth
|
|
|
215
215
|
## License
|
|
216
216
|
|
|
217
217
|
MIT
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## ⭐ Star Fancy UI
|
|
222
|
+
|
|
223
|
+
If this package is useful to you, a quick ⭐ on the repo really helps us build a better kit. Thank you!
|
|
@@ -3,7 +3,7 @@ import { B as Bridge } from '../types-CCSBGW9T.cjs';
|
|
|
3
3
|
import '../types-aOQLTW0E.cjs';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* A shell/profile an agent can switch
|
|
6
|
+
* A shell/profile an agent can switch a terminal to. Mirrors fancy-term's
|
|
7
7
|
* `ShellProfile` (kept local so the bridge never imports fancy-term).
|
|
8
8
|
*/
|
|
9
9
|
type TerminalShell = {
|
|
@@ -12,36 +12,63 @@ type TerminalShell = {
|
|
|
12
12
|
icon?: string;
|
|
13
13
|
};
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
15
|
+
* One terminal the bridge can drive. A Human+ app often hosts **several**
|
|
16
|
+
* terminals on a screen (a build pane, a server pane, an agent scratch shell);
|
|
17
|
+
* each is a `TerminalRef` with a stable `id` so an agent can read/write any of
|
|
18
|
+
* them — not just "its own". Wire the function fields to that terminal's
|
|
19
|
+
* fancy-term `TerminalHandle`.
|
|
18
20
|
*/
|
|
19
|
-
type
|
|
20
|
-
/**
|
|
21
|
-
|
|
22
|
-
/**
|
|
21
|
+
type TerminalRef = {
|
|
22
|
+
/** Stable id used to address this terminal (`terminal_list` enumerates them). */
|
|
23
|
+
id: string;
|
|
24
|
+
/** Human label, e.g. "Build", "Server". Defaults to the id. */
|
|
25
|
+
label?: string;
|
|
26
|
+
/** True for the focused terminal — the default target when no id is passed. */
|
|
27
|
+
active?: boolean;
|
|
28
|
+
/** Read the visible buffer as text (wire to `TerminalHandle.getBuffer`). */
|
|
23
29
|
getBuffer: () => string;
|
|
24
|
-
/** Write raw data / keystrokes
|
|
30
|
+
/** Write raw data / keystrokes (wire to `TerminalHandle.write`). */
|
|
25
31
|
write: (data: string) => void;
|
|
26
|
-
/** Run a command. Defaults to writing `${command}\r
|
|
32
|
+
/** Run a command. Defaults to writing `${command}\r`; override for a real runner. */
|
|
27
33
|
runCommand?: (command: string) => void | Promise<void>;
|
|
28
|
-
/**
|
|
34
|
+
/** Clear the viewport (wire to `TerminalHandle.clear`). */
|
|
29
35
|
clear?: () => void;
|
|
30
|
-
/**
|
|
36
|
+
/** Current text selection (wire to `TerminalHandle.getSelection`). */
|
|
37
|
+
getSelection?: () => string;
|
|
38
|
+
/** Shells this terminal offers (cmd, PowerShell, …). */
|
|
31
39
|
listShells?: () => TerminalShell[];
|
|
32
|
-
/**
|
|
40
|
+
/** Switch this terminal's active shell by id. */
|
|
33
41
|
setShell?: (id: string) => void | Promise<void>;
|
|
34
|
-
/**
|
|
42
|
+
/** This terminal's active shell id. */
|
|
35
43
|
getShell?: () => string | undefined;
|
|
36
44
|
};
|
|
45
|
+
/**
|
|
46
|
+
* Single-terminal adapter (back-compat). A `TerminalRef` without the
|
|
47
|
+
* `id`/`label`/`active` bookkeeping — pass it as `{ adapter }` and the bridge
|
|
48
|
+
* treats it as the one (active) terminal. Use `{ terminals }` for multiple.
|
|
49
|
+
*/
|
|
50
|
+
type TerminalBridgeAdapter = Omit<TerminalRef, "id" | "label" | "active"> & {
|
|
51
|
+
/** fancy-screens screen id (optional) so activity events know which screen the terminal lives in. */
|
|
52
|
+
screenId?: string;
|
|
53
|
+
};
|
|
37
54
|
type StagedKind = "write" | "run";
|
|
38
55
|
type Staged = {
|
|
39
56
|
id: string;
|
|
40
57
|
kind: StagedKind;
|
|
41
58
|
data: string;
|
|
59
|
+
terminalId: string;
|
|
42
60
|
};
|
|
43
61
|
type TerminalBridgeOptions = {
|
|
44
|
-
|
|
62
|
+
/** A single terminal (back-compat). Mutually exclusive with `terminals`. */
|
|
63
|
+
adapter?: TerminalBridgeAdapter;
|
|
64
|
+
/**
|
|
65
|
+
* The live list of terminals on the screen. Use this when the app hosts more
|
|
66
|
+
* than one terminal so an agent can `terminal_list` then target any of them by
|
|
67
|
+
* id — i.e. reach into another terminal in the same screen.
|
|
68
|
+
*/
|
|
69
|
+
terminals?: () => TerminalRef[];
|
|
70
|
+
/** fancy-screens screen id for activity events (defaults to `adapter.screenId`). */
|
|
71
|
+
screenId?: string;
|
|
45
72
|
agent?: {
|
|
46
73
|
id: string;
|
|
47
74
|
name?: string;
|
|
@@ -66,15 +93,20 @@ type TerminalBridge = Bridge & {
|
|
|
66
93
|
pending: () => Staged[];
|
|
67
94
|
};
|
|
68
95
|
/**
|
|
69
|
-
* registerTerminalBridge — MCP access to
|
|
70
|
-
* visible buffer (`terminal_read`), writes input
|
|
71
|
-
* commands (`terminal_run`) through the host
|
|
72
|
-
* an `AgentActivity` event. With `pendingMode`, destructive
|
|
73
|
-
* for human confirmation (`terminal_confirm` / `terminal_reject`
|
|
74
|
-
* `terminal_pending`).
|
|
75
|
-
*
|
|
76
|
-
*
|
|
96
|
+
* registerTerminalBridge — MCP access to one **or many** terminal surfaces on a
|
|
97
|
+
* screen. An agent reads the visible buffer (`terminal_read`), writes input
|
|
98
|
+
* (`terminal_write`), and runs commands (`terminal_run`) through the host; every
|
|
99
|
+
* mutation broadcasts an `AgentActivity` event. With `pendingMode`, destructive
|
|
100
|
+
* actions are staged for human confirmation (`terminal_confirm` / `terminal_reject`
|
|
101
|
+
* / `terminal_pending`).
|
|
102
|
+
*
|
|
103
|
+
* **Multi-terminal:** pass `{ terminals }` (vs a single `{ adapter }`) and every
|
|
104
|
+
* tool takes an optional `terminal` id; `terminal_list` enumerates them. This is
|
|
105
|
+
* how an agent **reaches into another terminal in the same screen** rather than
|
|
106
|
+
* being stuck in one. When a terminal offers shells, the agent can also list
|
|
107
|
+
* (`terminal_list_shells`) and switch (`terminal_set_shell`) its shell. Tool
|
|
108
|
+
* prefix `terminal_*`.
|
|
77
109
|
*/
|
|
78
110
|
declare function registerTerminalBridge(host: ToolHost, options: TerminalBridgeOptions): TerminalBridge;
|
|
79
111
|
|
|
80
|
-
export { type TerminalBridge, type TerminalBridgeAdapter, type TerminalBridgeOptions, type TerminalShell, registerTerminalBridge };
|
|
112
|
+
export { type TerminalBridge, type TerminalBridgeAdapter, type TerminalBridgeOptions, type TerminalRef, type TerminalShell, registerTerminalBridge };
|
|
@@ -3,7 +3,7 @@ import { B as Bridge } from '../types-DIVNcIQO.js';
|
|
|
3
3
|
import '../types-aOQLTW0E.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* A shell/profile an agent can switch
|
|
6
|
+
* A shell/profile an agent can switch a terminal to. Mirrors fancy-term's
|
|
7
7
|
* `ShellProfile` (kept local so the bridge never imports fancy-term).
|
|
8
8
|
*/
|
|
9
9
|
type TerminalShell = {
|
|
@@ -12,36 +12,63 @@ type TerminalShell = {
|
|
|
12
12
|
icon?: string;
|
|
13
13
|
};
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
15
|
+
* One terminal the bridge can drive. A Human+ app often hosts **several**
|
|
16
|
+
* terminals on a screen (a build pane, a server pane, an agent scratch shell);
|
|
17
|
+
* each is a `TerminalRef` with a stable `id` so an agent can read/write any of
|
|
18
|
+
* them — not just "its own". Wire the function fields to that terminal's
|
|
19
|
+
* fancy-term `TerminalHandle`.
|
|
18
20
|
*/
|
|
19
|
-
type
|
|
20
|
-
/**
|
|
21
|
-
|
|
22
|
-
/**
|
|
21
|
+
type TerminalRef = {
|
|
22
|
+
/** Stable id used to address this terminal (`terminal_list` enumerates them). */
|
|
23
|
+
id: string;
|
|
24
|
+
/** Human label, e.g. "Build", "Server". Defaults to the id. */
|
|
25
|
+
label?: string;
|
|
26
|
+
/** True for the focused terminal — the default target when no id is passed. */
|
|
27
|
+
active?: boolean;
|
|
28
|
+
/** Read the visible buffer as text (wire to `TerminalHandle.getBuffer`). */
|
|
23
29
|
getBuffer: () => string;
|
|
24
|
-
/** Write raw data / keystrokes
|
|
30
|
+
/** Write raw data / keystrokes (wire to `TerminalHandle.write`). */
|
|
25
31
|
write: (data: string) => void;
|
|
26
|
-
/** Run a command. Defaults to writing `${command}\r
|
|
32
|
+
/** Run a command. Defaults to writing `${command}\r`; override for a real runner. */
|
|
27
33
|
runCommand?: (command: string) => void | Promise<void>;
|
|
28
|
-
/**
|
|
34
|
+
/** Clear the viewport (wire to `TerminalHandle.clear`). */
|
|
29
35
|
clear?: () => void;
|
|
30
|
-
/**
|
|
36
|
+
/** Current text selection (wire to `TerminalHandle.getSelection`). */
|
|
37
|
+
getSelection?: () => string;
|
|
38
|
+
/** Shells this terminal offers (cmd, PowerShell, …). */
|
|
31
39
|
listShells?: () => TerminalShell[];
|
|
32
|
-
/**
|
|
40
|
+
/** Switch this terminal's active shell by id. */
|
|
33
41
|
setShell?: (id: string) => void | Promise<void>;
|
|
34
|
-
/**
|
|
42
|
+
/** This terminal's active shell id. */
|
|
35
43
|
getShell?: () => string | undefined;
|
|
36
44
|
};
|
|
45
|
+
/**
|
|
46
|
+
* Single-terminal adapter (back-compat). A `TerminalRef` without the
|
|
47
|
+
* `id`/`label`/`active` bookkeeping — pass it as `{ adapter }` and the bridge
|
|
48
|
+
* treats it as the one (active) terminal. Use `{ terminals }` for multiple.
|
|
49
|
+
*/
|
|
50
|
+
type TerminalBridgeAdapter = Omit<TerminalRef, "id" | "label" | "active"> & {
|
|
51
|
+
/** fancy-screens screen id (optional) so activity events know which screen the terminal lives in. */
|
|
52
|
+
screenId?: string;
|
|
53
|
+
};
|
|
37
54
|
type StagedKind = "write" | "run";
|
|
38
55
|
type Staged = {
|
|
39
56
|
id: string;
|
|
40
57
|
kind: StagedKind;
|
|
41
58
|
data: string;
|
|
59
|
+
terminalId: string;
|
|
42
60
|
};
|
|
43
61
|
type TerminalBridgeOptions = {
|
|
44
|
-
|
|
62
|
+
/** A single terminal (back-compat). Mutually exclusive with `terminals`. */
|
|
63
|
+
adapter?: TerminalBridgeAdapter;
|
|
64
|
+
/**
|
|
65
|
+
* The live list of terminals on the screen. Use this when the app hosts more
|
|
66
|
+
* than one terminal so an agent can `terminal_list` then target any of them by
|
|
67
|
+
* id — i.e. reach into another terminal in the same screen.
|
|
68
|
+
*/
|
|
69
|
+
terminals?: () => TerminalRef[];
|
|
70
|
+
/** fancy-screens screen id for activity events (defaults to `adapter.screenId`). */
|
|
71
|
+
screenId?: string;
|
|
45
72
|
agent?: {
|
|
46
73
|
id: string;
|
|
47
74
|
name?: string;
|
|
@@ -66,15 +93,20 @@ type TerminalBridge = Bridge & {
|
|
|
66
93
|
pending: () => Staged[];
|
|
67
94
|
};
|
|
68
95
|
/**
|
|
69
|
-
* registerTerminalBridge — MCP access to
|
|
70
|
-
* visible buffer (`terminal_read`), writes input
|
|
71
|
-
* commands (`terminal_run`) through the host
|
|
72
|
-
* an `AgentActivity` event. With `pendingMode`, destructive
|
|
73
|
-
* for human confirmation (`terminal_confirm` / `terminal_reject`
|
|
74
|
-
* `terminal_pending`).
|
|
75
|
-
*
|
|
76
|
-
*
|
|
96
|
+
* registerTerminalBridge — MCP access to one **or many** terminal surfaces on a
|
|
97
|
+
* screen. An agent reads the visible buffer (`terminal_read`), writes input
|
|
98
|
+
* (`terminal_write`), and runs commands (`terminal_run`) through the host; every
|
|
99
|
+
* mutation broadcasts an `AgentActivity` event. With `pendingMode`, destructive
|
|
100
|
+
* actions are staged for human confirmation (`terminal_confirm` / `terminal_reject`
|
|
101
|
+
* / `terminal_pending`).
|
|
102
|
+
*
|
|
103
|
+
* **Multi-terminal:** pass `{ terminals }` (vs a single `{ adapter }`) and every
|
|
104
|
+
* tool takes an optional `terminal` id; `terminal_list` enumerates them. This is
|
|
105
|
+
* how an agent **reaches into another terminal in the same screen** rather than
|
|
106
|
+
* being stuck in one. When a terminal offers shells, the agent can also list
|
|
107
|
+
* (`terminal_list_shells`) and switch (`terminal_set_shell`) its shell. Tool
|
|
108
|
+
* prefix `terminal_*`.
|
|
77
109
|
*/
|
|
78
110
|
declare function registerTerminalBridge(host: ToolHost, options: TerminalBridgeOptions): TerminalBridge;
|
|
79
111
|
|
|
80
|
-
export { type TerminalBridge, type TerminalBridgeAdapter, type TerminalBridgeOptions, type TerminalShell, registerTerminalBridge };
|
|
112
|
+
export { type TerminalBridge, type TerminalBridgeAdapter, type TerminalBridgeOptions, type TerminalRef, type TerminalShell, registerTerminalBridge };
|
|
@@ -126,19 +126,39 @@ function serialize(entry) {
|
|
|
126
126
|
var DEFAULT_AGENT = { id: "agent", name: "Agent", color: "#a855f7" };
|
|
127
127
|
var truncate = (s, n = 60) => s.length > n ? s.slice(0, n) + "\u2026" : s;
|
|
128
128
|
function registerTerminalBridge(host, options) {
|
|
129
|
-
const { adapter } = options;
|
|
130
129
|
const agent = { ...DEFAULT_AGENT, ...options.agent ?? {} };
|
|
131
130
|
const pendingMode = options.pendingMode ?? false;
|
|
131
|
+
const screenId = options.screenId ?? options.adapter?.screenId;
|
|
132
132
|
const disposers = [];
|
|
133
133
|
const staged = /* @__PURE__ */ new Map();
|
|
134
134
|
let seq = 0;
|
|
135
135
|
ensureUndoToolsRegistered(host);
|
|
136
|
-
const
|
|
136
|
+
const listTerminals = () => {
|
|
137
|
+
if (options.terminals) return options.terminals();
|
|
138
|
+
if (options.adapter) return [{ id: "terminal", label: "Terminal", active: true, ...options.adapter }];
|
|
139
|
+
return [];
|
|
140
|
+
};
|
|
141
|
+
const resolve = (id) => {
|
|
142
|
+
const list = listTerminals();
|
|
143
|
+
if (typeof id === "string" && id !== "") return list.find((t) => t.id === id);
|
|
144
|
+
return list.find((t) => t.active) ?? list[0];
|
|
145
|
+
};
|
|
146
|
+
const anyMulti = !!options.terminals;
|
|
147
|
+
const canClear = anyMulti || !!options.adapter?.clear;
|
|
148
|
+
const canShells = anyMulti || !!options.adapter?.listShells;
|
|
149
|
+
const canSetShell = anyMulti || !!options.adapter?.setShell;
|
|
150
|
+
const target = (label, terminalId) => ({
|
|
137
151
|
kind: "terminal",
|
|
138
|
-
screenId
|
|
139
|
-
elementId:
|
|
152
|
+
screenId,
|
|
153
|
+
elementId: terminalId ?? screenId ?? "terminal",
|
|
140
154
|
label: label ?? "terminal"
|
|
141
155
|
});
|
|
156
|
+
const TERMINAL_ARG = {
|
|
157
|
+
terminal: {
|
|
158
|
+
type: "string",
|
|
159
|
+
description: "Terminal id to target (call terminal_list for ids). Omit for the active / only terminal."
|
|
160
|
+
}
|
|
161
|
+
};
|
|
142
162
|
const reg = (name, description, properties, required, handler, isMutation, resolveTarget) => {
|
|
143
163
|
const wrapped = async (args) => {
|
|
144
164
|
try {
|
|
@@ -151,7 +171,7 @@ function registerTerminalBridge(host, options) {
|
|
|
151
171
|
toolName: name,
|
|
152
172
|
agent,
|
|
153
173
|
kind: "terminal",
|
|
154
|
-
screenId
|
|
174
|
+
screenId,
|
|
155
175
|
resolveTarget: ({ args, result }) => resolveTarget?.(args, result) ?? target()
|
|
156
176
|
}) : wrapped;
|
|
157
177
|
disposers.push(
|
|
@@ -165,38 +185,66 @@ function registerTerminalBridge(host, options) {
|
|
|
165
185
|
)
|
|
166
186
|
);
|
|
167
187
|
};
|
|
168
|
-
|
|
188
|
+
const need = (args) => {
|
|
189
|
+
const t = resolve(args.terminal);
|
|
190
|
+
if (!t) {
|
|
191
|
+
const ids = listTerminals().map((x) => x.id).join(", ") || "(none)";
|
|
192
|
+
throw new Error(
|
|
193
|
+
typeof args.terminal === "string" && args.terminal ? `Unknown terminal '${args.terminal}'. Available: ${ids}. Use terminal_list.` : "No terminal available."
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
return t;
|
|
197
|
+
};
|
|
198
|
+
async function exec(t, kind, data) {
|
|
169
199
|
if (kind === "run") {
|
|
170
|
-
if (
|
|
171
|
-
else
|
|
200
|
+
if (t.runCommand) await t.runCommand(data);
|
|
201
|
+
else t.write(data + "\r");
|
|
172
202
|
} else {
|
|
173
|
-
|
|
203
|
+
t.write(data);
|
|
174
204
|
}
|
|
175
205
|
}
|
|
176
|
-
async function stageOrExec(kind, data) {
|
|
206
|
+
async function stageOrExec(t, kind, data) {
|
|
177
207
|
if (!pendingMode) {
|
|
178
|
-
await exec(kind, data);
|
|
179
|
-
return textResult(`${kind === "run" ? "ran" : "wrote"}: ${truncate(data)}`, {
|
|
208
|
+
await exec(t, kind, data);
|
|
209
|
+
return textResult(`${kind === "run" ? "ran" : "wrote"} on ${t.id}: ${truncate(data)}`, {
|
|
210
|
+
kind,
|
|
211
|
+
data,
|
|
212
|
+
terminal: t.id,
|
|
213
|
+
executed: true
|
|
214
|
+
});
|
|
180
215
|
}
|
|
181
216
|
const id = `t${++seq}`;
|
|
182
|
-
const entry = { id, kind, data };
|
|
217
|
+
const entry = { id, kind, data, terminalId: t.id };
|
|
183
218
|
staged.set(id, entry);
|
|
184
219
|
options.onPending?.(entry);
|
|
185
220
|
return textResult(
|
|
186
|
-
`Staged ${kind} (id ${id}) \u2014 awaiting human confirmation: ${truncate(data)}`,
|
|
221
|
+
`Staged ${kind} on ${t.id} (id ${id}) \u2014 awaiting human confirmation: ${truncate(data)}`,
|
|
187
222
|
{ ...entry, pending: true }
|
|
188
223
|
);
|
|
189
224
|
}
|
|
225
|
+
reg(
|
|
226
|
+
"terminal_list",
|
|
227
|
+
"List the terminals on this screen (id, label, which is active) \u2014 so you can reach into another terminal, not just the active one. Pass the chosen id as `terminal` to the other tools.",
|
|
228
|
+
{},
|
|
229
|
+
[],
|
|
230
|
+
() => {
|
|
231
|
+
const list = listTerminals().map((t) => ({ id: t.id, label: t.label ?? t.id, active: !!t.active }));
|
|
232
|
+
const text = list.length ? list.map((t) => `${t.active ? "* " : " "}${t.id} \u2014 ${t.label}`).join("\n") : "(no terminals)";
|
|
233
|
+
return textResult(text, { terminals: list });
|
|
234
|
+
},
|
|
235
|
+
false
|
|
236
|
+
);
|
|
190
237
|
reg(
|
|
191
238
|
"terminal_read",
|
|
192
|
-
"Read
|
|
193
|
-
{ tail: { type: "number", description: "Return only the last N lines." } },
|
|
239
|
+
"Read a terminal's visible buffer as text \u2014 what the user sees. Pass `tail` for only the last N lines, `terminal` to read a specific one.",
|
|
240
|
+
{ ...TERMINAL_ARG, tail: { type: "number", description: "Return only the last N lines." } },
|
|
194
241
|
[],
|
|
195
242
|
(args) => {
|
|
196
|
-
|
|
243
|
+
const t = need(args);
|
|
244
|
+
let buf = t.getBuffer();
|
|
197
245
|
const tail = typeof args.tail === "number" ? args.tail : void 0;
|
|
198
246
|
if (tail && tail > 0) buf = buf.split("\n").slice(-tail).join("\n");
|
|
199
|
-
return textResult(buf, { buffer: buf });
|
|
247
|
+
return textResult(buf, { buffer: buf, terminal: t.id });
|
|
200
248
|
},
|
|
201
249
|
false
|
|
202
250
|
);
|
|
@@ -208,7 +256,7 @@ function registerTerminalBridge(host, options) {
|
|
|
208
256
|
() => {
|
|
209
257
|
const list = [...staged.values()];
|
|
210
258
|
return textResult(
|
|
211
|
-
list.length ? list.map((s) => `${s.id}: ${s.kind} ${truncate(s.data)}`).join("\n") : "(none)",
|
|
259
|
+
list.length ? list.map((s) => `${s.id}: ${s.kind} on ${s.terminalId} ${truncate(s.data)}`).join("\n") : "(none)",
|
|
212
260
|
{ pending: list }
|
|
213
261
|
);
|
|
214
262
|
},
|
|
@@ -216,20 +264,21 @@ function registerTerminalBridge(host, options) {
|
|
|
216
264
|
);
|
|
217
265
|
reg(
|
|
218
266
|
"terminal_write",
|
|
219
|
-
"Write raw data / keystrokes to
|
|
220
|
-
{ data: { type: "string", description: "Raw bytes to write." } },
|
|
267
|
+
"Write raw data / keystrokes to a terminal (input, control chars, ANSI). Pass `terminal` to target a specific one. In pendingMode this stages instead of executing.",
|
|
268
|
+
{ ...TERMINAL_ARG, data: { type: "string", description: "Raw bytes to write." } },
|
|
221
269
|
["data"],
|
|
222
|
-
(args) => stageOrExec("write", String(args.data)),
|
|
223
|
-
true
|
|
270
|
+
(args) => stageOrExec(need(args), "write", String(args.data)),
|
|
271
|
+
true,
|
|
272
|
+
(args) => target(`write:${String(args.terminal ?? "")}`, resolve(args.terminal)?.id)
|
|
224
273
|
);
|
|
225
274
|
reg(
|
|
226
275
|
"terminal_run",
|
|
227
|
-
"Run a shell command \u2014 writes the command
|
|
228
|
-
{ command: { type: "string", description: "The command line to run." } },
|
|
276
|
+
"Run a shell command in a terminal \u2014 writes the command + Enter (or the host's runner). Pass `terminal` to target a specific one. In pendingMode this stages it for confirmation.",
|
|
277
|
+
{ ...TERMINAL_ARG, command: { type: "string", description: "The command line to run." } },
|
|
229
278
|
["command"],
|
|
230
|
-
(args) => stageOrExec("run", String(args.command)),
|
|
279
|
+
(args) => stageOrExec(need(args), "run", String(args.command)),
|
|
231
280
|
true,
|
|
232
|
-
(args) => target(truncate(String(args.command ?? "")))
|
|
281
|
+
(args) => target(truncate(String(args.command ?? "")), resolve(args.terminal)?.id)
|
|
233
282
|
);
|
|
234
283
|
reg(
|
|
235
284
|
"terminal_confirm",
|
|
@@ -240,11 +289,17 @@ function registerTerminalBridge(host, options) {
|
|
|
240
289
|
const id = String(args.id);
|
|
241
290
|
const entry = staged.get(id);
|
|
242
291
|
if (!entry) return errorResult(`No staged command ${id}`);
|
|
292
|
+
const t = resolve(entry.terminalId);
|
|
293
|
+
if (!t) return errorResult(`Terminal '${entry.terminalId}' is gone \u2014 cannot run ${id}`);
|
|
243
294
|
staged.delete(id);
|
|
244
|
-
await exec(entry.kind, entry.data);
|
|
245
|
-
return textResult(`Confirmed ${id}: ${entry.kind} ${truncate(entry.data)}`, { ...entry, executed: true });
|
|
295
|
+
await exec(t, entry.kind, entry.data);
|
|
296
|
+
return textResult(`Confirmed ${id}: ${entry.kind} on ${t.id} ${truncate(entry.data)}`, { ...entry, executed: true });
|
|
246
297
|
},
|
|
247
|
-
true
|
|
298
|
+
true,
|
|
299
|
+
(args) => {
|
|
300
|
+
const e = staged.get(String(args.id));
|
|
301
|
+
return target(`confirm:${String(args.id ?? "")}`, e?.terminalId);
|
|
302
|
+
}
|
|
248
303
|
);
|
|
249
304
|
reg(
|
|
250
305
|
"terminal_reject",
|
|
@@ -258,51 +313,58 @@ function registerTerminalBridge(host, options) {
|
|
|
258
313
|
},
|
|
259
314
|
false
|
|
260
315
|
);
|
|
261
|
-
if (
|
|
316
|
+
if (canClear) {
|
|
262
317
|
reg(
|
|
263
318
|
"terminal_clear",
|
|
264
|
-
"Clear
|
|
265
|
-
{},
|
|
319
|
+
"Clear a terminal's viewport. Pass `terminal` to target a specific one.",
|
|
320
|
+
{ ...TERMINAL_ARG },
|
|
266
321
|
[],
|
|
267
|
-
() => {
|
|
268
|
-
|
|
269
|
-
return
|
|
322
|
+
(args) => {
|
|
323
|
+
const t = need(args);
|
|
324
|
+
if (!t.clear) return errorResult(`Terminal '${t.id}' can't be cleared.`);
|
|
325
|
+
t.clear();
|
|
326
|
+
return textResult(`cleared ${t.id}`, { terminal: t.id });
|
|
270
327
|
},
|
|
271
|
-
true
|
|
328
|
+
true,
|
|
329
|
+
(args) => target(`clear:${String(args.terminal ?? "")}`, resolve(args.terminal)?.id)
|
|
272
330
|
);
|
|
273
331
|
}
|
|
274
|
-
if (
|
|
332
|
+
if (canShells) {
|
|
275
333
|
reg(
|
|
276
334
|
"terminal_list_shells",
|
|
277
|
-
"List the shells
|
|
278
|
-
{},
|
|
335
|
+
"List the shells a terminal can switch to (cmd, PowerShell, Git Bash, \u2026) \u2014 id + label, active one marked. Pass `terminal` to target a specific one.",
|
|
336
|
+
{ ...TERMINAL_ARG },
|
|
279
337
|
[],
|
|
280
|
-
() => {
|
|
281
|
-
const
|
|
282
|
-
|
|
338
|
+
(args) => {
|
|
339
|
+
const t = need(args);
|
|
340
|
+
if (!t.listShells) return errorResult(`Terminal '${t.id}' has no switchable shells.`);
|
|
341
|
+
const shells = t.listShells();
|
|
342
|
+
const active = t.getShell?.();
|
|
283
343
|
const text = shells.length ? shells.map((s) => `${s.id === active ? "* " : " "}${s.id} \u2014 ${s.label}`).join("\n") : "(none)";
|
|
284
|
-
return textResult(text, { shells, active });
|
|
344
|
+
return textResult(text, { shells, active, terminal: t.id });
|
|
285
345
|
},
|
|
286
346
|
false
|
|
287
347
|
);
|
|
288
348
|
}
|
|
289
|
-
if (
|
|
349
|
+
if (canSetShell) {
|
|
290
350
|
reg(
|
|
291
351
|
"terminal_set_shell",
|
|
292
|
-
"Switch
|
|
293
|
-
{ id: { type: "string", description: "Shell id to switch to." } },
|
|
352
|
+
"Switch a terminal's active shell by id (e.g. 'powershell', 'git-bash'). Call terminal_list_shells first for valid ids. Pass `terminal` to target a specific one.",
|
|
353
|
+
{ ...TERMINAL_ARG, id: { type: "string", description: "Shell id to switch to." } },
|
|
294
354
|
["id"],
|
|
295
355
|
async (args) => {
|
|
356
|
+
const t = need(args);
|
|
357
|
+
if (!t.setShell) return errorResult(`Terminal '${t.id}' can't switch shells.`);
|
|
296
358
|
const id = String(args.id);
|
|
297
|
-
const shells =
|
|
359
|
+
const shells = t.listShells?.();
|
|
298
360
|
if (shells && shells.length && !shells.some((s) => s.id === id)) {
|
|
299
|
-
return errorResult(`Unknown shell '${id}'. Use terminal_list_shells for valid ids.`);
|
|
361
|
+
return errorResult(`Unknown shell '${id}' for ${t.id}. Use terminal_list_shells for valid ids.`);
|
|
300
362
|
}
|
|
301
|
-
await
|
|
302
|
-
return textResult(`Switched shell to ${id}`, { shell: id });
|
|
363
|
+
await t.setShell(id);
|
|
364
|
+
return textResult(`Switched ${t.id} shell to ${id}`, { shell: id, terminal: t.id });
|
|
303
365
|
},
|
|
304
366
|
true,
|
|
305
|
-
(args) => target(`shell:${String(args.id ?? "")}
|
|
367
|
+
(args) => target(`shell:${String(args.id ?? "")}`, resolve(args.terminal)?.id)
|
|
306
368
|
);
|
|
307
369
|
}
|
|
308
370
|
return {
|
|
@@ -316,8 +378,9 @@ function registerTerminalBridge(host, options) {
|
|
|
316
378
|
confirm: (id) => {
|
|
317
379
|
const e = staged.get(id);
|
|
318
380
|
if (e) {
|
|
381
|
+
const t = resolve(e.terminalId);
|
|
319
382
|
staged.delete(id);
|
|
320
|
-
void exec(e.kind, e.data);
|
|
383
|
+
if (t) void exec(t, e.kind, e.data);
|
|
321
384
|
}
|
|
322
385
|
},
|
|
323
386
|
reject: (id) => {
|