@el4cteo/rbx-studio-mcp 0.4.2 → 0.4.5
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 +211 -211
- package/dist/bridge/api.js +3 -0
- package/dist/bridge/api.js.map +1 -1
- package/dist/bridge/failover.js +13 -0
- package/dist/bridge/failover.js.map +1 -1
- package/dist/bridge/remote.js +15 -1
- package/dist/bridge/remote.js.map +1 -1
- package/dist/bridge/rpc.js +35 -6
- package/dist/bridge/rpc.js.map +1 -1
- package/dist/bridge/server.js +55 -7
- package/dist/bridge/server.js.map +1 -1
- package/dist/doctor.js +160 -0
- package/dist/doctor.js.map +1 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/protocol.js.map +1 -1
- package/dist/tools/exec.js +53 -3
- package/dist/tools/exec.js.map +1 -1
- package/dist/tools/generate.js +120 -0
- package/dist/tools/generate.js.map +1 -0
- package/dist/tools/scripts.js +20 -1
- package/dist/tools/scripts.js.map +1 -1
- package/dist/tools/world.js +203 -20
- package/dist/tools/world.js.map +1 -1
- package/package.json +2 -1
- package/plugin/src/Config.luau +1 -1
- package/plugin/src/Console.luau +233 -9
- package/plugin/src/Transport.luau +6 -1
- package/plugin/src/Visuals.luau +52 -1
- package/plugin/src/handlers/Assets.luau +352 -145
- package/plugin/src/handlers/Generate.luau +386 -0
- package/plugin/src/handlers/Geometry.luau +170 -0
- package/plugin/src/handlers/Scripts.luau +79 -1
- package/plugin/src/handlers/Viewport.luau +416 -302
- package/plugin/src/handlers/World.luau +15 -5
- package/plugin/src/init.server.luau +725 -674
- package/scripts/test-bridge.mjs +56 -0
- package/scripts/test-failover.mjs +136 -95
package/README.md
CHANGED
|
@@ -1,211 +1,211 @@
|
|
|
1
|
-
# Roblox Studio MCP
|
|
2
|
-
|
|
3
|
-
Let an AI agent drive Roblox Studio: read your place, edit scripts, run playtests, take screenshots.
|
|
4
|
-
|
|
5
|
-

|
|
6
|
-
|
|
7
|
-
## Install
|
|
8
|
-
|
|
9
|
-
**1. The Studio plugin**
|
|
10
|
-
|
|
11
|
-
```bash
|
|
12
|
-
npx -y @el4cteo/rbx-studio-mcp --install-plugin
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
Or download `StudioMCP.rbxmx` from [Releases](https://github.com/EL4CTEO/rbx-studio-mcp/releases) into your Studio plugins folder.
|
|
16
|
-
|
|
17
|
-
**2. The server**, in whichever client you use:
|
|
18
|
-
|
|
19
|
-
<details>
|
|
20
|
-
<summary><b>Claude Code</b></summary>
|
|
21
|
-
|
|
22
|
-
```bash
|
|
23
|
-
claude mcp add roblox-studio -- npx -y @el4cteo/rbx-studio-mcp
|
|
24
|
-
```
|
|
25
|
-
</details>
|
|
26
|
-
|
|
27
|
-
<details>
|
|
28
|
-
<summary><b>Codex CLI</b></summary>
|
|
29
|
-
|
|
30
|
-
```bash
|
|
31
|
-
codex mcp add roblox-studio -- npx -y @el4cteo/rbx-studio-mcp
|
|
32
|
-
```
|
|
33
|
-
</details>
|
|
34
|
-
|
|
35
|
-
<details>
|
|
36
|
-
<summary><b>Cursor</b> — <code>~/.cursor/mcp.json</code> or <code>.cursor/mcp.json</code></summary>
|
|
37
|
-
|
|
38
|
-
```json
|
|
39
|
-
{
|
|
40
|
-
"mcpServers": {
|
|
41
|
-
"roblox-studio": {
|
|
42
|
-
"command": "npx",
|
|
43
|
-
"args": ["-y", "@el4cteo/rbx-studio-mcp"]
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
```
|
|
48
|
-
</details>
|
|
49
|
-
|
|
50
|
-
<details>
|
|
51
|
-
<summary><b>Claude Desktop</b> — <code>claude_desktop_config.json</code></summary>
|
|
52
|
-
|
|
53
|
-
```json
|
|
54
|
-
{
|
|
55
|
-
"mcpServers": {
|
|
56
|
-
"roblox-studio": {
|
|
57
|
-
"command": "npx",
|
|
58
|
-
"args": ["-y", "@el4cteo/rbx-studio-mcp"]
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
```
|
|
63
|
-
</details>
|
|
64
|
-
|
|
65
|
-
<details>
|
|
66
|
-
<summary><b>Gemini CLI</b> — <code>~/.gemini/settings.json</code></summary>
|
|
67
|
-
|
|
68
|
-
```json
|
|
69
|
-
{
|
|
70
|
-
"mcpServers": {
|
|
71
|
-
"roblox-studio": {
|
|
72
|
-
"command": "npx",
|
|
73
|
-
"args": ["-y", "@el4cteo/rbx-studio-mcp"]
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
```
|
|
78
|
-
</details>
|
|
79
|
-
|
|
80
|
-
<details>
|
|
81
|
-
<summary><b>Windsurf</b> — <code>~/.codeium/windsurf/mcp_config.json</code></summary>
|
|
82
|
-
|
|
83
|
-
```json
|
|
84
|
-
{
|
|
85
|
-
"mcpServers": {
|
|
86
|
-
"roblox-studio": {
|
|
87
|
-
"command": "npx",
|
|
88
|
-
"args": ["-y", "@el4cteo/rbx-studio-mcp"]
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
```
|
|
93
|
-
</details>
|
|
94
|
-
|
|
95
|
-
<details>
|
|
96
|
-
<summary><b>VS Code / Copilot</b> — <code>.vscode/mcp.json</code></summary>
|
|
97
|
-
|
|
98
|
-
```json
|
|
99
|
-
{
|
|
100
|
-
"servers": {
|
|
101
|
-
"roblox-studio": {
|
|
102
|
-
"type": "stdio",
|
|
103
|
-
"command": "npx",
|
|
104
|
-
"args": ["-y", "@el4cteo/rbx-studio-mcp"]
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
```
|
|
109
|
-
</details>
|
|
110
|
-
|
|
111
|
-
<details>
|
|
112
|
-
<summary><b>opencode</b> — <code>opencode.json</code></summary>
|
|
113
|
-
|
|
114
|
-
```json
|
|
115
|
-
{
|
|
116
|
-
"$schema": "https://opencode.ai/config.json",
|
|
117
|
-
"mcp": {
|
|
118
|
-
"roblox-studio": {
|
|
119
|
-
"type": "local",
|
|
120
|
-
"command": ["npx", "-y", "@el4cteo/rbx-studio-mcp"],
|
|
121
|
-
"enabled": true
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
```
|
|
126
|
-
</details>
|
|
127
|
-
|
|
128
|
-
**3.** Open Studio and accept the `127.0.0.1` prompt — the plugin connects automatically. Verify with `studio_status`.
|
|
129
|
-
|
|
130
|
-
**4.** `debug` additionally needs **Debugger Luau API** in File → Beta Features, plus a Studio restart. Nothing else requires it.
|
|
131
|
-
|
|
132
|
-

|
|
133
|
-
|
|
134
|
-
Port defaults to **44755** — change with `--port` or `ROBLOX_STUDIO_MCP_PORT`, and match it in the plugin widget. Loopback only.
|
|
135
|
-
|
|
136
|
-
## Multiple agents
|
|
137
|
-
|
|
138
|
-
Register the server in as many clients as you like. No extra configuration.
|
|
139
|
-
|
|
140
|
-
Each agent keeps its own target, so two agents can work on two open places. Subagents share their parent's target — give them an explicit `studioId` per call.
|
|
141
|
-
|
|
142
|
-
## Tools
|
|
143
|
-
|
|
144
|
-
| | |
|
|
145
|
-
|---|---|
|
|
146
|
-
| **Session** | `studio_status` `list_studios` `set_active_studio` |
|
|
147
|
-
| **Discover** | `tree` `inspect` `find` `api` |
|
|
148
|
-
| **Scripts** | `script_read` `script_edit` `script_grep` `script_create` |
|
|
149
|
-
| **Instances** | `create` `modify` `delete` `move` |
|
|
150
|
-
| **World** | `geometry` `assets` `collision` `undo` |
|
|
151
|
-
| **Run & debug** | `playtest` `execute_luau` `character` `input` `console` `debug` `performance` |
|
|
152
|
-
| **Look** | `screenshot` `viewport` `device` |
|
|
153
|
-
|
|
154
|
-
Gotchas: during a playtest two sessions connect — pass `studioId` explicitly and use the edit session for anything that must persist. `device` emulation persists until `device op="stop"`.
|
|
155
|
-
|
|
156
|
-
## The console panel
|
|
157
|
-
|
|
158
|
-
Every call is logged with its latency, above a trace of the last forty.
|
|
159
|
-
|
|
160
|
-
Hover the tab on the right edge for eight themes — Lattice, Observatory, Orbit, Void, Nebula, Aurora, Phosphor, Blueprint. Each redraws the activity band, not just its colours. Your pick is remembered.
|
|
161
|
-
|
|
162
|
-
## Batching
|
|
163
|
-
|
|
164
|
-
Every write tool takes an array — ten script edits is one call.
|
|
165
|
-
|
|
166
|
-
| tool | takes | cap |
|
|
167
|
-
|---|---|---|
|
|
168
|
-
| `create` | instances, each nesting `children` to any depth | 100 |
|
|
169
|
-
| `modify` | entries, each with an unlimited list of `paths` | 100 entries |
|
|
170
|
-
| `delete` | paths | 200 |
|
|
171
|
-
| `move` | moves | 200 |
|
|
172
|
-
| `script_edit` | edits, across any number of scripts | 50 |
|
|
173
|
-
| `script_create` | scripts | 50 |
|
|
174
|
-
| `inspect` | paths | 50 |
|
|
175
|
-
| `input` | input steps, delivered in order | 40 |
|
|
176
|
-
|
|
177
|
-
`modify` caps *entries*, not targets — one entry can anchor five hundred parts.
|
|
178
|
-
|
|
179
|
-
Each batch is one **Ctrl+Z**, and all-or-nothing: a failed match leaves the place untouched.
|
|
180
|
-
|
|
181
|
-
## Why this one
|
|
182
|
-
|
|
183
|
-
- **Push, not poll.** 50 sequential round trips average 13.6 ms, against 25.8 ms polling — reproduce with `node scripts/latency.mjs --count 50 --compare`.
|
|
184
|
-
- **Safe script edits.** Writes go through `ScriptEditorService:UpdateSourceAsync`, so your unsaved editor buffer survives.
|
|
185
|
-
- **One Ctrl+Z per call.** Every batch is a single undo recording.
|
|
186
|
-
- **Property names are checked** against the running engine's API dump, so a typo comes back as `Anchorred` → `Anchored` instead of a runtime error.
|
|
187
|
-
- **~16k tokens of schema**, cursor-paged and capped, with `detail: concise | standard | full`.
|
|
188
|
-
|
|
189
|
-
Not built here: terrain, AI mesh and material generation.
|
|
190
|
-
|
|
191
|
-
## Security
|
|
192
|
-
|
|
193
|
-
Loopback only. Requires a header a browser cannot set cross-origin, which closes the DNS-rebinding hole. Your experience's "Allow HTTP Requests" setting is untouched.
|
|
194
|
-
|
|
195
|
-
## Development
|
|
196
|
-
|
|
197
|
-
```bash
|
|
198
|
-
npm install
|
|
199
|
-
npm run build # TypeScript -> dist/
|
|
200
|
-
npm run build:plugin # plugin/src -> build/StudioMCP.rbxmx
|
|
201
|
-
npm run install:plugin # build + copy into the Studio plugins folder
|
|
202
|
-
npm test # plugin (Luau) + bridge (Node) tests
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
Needs `luau`, `luau-compile` and `luau-analyze` from [the Luau releases](https://github.com/luau-lang/luau/releases) on `PATH` or in `tools/`.
|
|
206
|
-
|
|
207
|
-
`evals/` holds ten questions answerable only by driving a real Studio session — see [evals/README.md](evals/README.md).
|
|
208
|
-
|
|
209
|
-
## Licence
|
|
210
|
-
|
|
211
|
-
MIT.
|
|
1
|
+
# Roblox Studio MCP
|
|
2
|
+
|
|
3
|
+
Let an AI agent drive Roblox Studio: read your place, edit scripts, run playtests, take screenshots. 30 tools. MIT.
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
**1. The Studio plugin**
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx -y @el4cteo/rbx-studio-mcp --install-plugin
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Or download `StudioMCP.rbxmx` from [Releases](https://github.com/EL4CTEO/rbx-studio-mcp/releases) into your Studio plugins folder.
|
|
16
|
+
|
|
17
|
+
**2. The server**, in whichever client you use:
|
|
18
|
+
|
|
19
|
+
<details>
|
|
20
|
+
<summary><b>Claude Code</b></summary>
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
claude mcp add roblox-studio -- npx -y @el4cteo/rbx-studio-mcp
|
|
24
|
+
```
|
|
25
|
+
</details>
|
|
26
|
+
|
|
27
|
+
<details>
|
|
28
|
+
<summary><b>Codex CLI</b></summary>
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
codex mcp add roblox-studio -- npx -y @el4cteo/rbx-studio-mcp
|
|
32
|
+
```
|
|
33
|
+
</details>
|
|
34
|
+
|
|
35
|
+
<details>
|
|
36
|
+
<summary><b>Cursor</b> — <code>~/.cursor/mcp.json</code> or <code>.cursor/mcp.json</code></summary>
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"mcpServers": {
|
|
41
|
+
"roblox-studio": {
|
|
42
|
+
"command": "npx",
|
|
43
|
+
"args": ["-y", "@el4cteo/rbx-studio-mcp"]
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
</details>
|
|
49
|
+
|
|
50
|
+
<details>
|
|
51
|
+
<summary><b>Claude Desktop</b> — <code>claude_desktop_config.json</code></summary>
|
|
52
|
+
|
|
53
|
+
```json
|
|
54
|
+
{
|
|
55
|
+
"mcpServers": {
|
|
56
|
+
"roblox-studio": {
|
|
57
|
+
"command": "npx",
|
|
58
|
+
"args": ["-y", "@el4cteo/rbx-studio-mcp"]
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
</details>
|
|
64
|
+
|
|
65
|
+
<details>
|
|
66
|
+
<summary><b>Gemini CLI</b> — <code>~/.gemini/settings.json</code></summary>
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"mcpServers": {
|
|
71
|
+
"roblox-studio": {
|
|
72
|
+
"command": "npx",
|
|
73
|
+
"args": ["-y", "@el4cteo/rbx-studio-mcp"]
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
</details>
|
|
79
|
+
|
|
80
|
+
<details>
|
|
81
|
+
<summary><b>Windsurf</b> — <code>~/.codeium/windsurf/mcp_config.json</code></summary>
|
|
82
|
+
|
|
83
|
+
```json
|
|
84
|
+
{
|
|
85
|
+
"mcpServers": {
|
|
86
|
+
"roblox-studio": {
|
|
87
|
+
"command": "npx",
|
|
88
|
+
"args": ["-y", "@el4cteo/rbx-studio-mcp"]
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
</details>
|
|
94
|
+
|
|
95
|
+
<details>
|
|
96
|
+
<summary><b>VS Code / Copilot</b> — <code>.vscode/mcp.json</code></summary>
|
|
97
|
+
|
|
98
|
+
```json
|
|
99
|
+
{
|
|
100
|
+
"servers": {
|
|
101
|
+
"roblox-studio": {
|
|
102
|
+
"type": "stdio",
|
|
103
|
+
"command": "npx",
|
|
104
|
+
"args": ["-y", "@el4cteo/rbx-studio-mcp"]
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
</details>
|
|
110
|
+
|
|
111
|
+
<details>
|
|
112
|
+
<summary><b>opencode</b> — <code>opencode.json</code></summary>
|
|
113
|
+
|
|
114
|
+
```json
|
|
115
|
+
{
|
|
116
|
+
"$schema": "https://opencode.ai/config.json",
|
|
117
|
+
"mcp": {
|
|
118
|
+
"roblox-studio": {
|
|
119
|
+
"type": "local",
|
|
120
|
+
"command": ["npx", "-y", "@el4cteo/rbx-studio-mcp"],
|
|
121
|
+
"enabled": true
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
</details>
|
|
127
|
+
|
|
128
|
+
**3.** Open Studio and accept the `127.0.0.1` prompt — the plugin connects automatically. Verify with `studio_status`.
|
|
129
|
+
|
|
130
|
+
**4.** `debug` additionally needs **Debugger Luau API** in File → Beta Features, plus a Studio restart. Nothing else requires it.
|
|
131
|
+
|
|
132
|
+

|
|
133
|
+
|
|
134
|
+
Port defaults to **44755** — change with `--port` or `ROBLOX_STUDIO_MCP_PORT`, and match it in the plugin widget. Loopback only.
|
|
135
|
+
|
|
136
|
+
## Multiple agents
|
|
137
|
+
|
|
138
|
+
Register the server in as many clients as you like. No extra configuration.
|
|
139
|
+
|
|
140
|
+
Each agent keeps its own target, so two agents can work on two open places. Subagents share their parent's target — give them an explicit `studioId` per call.
|
|
141
|
+
|
|
142
|
+
## Tools
|
|
143
|
+
|
|
144
|
+
| | |
|
|
145
|
+
|---|---|
|
|
146
|
+
| **Session** | `studio_status` `list_studios` `set_active_studio` |
|
|
147
|
+
| **Discover** | `tree` `inspect` `find` `api` |
|
|
148
|
+
| **Scripts** | `script_read` `script_edit` `script_grep` `script_create` |
|
|
149
|
+
| **Instances** | `create` `modify` `delete` `move` |
|
|
150
|
+
| **World** | `geometry` `generate` `assets` `collision` `undo` |
|
|
151
|
+
| **Run & debug** | `playtest` `execute_luau` `character` `input` `console` `debug` `performance` |
|
|
152
|
+
| **Look** | `screenshot` `viewport` `device` |
|
|
153
|
+
|
|
154
|
+
Gotchas: during a playtest two sessions connect — pass `studioId` explicitly and use the edit session for anything that must persist. `device` emulation persists until `device op="stop"`.
|
|
155
|
+
|
|
156
|
+
## The console panel
|
|
157
|
+
|
|
158
|
+
Every call is logged with its latency, above a trace of the last forty.
|
|
159
|
+
|
|
160
|
+
Hover the tab on the right edge for eight themes — Lattice, Observatory, Orbit, Void, Nebula, Aurora, Phosphor, Blueprint. Each redraws the activity band, not just its colours. Your pick is remembered.
|
|
161
|
+
|
|
162
|
+
## Batching
|
|
163
|
+
|
|
164
|
+
Every write tool takes an array — ten script edits is one call.
|
|
165
|
+
|
|
166
|
+
| tool | takes | cap |
|
|
167
|
+
|---|---|---|
|
|
168
|
+
| `create` | instances, each nesting `children` to any depth | 100 |
|
|
169
|
+
| `modify` | entries, each with an unlimited list of `paths` | 100 entries |
|
|
170
|
+
| `delete` | paths | 200 |
|
|
171
|
+
| `move` | moves | 200 |
|
|
172
|
+
| `script_edit` | edits, across any number of scripts | 50 |
|
|
173
|
+
| `script_create` | scripts | 50 |
|
|
174
|
+
| `inspect` | paths | 50 |
|
|
175
|
+
| `input` | input steps, delivered in order | 40 |
|
|
176
|
+
|
|
177
|
+
`modify` caps *entries*, not targets — one entry can anchor five hundred parts.
|
|
178
|
+
|
|
179
|
+
Each batch is one **Ctrl+Z**, and all-or-nothing: a failed match leaves the place untouched.
|
|
180
|
+
|
|
181
|
+
## Why this one
|
|
182
|
+
|
|
183
|
+
- **Push, not poll.** 50 sequential round trips average 13.6 ms, against 25.8 ms polling — reproduce with `node scripts/latency.mjs --count 50 --compare`.
|
|
184
|
+
- **Safe script edits.** Writes go through `ScriptEditorService:UpdateSourceAsync`, so your unsaved editor buffer survives.
|
|
185
|
+
- **One Ctrl+Z per call.** Every batch is a single undo recording.
|
|
186
|
+
- **Property names are checked** against the running engine's API dump, so a typo comes back as `Anchorred` → `Anchored` instead of a runtime error.
|
|
187
|
+
- **~16k tokens of schema**, cursor-paged and capped, with `detail: concise | standard | full`.
|
|
188
|
+
|
|
189
|
+
Not built here: terrain, AI mesh and material generation.
|
|
190
|
+
|
|
191
|
+
## Security
|
|
192
|
+
|
|
193
|
+
Loopback only. Requires a header a browser cannot set cross-origin, which closes the DNS-rebinding hole. Your experience's "Allow HTTP Requests" setting is untouched.
|
|
194
|
+
|
|
195
|
+
## Development
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
npm install
|
|
199
|
+
npm run build # TypeScript -> dist/
|
|
200
|
+
npm run build:plugin # plugin/src -> build/StudioMCP.rbxmx
|
|
201
|
+
npm run install:plugin # build + copy into the Studio plugins folder
|
|
202
|
+
npm test # plugin (Luau) + bridge (Node) tests
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Needs `luau`, `luau-compile` and `luau-analyze` from [the Luau releases](https://github.com/luau-lang/luau/releases) on `PATH` or in `tools/`.
|
|
206
|
+
|
|
207
|
+
`evals/` holds ten questions answerable only by driving a real Studio session — see [evals/README.md](evals/README.md).
|
|
208
|
+
|
|
209
|
+
## Licence
|
|
210
|
+
|
|
211
|
+
MIT.
|
package/dist/bridge/api.js
CHANGED
|
@@ -59,5 +59,8 @@ export class LocalBridge {
|
|
|
59
59
|
async notePlaceName(studioId, placeName, context) {
|
|
60
60
|
this.inner.notePlaceName(studioId, placeName, context);
|
|
61
61
|
}
|
|
62
|
+
describe(about) {
|
|
63
|
+
this.inner.noteClient(this.clientId, { ...about, pid: process.pid });
|
|
64
|
+
}
|
|
62
65
|
}
|
|
63
66
|
//# sourceMappingURL=api.js.map
|
package/dist/bridge/api.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/bridge/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/bridge/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAoEzC;;;;;;;GAOG;AACH;;;;;;;;;;;GAWG;AACH,MAAM,YAAY,GAAG,MAAM,CAAC;AAE5B,MAAM,OAAO,WAAW;IAKO,KAAK;IAJzB,OAAO,GAAG,IAAI,CAAC;IACP,QAAQ,GAAG,UAAU,EAAE,CAAC;IACxB,SAAS,CAAiB;IAE3C,YAA6B,KAAa;qBAAb,KAAK;QAChC,2EAA2E;QAC3E,yEAAyE;QACzE,2EAA2E;QAC3E,yEAAyE;QACzE,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAChC,IAAI,CAAC,SAAS,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,YAAY,CAAC,CAAC;QAClF,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;IACzB,CAAC;IAED,oEAAoE;IACpE,OAAO;QACL,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC9B,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACzC,CAAC;IAED,IAAI,CACF,EAAU,EACV,MAAM,GAA4B,EAAE,EACpC,OAAO,GAA8C,EAAE;QAEvD,wEAAwE;QACxE,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAI,EAAE,EAAE,MAAM,EAAE,EAAE,GAAG,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;IACjF,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrC,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE;YACvB,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC;YAC5C,cAAc,EAAE,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC;SACzD,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,QAAgB;QAC9B,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAChD,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,QAAgB,EAAE,SAAiB,EAAE,OAAgB;QACvE,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,QAAQ,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IACzD,CAAC;IAED,QAAQ,CAAC,KAAwB;QAC/B,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,GAAG,KAAK,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IACvE,CAAC;CACF"}
|
package/dist/bridge/failover.js
CHANGED
|
@@ -34,6 +34,13 @@ export class FailoverBridge {
|
|
|
34
34
|
watch = null;
|
|
35
35
|
claiming = false;
|
|
36
36
|
stopped = false;
|
|
37
|
+
/*
|
|
38
|
+
* Remembered so a handover does not forget who we are. The MCP handshake
|
|
39
|
+
* happens once, at startup; taking the port over happens whenever the holder
|
|
40
|
+
* exits, which can be long after. Without this the new owner would list
|
|
41
|
+
* itself as "unknown" for the rest of the session.
|
|
42
|
+
*/
|
|
43
|
+
about = null;
|
|
37
44
|
constructor(peer, claim) {
|
|
38
45
|
this.peer = peer;
|
|
39
46
|
this.claim = claim;
|
|
@@ -72,6 +79,8 @@ export class FailoverBridge {
|
|
|
72
79
|
await this.peer.goodbye();
|
|
73
80
|
this.claimed = port;
|
|
74
81
|
this.current = port.bridge;
|
|
82
|
+
if (this.about !== null)
|
|
83
|
+
void port.bridge.describe(this.about);
|
|
75
84
|
this.stopWatching();
|
|
76
85
|
// stdout belongs to the MCP transport, so this goes to stderr. Worth
|
|
77
86
|
// saying: a handover explains a Studio reconnect and a lost target
|
|
@@ -136,5 +145,9 @@ export class FailoverBridge {
|
|
|
136
145
|
notePlaceName(studioId, placeName, context) {
|
|
137
146
|
return this.current.notePlaceName(studioId, placeName, context);
|
|
138
147
|
}
|
|
148
|
+
describe(about) {
|
|
149
|
+
this.about = about;
|
|
150
|
+
return this.current.describe(about);
|
|
151
|
+
}
|
|
139
152
|
}
|
|
140
153
|
//# sourceMappingURL=failover.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"failover.js","sourceRoot":"","sources":["../../src/bridge/failover.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAI7C;;;;;;;;;GASG;AACH,MAAM,QAAQ,GAAG,KAAK,CAAC;AAQvB;;;;;;;;;;;;;;;GAeG;AACH,MAAM,OAAO,cAAc;
|
|
1
|
+
{"version":3,"file":"failover.js","sourceRoot":"","sources":["../../src/bridge/failover.ts"],"names":[],"mappings":"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"}
|
package/dist/bridge/remote.js
CHANGED
|
@@ -57,6 +57,16 @@ export class RemoteBridge {
|
|
|
57
57
|
isOwner = false;
|
|
58
58
|
clientId = randomUUID();
|
|
59
59
|
keepalive = null;
|
|
60
|
+
/*
|
|
61
|
+
* Sent on every hello, not once on arrival.
|
|
62
|
+
*
|
|
63
|
+
* The owner can be replaced under us -- see FailoverBridge -- and the process
|
|
64
|
+
* that takes over starts with an empty client list. Repeating the description
|
|
65
|
+
* with each keepalive means a peer re-introduces itself to a new owner within
|
|
66
|
+
* one interval, where announcing once would leave it listed as "unknown" for
|
|
67
|
+
* the rest of the session.
|
|
68
|
+
*/
|
|
69
|
+
about = null;
|
|
60
70
|
constructor(port, owner) {
|
|
61
71
|
this.port = port;
|
|
62
72
|
this.owner = owner;
|
|
@@ -70,12 +80,16 @@ export class RemoteBridge {
|
|
|
70
80
|
/** Best effort: failing to register costs a badge, never a call. */
|
|
71
81
|
async hello() {
|
|
72
82
|
try {
|
|
73
|
-
await this.post("/hello", {}, 5_000);
|
|
83
|
+
await this.post("/hello", { name: this.about?.name, version: this.about?.version, pid: process.pid }, 5_000);
|
|
74
84
|
}
|
|
75
85
|
catch {
|
|
76
86
|
/* ignored */
|
|
77
87
|
}
|
|
78
88
|
}
|
|
89
|
+
describe(about) {
|
|
90
|
+
this.about = about;
|
|
91
|
+
void this.hello();
|
|
92
|
+
}
|
|
79
93
|
async goodbye() {
|
|
80
94
|
if (this.keepalive) {
|
|
81
95
|
clearInterval(this.keepalive);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote.js","sourceRoot":"","sources":["../../src/bridge/remote.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACrE,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAU7C;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,0BAA0B,CAAC;AAEtD;;;;;GAKG;AACH,MAAM,YAAY,GAAG,MAAM,CAAC;AAE5B;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAY;IAC3C,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,oBAAoB,IAAI,WAAW,EAAE;YAChE,OAAO,EAAE,EAAE,CAAC,aAAa,CAAC,EAAE,MAAM,EAAE;YACpC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,KAAK,CAAC;SACnC,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,CAAC,EAAE;YAAE,OAAO,IAAI,CAAC;QAC9B,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAA2B,CAAC;QAC/D,IAAI,IAAI,CAAC,MAAM,KAAK,mBAAmB;YAAE,OAAO,IAAI,CAAC;QACrD,IAAI,IAAI,CAAC,eAAe,KAAK,gBAAgB;YAAE,OAAO,IAAI,CAAC;QAC3D,OAAO,IAAqB,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,OAAO,YAAY;
|
|
1
|
+
{"version":3,"file":"remote.js","sourceRoot":"","sources":["../../src/bridge/remote.ts"],"names":[],"mappings":"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"}
|
package/dist/bridge/rpc.js
CHANGED
|
@@ -67,7 +67,7 @@ export class Bridge {
|
|
|
67
67
|
* one Studio between two agents actually wants to see.
|
|
68
68
|
*/
|
|
69
69
|
clients = new Map();
|
|
70
|
-
/** Called whenever the client
|
|
70
|
+
/** Called whenever the client roster changes, so the plugin can be told. */
|
|
71
71
|
onClientsChanged = null;
|
|
72
72
|
/**
|
|
73
73
|
* Which Studio each connected client chose, keyed by client.
|
|
@@ -154,17 +154,46 @@ export class Bridge {
|
|
|
154
154
|
}
|
|
155
155
|
// --- clients -----------------------------------------------------------
|
|
156
156
|
/** Records a client as present, or refreshes one already known. */
|
|
157
|
-
noteClient(clientId) {
|
|
157
|
+
noteClient(clientId, about) {
|
|
158
158
|
if (clientId.length === 0)
|
|
159
159
|
return;
|
|
160
|
-
const known = this.clients.
|
|
161
|
-
|
|
162
|
-
|
|
160
|
+
const known = this.clients.get(clientId);
|
|
161
|
+
const named = about?.name !== undefined && about.name.length > 0;
|
|
162
|
+
this.clients.set(clientId, {
|
|
163
|
+
lastSeenAt: Date.now(),
|
|
164
|
+
// Set once. See ClientRecord: a keepalive is not a new connection.
|
|
165
|
+
connectedAt: known?.connectedAt ?? Date.now(),
|
|
166
|
+
name: about?.name ?? known?.name ?? "unknown",
|
|
167
|
+
version: about?.version ?? known?.version ?? "",
|
|
168
|
+
pid: about?.pid ?? known?.pid ?? 0,
|
|
169
|
+
});
|
|
170
|
+
// Announced when a client arrives, and when one finally says who it is: the
|
|
171
|
+
// MCP handshake lands after the process has already registered, so the
|
|
172
|
+
// first roster is nameless and the second is the useful one.
|
|
173
|
+
if (known === undefined || (named && known.name !== about?.name)) {
|
|
163
174
|
this.announceClients();
|
|
175
|
+
}
|
|
164
176
|
}
|
|
165
177
|
clientCount() {
|
|
166
178
|
return this.clients.size;
|
|
167
179
|
}
|
|
180
|
+
/**
|
|
181
|
+
* Every client currently sharing this bridge, oldest first.
|
|
182
|
+
*
|
|
183
|
+
* Ordered by arrival rather than by name so the list reads as a history: the
|
|
184
|
+
* one that has been here longest is the one the user most likely started on
|
|
185
|
+
* purpose, and a newcomer appears at the end instead of shuffling the rest.
|
|
186
|
+
*/
|
|
187
|
+
clientList() {
|
|
188
|
+
return [...this.clients.values()]
|
|
189
|
+
.map((client) => ({
|
|
190
|
+
name: client.name,
|
|
191
|
+
version: client.version,
|
|
192
|
+
pid: client.pid,
|
|
193
|
+
connectedAt: client.connectedAt,
|
|
194
|
+
}))
|
|
195
|
+
.sort((left, right) => left.connectedAt - right.connectedAt);
|
|
196
|
+
}
|
|
168
197
|
/**
|
|
169
198
|
* Subscribes to changes in the client count.
|
|
170
199
|
*
|
|
@@ -176,7 +205,7 @@ export class Bridge {
|
|
|
176
205
|
this.onClientsChanged = listener;
|
|
177
206
|
}
|
|
178
207
|
announceClients() {
|
|
179
|
-
this.onClientsChanged?.(this.clients.size);
|
|
208
|
+
this.onClientsChanged?.(this.clients.size, this.clientList());
|
|
180
209
|
}
|
|
181
210
|
/**
|
|
182
211
|
* Records the place name a status call resolved.
|