poi-plugin-mcp 0.2.16 → 0.2.21
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 +290 -201
- package/index.js +3 -0
- package/lib/bridge-controller.js +75 -1
- package/lib/fleet-metrics.js +249 -0
- package/lib/poi-action-events.js +405 -292
- package/lib/poi-api-responses.js +165 -0
- package/lib/poi-auto-interaction-recorder.js +399 -0
- package/lib/poi-data-query.js +254 -0
- package/lib/poi-http-bridge.js +1673 -1320
- package/lib/poi-input.js +366 -302
- package/lib/poi-interaction-recorder.js +1693 -0
- package/lib/poi-telemetry.js +515 -439
- package/lib/poi-webview-runtime.js +1181 -0
- package/lib/settings-view.js +112 -0
- package/lib/settings.js +8 -0
- package/mcp-server.js +542 -456
- package/package.json +3 -3
package/lib/settings-view.js
CHANGED
|
@@ -26,6 +26,8 @@ function renderStatefulSettings(React, controller) {
|
|
|
26
26
|
const [busy, setBusy] = React.useState(false)
|
|
27
27
|
const [message, setMessage] = React.useState('')
|
|
28
28
|
|
|
29
|
+
React.useEffect(() => startStatusPolling(controller, setStatus), [controller])
|
|
30
|
+
|
|
29
31
|
async function run(action, successMessage) {
|
|
30
32
|
setBusy(true)
|
|
31
33
|
setMessage('')
|
|
@@ -58,6 +60,8 @@ function renderStatefulSettings(React, controller) {
|
|
|
58
60
|
port,
|
|
59
61
|
enabled: status.enabled,
|
|
60
62
|
inputEnabled: status.inputEnabled,
|
|
63
|
+
recordingEnabled: status.recordingEnabled,
|
|
64
|
+
debugEvalEnabled: status.debugEvalEnabled,
|
|
61
65
|
}),
|
|
62
66
|
'Port saved',
|
|
63
67
|
),
|
|
@@ -68,6 +72,22 @@ function renderStatefulSettings(React, controller) {
|
|
|
68
72
|
inputEnabled ? 'WebView input enabled' : 'WebView input disabled',
|
|
69
73
|
)
|
|
70
74
|
},
|
|
75
|
+
onRecordingToggle: (event) => {
|
|
76
|
+
const recordingEnabled = event.target.checked
|
|
77
|
+
return run(
|
|
78
|
+
() => controller.applySettings({ recordingEnabled }),
|
|
79
|
+
recordingEnabled ? 'Play recording enabled' : 'Play recording disabled',
|
|
80
|
+
)
|
|
81
|
+
},
|
|
82
|
+
onDebugEvalToggle: (event) => {
|
|
83
|
+
const debugEvalEnabled = event.target.checked
|
|
84
|
+
return run(
|
|
85
|
+
() => controller.applySettings({ debugEvalEnabled }),
|
|
86
|
+
debugEvalEnabled
|
|
87
|
+
? 'Dangerous WebView eval enabled'
|
|
88
|
+
: 'Dangerous WebView eval disabled',
|
|
89
|
+
)
|
|
90
|
+
},
|
|
71
91
|
onToggle: () => run(
|
|
72
92
|
() => (status.running ? controller.stopBridge() : controller.startBridge()),
|
|
73
93
|
status.running ? 'Stopped' : 'Started',
|
|
@@ -75,6 +95,15 @@ function renderStatefulSettings(React, controller) {
|
|
|
75
95
|
})
|
|
76
96
|
}
|
|
77
97
|
|
|
98
|
+
function startStatusPolling(controller, setStatus, options = {}) {
|
|
99
|
+
const setIntervalFn = options.setInterval || setInterval
|
|
100
|
+
const clearIntervalFn = options.clearInterval || clearInterval
|
|
101
|
+
const timer = setIntervalFn(() => {
|
|
102
|
+
setStatus(controller.getStatus())
|
|
103
|
+
}, 1000)
|
|
104
|
+
return () => clearIntervalFn(timer)
|
|
105
|
+
}
|
|
106
|
+
|
|
78
107
|
function renderStaticSettings(e, controller) {
|
|
79
108
|
const status = controller.getStatus()
|
|
80
109
|
const port = status.port
|
|
@@ -94,12 +123,30 @@ function renderStaticSettings(e, controller) {
|
|
|
94
123
|
port: nextPort,
|
|
95
124
|
enabled: status.enabled,
|
|
96
125
|
inputEnabled: status.inputEnabled,
|
|
126
|
+
recordingEnabled: status.recordingEnabled,
|
|
127
|
+
debugEvalEnabled: status.debugEvalEnabled,
|
|
97
128
|
})
|
|
98
129
|
},
|
|
99
130
|
onInputToggle: (event) => controller.applySettings({
|
|
100
131
|
port,
|
|
101
132
|
enabled: status.enabled,
|
|
102
133
|
inputEnabled: event.target.checked,
|
|
134
|
+
recordingEnabled: status.recordingEnabled,
|
|
135
|
+
debugEvalEnabled: status.debugEvalEnabled,
|
|
136
|
+
}),
|
|
137
|
+
onRecordingToggle: (event) => controller.applySettings({
|
|
138
|
+
port,
|
|
139
|
+
enabled: status.enabled,
|
|
140
|
+
inputEnabled: status.inputEnabled,
|
|
141
|
+
recordingEnabled: event.target.checked,
|
|
142
|
+
debugEvalEnabled: status.debugEvalEnabled,
|
|
143
|
+
}),
|
|
144
|
+
onDebugEvalToggle: (event) => controller.applySettings({
|
|
145
|
+
port,
|
|
146
|
+
enabled: status.enabled,
|
|
147
|
+
inputEnabled: status.inputEnabled,
|
|
148
|
+
recordingEnabled: status.recordingEnabled,
|
|
149
|
+
debugEvalEnabled: event.target.checked,
|
|
103
150
|
}),
|
|
104
151
|
onToggle: () => (status.running ? controller.stopBridge() : controller.startBridge()),
|
|
105
152
|
})
|
|
@@ -155,6 +202,57 @@ function renderSettings(e, props) {
|
|
|
155
202
|
props.status.inputEnabled ? 'Enabled' : 'Disabled',
|
|
156
203
|
),
|
|
157
204
|
),
|
|
205
|
+
e('div', { style: styles.row },
|
|
206
|
+
e('label', { style: styles.label, htmlFor: 'poi-mcp-recording-enabled' }, 'Record play'),
|
|
207
|
+
e('input', {
|
|
208
|
+
id: 'poi-mcp-recording-enabled',
|
|
209
|
+
type: 'checkbox',
|
|
210
|
+
checked: props.onPortChange ? props.status.recordingEnabled : undefined,
|
|
211
|
+
defaultChecked: props.onPortChange ? undefined : props.status.recordingEnabled,
|
|
212
|
+
disabled: props.busy,
|
|
213
|
+
onChange: props.onRecordingToggle,
|
|
214
|
+
}),
|
|
215
|
+
e('span', { style: styles.status },
|
|
216
|
+
props.status.recordingLimitReached
|
|
217
|
+
? `Paused: ${
|
|
218
|
+
props.status.recordingLimitReached.message ||
|
|
219
|
+
props.status.recordingLimitReached.reason
|
|
220
|
+
}`
|
|
221
|
+
: props.status.recording
|
|
222
|
+
? 'Recording - splits after 5 min idle'
|
|
223
|
+
: props.status.recordingStarting
|
|
224
|
+
? 'Starting recording session'
|
|
225
|
+
: props.status.recordingArmed
|
|
226
|
+
? props.status.recordingAttached
|
|
227
|
+
? 'Armed - starts on game mouse activity'
|
|
228
|
+
: 'Armed - waiting for game WebView'
|
|
229
|
+
: 'Disabled',
|
|
230
|
+
),
|
|
231
|
+
),
|
|
232
|
+
props.status.recordingSessionDir
|
|
233
|
+
? e('div', { style: styles.note }, `Recording: ${props.status.recordingSessionDir}`)
|
|
234
|
+
: null,
|
|
235
|
+
e('div', { style: styles.warningRow },
|
|
236
|
+
e('label', {
|
|
237
|
+
style: styles.label,
|
|
238
|
+
htmlFor: 'poi-mcp-debug-eval-enabled',
|
|
239
|
+
}, 'Debug eval'),
|
|
240
|
+
e('input', {
|
|
241
|
+
id: 'poi-mcp-debug-eval-enabled',
|
|
242
|
+
type: 'checkbox',
|
|
243
|
+
checked: props.onPortChange ? props.status.debugEvalEnabled : undefined,
|
|
244
|
+
defaultChecked: props.onPortChange
|
|
245
|
+
? undefined
|
|
246
|
+
: props.status.debugEvalEnabled,
|
|
247
|
+
disabled: props.busy,
|
|
248
|
+
onChange: props.onDebugEvalToggle,
|
|
249
|
+
}),
|
|
250
|
+
e('span', { style: styles.warning },
|
|
251
|
+
props.status.debugEvalEnabled
|
|
252
|
+
? 'DANGEROUS: authenticated arbitrary code can run in the game WebView'
|
|
253
|
+
: 'Disabled (recommended)',
|
|
254
|
+
),
|
|
255
|
+
),
|
|
158
256
|
e('div', { style: styles.note },
|
|
159
257
|
'Pi extension defaults to 127.0.0.1:17777; keep this port unless you also update Pi.',
|
|
160
258
|
),
|
|
@@ -189,6 +287,14 @@ const styles = {
|
|
|
189
287
|
display: 'flex',
|
|
190
288
|
gap: 8,
|
|
191
289
|
},
|
|
290
|
+
warningRow: {
|
|
291
|
+
alignItems: 'center',
|
|
292
|
+
display: 'flex',
|
|
293
|
+
gap: 8,
|
|
294
|
+
padding: 8,
|
|
295
|
+
border: '1px solid #cf222e',
|
|
296
|
+
borderRadius: 4,
|
|
297
|
+
},
|
|
192
298
|
label: {
|
|
193
299
|
flex: '0 0 80px',
|
|
194
300
|
fontWeight: 600,
|
|
@@ -202,6 +308,11 @@ const styles = {
|
|
|
202
308
|
status: {
|
|
203
309
|
color: '#59636e',
|
|
204
310
|
},
|
|
311
|
+
warning: {
|
|
312
|
+
color: '#cf222e',
|
|
313
|
+
fontSize: 12,
|
|
314
|
+
fontWeight: 600,
|
|
315
|
+
},
|
|
205
316
|
note: {
|
|
206
317
|
color: '#59636e',
|
|
207
318
|
fontSize: 12,
|
|
@@ -215,4 +326,5 @@ const styles = {
|
|
|
215
326
|
|
|
216
327
|
module.exports = {
|
|
217
328
|
createSettingsClass,
|
|
329
|
+
startStatusPolling,
|
|
218
330
|
}
|
package/lib/settings.js
CHANGED
|
@@ -6,6 +6,8 @@ const DEFAULT_SETTINGS = Object.freeze({
|
|
|
6
6
|
port: 17777,
|
|
7
7
|
enabled: true,
|
|
8
8
|
inputEnabled: false,
|
|
9
|
+
recordingEnabled: false,
|
|
10
|
+
debugEvalEnabled: false,
|
|
9
11
|
})
|
|
10
12
|
|
|
11
13
|
const DEFAULT_SETTINGS_FILE = path.join(os.homedir(), '.poi-mcp', 'settings.json')
|
|
@@ -18,6 +20,12 @@ function normalizeSettings(input = {}) {
|
|
|
18
20
|
inputEnabled: typeof input.inputEnabled === 'boolean'
|
|
19
21
|
? input.inputEnabled
|
|
20
22
|
: DEFAULT_SETTINGS.inputEnabled,
|
|
23
|
+
recordingEnabled: typeof input.recordingEnabled === 'boolean'
|
|
24
|
+
? input.recordingEnabled
|
|
25
|
+
: DEFAULT_SETTINGS.recordingEnabled,
|
|
26
|
+
debugEvalEnabled: typeof input.debugEvalEnabled === 'boolean'
|
|
27
|
+
? input.debugEvalEnabled
|
|
28
|
+
: DEFAULT_SETTINGS.debugEvalEnabled,
|
|
21
29
|
}
|
|
22
30
|
}
|
|
23
31
|
|