poke-browser 0.2.8
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 +60 -0
- package/cli.mjs +457 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +13 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +23 -0
- package/dist/logger.js.map +1 -0
- package/dist/run.d.ts +7 -0
- package/dist/run.d.ts.map +1 -0
- package/dist/run.js +279 -0
- package/dist/run.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +13 -0
- package/dist/server.js.map +1 -0
- package/dist/tools.d.ts +12 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +707 -0
- package/dist/tools.js.map +1 -0
- package/dist/transport.d.ts +60 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +387 -0
- package/dist/transport.js.map +1 -0
- package/extension/background.js +1998 -0
- package/extension/content.js +1416 -0
- package/extension/icon.png +0 -0
- package/extension/manifest.json +48 -0
- package/extension/offscreen.html +10 -0
- package/extension/offscreen.js +246 -0
- package/extension/popup.html +426 -0
- package/extension/popup.js +205 -0
- package/package.json +56 -0
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<title>poke-browser</title>
|
|
6
|
+
<style>
|
|
7
|
+
:root {
|
|
8
|
+
--bg: #0a0a0a;
|
|
9
|
+
--surface: #111;
|
|
10
|
+
--border: #222;
|
|
11
|
+
--text-primary: #fafafa;
|
|
12
|
+
--text-muted: #888;
|
|
13
|
+
--accent: #7c3aed;
|
|
14
|
+
--accent-muted: rgba(124, 58, 237, 0.35);
|
|
15
|
+
--error: #ef4444;
|
|
16
|
+
--connected: #22c55e;
|
|
17
|
+
--footer-mid: #555;
|
|
18
|
+
--footer-low: #444;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
* {
|
|
22
|
+
box-sizing: border-box;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
body {
|
|
26
|
+
margin: 0;
|
|
27
|
+
padding: 14px;
|
|
28
|
+
width: 320px;
|
|
29
|
+
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
30
|
+
font-size: 13px;
|
|
31
|
+
background: var(--bg);
|
|
32
|
+
color: var(--text-primary);
|
|
33
|
+
-webkit-font-smoothing: antialiased;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.header {
|
|
37
|
+
display: flex;
|
|
38
|
+
align-items: center;
|
|
39
|
+
justify-content: space-between;
|
|
40
|
+
margin-bottom: 14px;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.wordmark {
|
|
44
|
+
font-size: 14px;
|
|
45
|
+
font-weight: 600;
|
|
46
|
+
color: var(--text-primary);
|
|
47
|
+
letter-spacing: -0.02em;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.gh-link {
|
|
51
|
+
display: flex;
|
|
52
|
+
align-items: center;
|
|
53
|
+
justify-content: center;
|
|
54
|
+
width: 32px;
|
|
55
|
+
height: 32px;
|
|
56
|
+
border-radius: 6px;
|
|
57
|
+
color: var(--text-muted);
|
|
58
|
+
transition: color 0.15s ease, background 0.15s ease;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.gh-link:hover {
|
|
62
|
+
color: var(--text-primary);
|
|
63
|
+
background: rgba(255, 255, 255, 0.06);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.gh-link svg {
|
|
67
|
+
width: 18px;
|
|
68
|
+
height: 18px;
|
|
69
|
+
fill: currentColor;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.card {
|
|
73
|
+
background: var(--surface);
|
|
74
|
+
border: 1px solid var(--border);
|
|
75
|
+
border-radius: 8px;
|
|
76
|
+
padding: 12px 14px;
|
|
77
|
+
margin-bottom: 12px;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.status-row {
|
|
81
|
+
display: flex;
|
|
82
|
+
align-items: center;
|
|
83
|
+
gap: 8px;
|
|
84
|
+
margin-bottom: 8px;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.dot {
|
|
88
|
+
width: 8px;
|
|
89
|
+
height: 8px;
|
|
90
|
+
border-radius: 50%;
|
|
91
|
+
flex-shrink: 0;
|
|
92
|
+
background: var(--error);
|
|
93
|
+
box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.25);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.dot.connected {
|
|
97
|
+
background: var(--connected);
|
|
98
|
+
box-shadow: 0 0 0 2px rgba(34, 197, 94, 0.25);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.dot.connecting {
|
|
102
|
+
background: #eab308;
|
|
103
|
+
box-shadow: 0 0 0 2px rgba(234, 179, 8, 0.3);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
#statusText {
|
|
107
|
+
font-weight: 500;
|
|
108
|
+
font-size: 13px;
|
|
109
|
+
color: var(--text-primary);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.port-url {
|
|
113
|
+
font-size: 12px;
|
|
114
|
+
color: var(--text-muted);
|
|
115
|
+
font-variant-numeric: tabular-nums;
|
|
116
|
+
word-break: break-all;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.url-edit-row {
|
|
120
|
+
display: inline-flex;
|
|
121
|
+
align-items: center;
|
|
122
|
+
gap: 6px;
|
|
123
|
+
flex-wrap: wrap;
|
|
124
|
+
max-width: 100%;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
#url-display {
|
|
128
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
129
|
+
font-size: 12px;
|
|
130
|
+
color: var(--text-muted);
|
|
131
|
+
word-break: break-all;
|
|
132
|
+
flex: 1;
|
|
133
|
+
min-width: 0;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
#url-input {
|
|
137
|
+
flex: 1;
|
|
138
|
+
min-width: 0;
|
|
139
|
+
padding: 4px 8px;
|
|
140
|
+
font-size: 12px;
|
|
141
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
142
|
+
background: #1a1a1a;
|
|
143
|
+
border: 1px solid #333;
|
|
144
|
+
border-radius: 4px;
|
|
145
|
+
color: var(--text-primary);
|
|
146
|
+
outline: none;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
#url-input:focus {
|
|
150
|
+
border-color: var(--accent-muted);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.url-icon-btn {
|
|
154
|
+
display: inline-flex;
|
|
155
|
+
align-items: center;
|
|
156
|
+
justify-content: center;
|
|
157
|
+
flex-shrink: 0;
|
|
158
|
+
padding: 2px;
|
|
159
|
+
border: none;
|
|
160
|
+
border-radius: 4px;
|
|
161
|
+
background: transparent;
|
|
162
|
+
color: var(--text-muted);
|
|
163
|
+
cursor: pointer;
|
|
164
|
+
transition: color 0.15s ease, background 0.15s ease;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.url-icon-btn:hover {
|
|
168
|
+
color: var(--text-primary);
|
|
169
|
+
background: rgba(255, 255, 255, 0.06);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.hidden {
|
|
173
|
+
display: none !important;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.toggle-row {
|
|
177
|
+
display: flex;
|
|
178
|
+
align-items: center;
|
|
179
|
+
justify-content: space-between;
|
|
180
|
+
gap: 12px;
|
|
181
|
+
padding: 10px 14px;
|
|
182
|
+
background: var(--surface);
|
|
183
|
+
border: 1px solid var(--border);
|
|
184
|
+
border-radius: 8px;
|
|
185
|
+
margin-bottom: 14px;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.toggle-label {
|
|
189
|
+
font-size: 13px;
|
|
190
|
+
color: var(--text-primary);
|
|
191
|
+
font-weight: 500;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.switch {
|
|
195
|
+
position: relative;
|
|
196
|
+
width: 40px;
|
|
197
|
+
height: 22px;
|
|
198
|
+
flex-shrink: 0;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.switch input {
|
|
202
|
+
opacity: 0;
|
|
203
|
+
width: 0;
|
|
204
|
+
height: 0;
|
|
205
|
+
position: absolute;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.slider {
|
|
209
|
+
position: absolute;
|
|
210
|
+
cursor: pointer;
|
|
211
|
+
inset: 0;
|
|
212
|
+
background: #3f3f46;
|
|
213
|
+
border-radius: 6px;
|
|
214
|
+
transition: background 0.2s ease;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.slider::before {
|
|
218
|
+
content: "";
|
|
219
|
+
position: absolute;
|
|
220
|
+
height: 16px;
|
|
221
|
+
width: 16px;
|
|
222
|
+
left: 3px;
|
|
223
|
+
bottom: 3px;
|
|
224
|
+
background: #fafafa;
|
|
225
|
+
border-radius: 4px;
|
|
226
|
+
transition: transform 0.2s ease;
|
|
227
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.switch input:checked + .slider {
|
|
231
|
+
background: var(--accent);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.switch input:checked + .slider::before {
|
|
235
|
+
transform: translateX(18px);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.switch input:focus-visible + .slider {
|
|
239
|
+
outline: 2px solid var(--accent);
|
|
240
|
+
outline-offset: 2px;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.logs-section {
|
|
244
|
+
border: 1px solid var(--border);
|
|
245
|
+
border-radius: 8px;
|
|
246
|
+
margin-bottom: 14px;
|
|
247
|
+
overflow: hidden;
|
|
248
|
+
background: var(--surface);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.logs-header {
|
|
252
|
+
width: 100%;
|
|
253
|
+
display: flex;
|
|
254
|
+
align-items: center;
|
|
255
|
+
justify-content: space-between;
|
|
256
|
+
padding: 10px 14px;
|
|
257
|
+
margin: 0;
|
|
258
|
+
background: transparent;
|
|
259
|
+
border: none;
|
|
260
|
+
color: var(--text-primary);
|
|
261
|
+
font-size: 13px;
|
|
262
|
+
font-weight: 500;
|
|
263
|
+
font-family: inherit;
|
|
264
|
+
cursor: pointer;
|
|
265
|
+
text-align: left;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.logs-header:hover {
|
|
269
|
+
background: rgba(255, 255, 255, 0.04);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
.logs-chevron {
|
|
273
|
+
display: inline-flex;
|
|
274
|
+
align-items: center;
|
|
275
|
+
justify-content: center;
|
|
276
|
+
color: var(--text-muted);
|
|
277
|
+
transition: transform 0.2s ease;
|
|
278
|
+
flex-shrink: 0;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.logs-section.expanded .logs-chevron {
|
|
282
|
+
transform: rotate(180deg);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.logs-collapse {
|
|
286
|
+
max-height: 0;
|
|
287
|
+
overflow: hidden;
|
|
288
|
+
transition: max-height 0.25s ease;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
.logs-section.expanded .logs-collapse {
|
|
292
|
+
max-height: 200px;
|
|
293
|
+
overflow-y: auto;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
#log-list {
|
|
297
|
+
padding: 0 14px 10px;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
#log-list .log-line {
|
|
301
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
302
|
+
font-size: 11px;
|
|
303
|
+
color: #666;
|
|
304
|
+
border-bottom: 1px solid #1a1a1a;
|
|
305
|
+
padding: 6px 0;
|
|
306
|
+
word-break: break-word;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
#log-list .log-line:last-child {
|
|
310
|
+
border-bottom: none;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.footer {
|
|
314
|
+
text-align: center;
|
|
315
|
+
padding-top: 4px;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
.version {
|
|
319
|
+
font-size: 11px;
|
|
320
|
+
color: var(--footer-mid);
|
|
321
|
+
margin-bottom: 4px;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
.credit {
|
|
325
|
+
font-size: 10px;
|
|
326
|
+
color: var(--footer-low);
|
|
327
|
+
}
|
|
328
|
+
</style>
|
|
329
|
+
</head>
|
|
330
|
+
<body>
|
|
331
|
+
<header class="header">
|
|
332
|
+
<span class="wordmark">poke-browser</span>
|
|
333
|
+
<a
|
|
334
|
+
class="gh-link"
|
|
335
|
+
href="https://github.com/leoakok/poke-browser"
|
|
336
|
+
target="_blank"
|
|
337
|
+
rel="noopener noreferrer"
|
|
338
|
+
title="GitHub"
|
|
339
|
+
aria-label="poke-browser on GitHub"
|
|
340
|
+
>
|
|
341
|
+
<svg viewBox="0 0 98 96" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
|
342
|
+
<path
|
|
343
|
+
fill-rule="evenodd"
|
|
344
|
+
clip-rule="evenodd"
|
|
345
|
+
d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z"
|
|
346
|
+
/>
|
|
347
|
+
</svg>
|
|
348
|
+
</a>
|
|
349
|
+
</header>
|
|
350
|
+
|
|
351
|
+
<section class="card" aria-live="polite">
|
|
352
|
+
<div class="status-row">
|
|
353
|
+
<span class="dot disconnected" id="statusDot" aria-hidden="true"></span>
|
|
354
|
+
<span id="statusText">Disconnected</span>
|
|
355
|
+
</div>
|
|
356
|
+
<div id="disconnected-hint" class="hidden">
|
|
357
|
+
<p style="font-size:11px;color:#888;margin:8px 0 4px;">Start the MCP server:</p>
|
|
358
|
+
<code style="font-size:10px;background:#1a1a1a;border:1px solid #222;border-radius:4px;padding:4px 8px;display:block;color:#a78bfa;font-family:monospace;user-select:all;">npx poke-browser@latest</code>
|
|
359
|
+
</div>
|
|
360
|
+
<div class="url-edit-row" id="urlEditRow">
|
|
361
|
+
<span id="url-display"></span>
|
|
362
|
+
<button type="button" id="url-edit-btn" class="url-icon-btn" aria-label="Edit WebSocket URL" title="Edit URL">
|
|
363
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
|
364
|
+
<path
|
|
365
|
+
d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"
|
|
366
|
+
stroke="currentColor"
|
|
367
|
+
stroke-width="2"
|
|
368
|
+
stroke-linecap="round"
|
|
369
|
+
stroke-linejoin="round"
|
|
370
|
+
/>
|
|
371
|
+
<path
|
|
372
|
+
d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"
|
|
373
|
+
stroke="currentColor"
|
|
374
|
+
stroke-width="2"
|
|
375
|
+
stroke-linecap="round"
|
|
376
|
+
stroke-linejoin="round"
|
|
377
|
+
/>
|
|
378
|
+
</svg>
|
|
379
|
+
</button>
|
|
380
|
+
<input type="text" id="url-input" class="hidden" spellcheck="false" autocomplete="off" aria-label="WebSocket URL" />
|
|
381
|
+
<button type="button" id="url-ok-btn" class="url-icon-btn hidden" aria-label="Save URL" title="Save">
|
|
382
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
|
383
|
+
<polyline points="20 6 9 17 4 12" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
|
384
|
+
</svg>
|
|
385
|
+
</button>
|
|
386
|
+
<button type="button" id="url-cancel-btn" class="url-icon-btn hidden" aria-label="Cancel editing" title="Cancel">
|
|
387
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
|
388
|
+
<line x1="18" y1="6" x2="6" y2="18" stroke="#ef4444" stroke-width="2" stroke-linecap="round" />
|
|
389
|
+
<line x1="6" y1="6" x2="18" y2="18" stroke="#ef4444" stroke-width="2" stroke-linecap="round" />
|
|
390
|
+
</svg>
|
|
391
|
+
</button>
|
|
392
|
+
</div>
|
|
393
|
+
</section>
|
|
394
|
+
|
|
395
|
+
<div class="toggle-row">
|
|
396
|
+
<span class="toggle-label" id="toggleLabel">MCP Connection</span>
|
|
397
|
+
<label class="switch" title="Enable MCP WebSocket bridge">
|
|
398
|
+
<input type="checkbox" id="mcpEnabled" checked />
|
|
399
|
+
<span class="slider"></span>
|
|
400
|
+
</label>
|
|
401
|
+
</div>
|
|
402
|
+
|
|
403
|
+
<div class="logs-section" id="logsSection">
|
|
404
|
+
<button type="button" class="logs-header" id="logsToggle" aria-expanded="false" aria-controls="logsCollapse">
|
|
405
|
+
<span>Logs</span>
|
|
406
|
+
<span class="logs-chevron" aria-hidden="true">
|
|
407
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
408
|
+
<polyline points="6 9 12 15 18 9"></polyline>
|
|
409
|
+
</svg>
|
|
410
|
+
</span>
|
|
411
|
+
</button>
|
|
412
|
+
<div class="logs-collapse" id="logsCollapse">
|
|
413
|
+
<div id="log-list"></div>
|
|
414
|
+
</div>
|
|
415
|
+
</div>
|
|
416
|
+
|
|
417
|
+
<div id="update-notice" style="display:none;font-size:10px;color:#f59e0b;background:#1c1500;border:1px solid #3d2e00;border-radius:4px;padding:4px 8px;margin-top:6px;text-align:center;cursor:pointer;" onclick="window.open('https://www.npmjs.com/package/poke-browser')"></div>
|
|
418
|
+
|
|
419
|
+
<footer class="footer">
|
|
420
|
+
<div class="version" id="version"></div>
|
|
421
|
+
<div class="credit">Built by Poke x Leo</div>
|
|
422
|
+
</footer>
|
|
423
|
+
|
|
424
|
+
<script src="popup.js"></script>
|
|
425
|
+
</body>
|
|
426
|
+
</html>
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
const statusDot = document.getElementById("statusDot");
|
|
2
|
+
const statusText = document.getElementById("statusText");
|
|
3
|
+
const urlDisplay = document.getElementById("url-display");
|
|
4
|
+
const urlEditBtn = document.getElementById("url-edit-btn");
|
|
5
|
+
const urlInput = document.getElementById("url-input");
|
|
6
|
+
const urlOkBtn = document.getElementById("url-ok-btn");
|
|
7
|
+
const urlCancelBtn = document.getElementById("url-cancel-btn");
|
|
8
|
+
const mcpEnabled = document.getElementById("mcpEnabled");
|
|
9
|
+
const versionEl = document.getElementById("version");
|
|
10
|
+
const logsSection = document.getElementById("logsSection");
|
|
11
|
+
const logsToggle = document.getElementById("logsToggle");
|
|
12
|
+
const disconnectedHint = document.getElementById("disconnected-hint");
|
|
13
|
+
|
|
14
|
+
const { version } = chrome.runtime.getManifest();
|
|
15
|
+
|
|
16
|
+
const DEFAULT_PORT = 9009;
|
|
17
|
+
const LOG_LIST_MAX = 50;
|
|
18
|
+
|
|
19
|
+
function checkForUpdates() {
|
|
20
|
+
const currentVersion = chrome.runtime.getManifest().version;
|
|
21
|
+
fetch("https://registry.npmjs.org/poke-browser/latest", {
|
|
22
|
+
signal: AbortSignal.timeout(5000),
|
|
23
|
+
})
|
|
24
|
+
.then((r) => r.json())
|
|
25
|
+
.then((data) => {
|
|
26
|
+
const latestVersion = data.version;
|
|
27
|
+
if (latestVersion && latestVersion !== currentVersion) {
|
|
28
|
+
const updateEl = document.getElementById("update-notice");
|
|
29
|
+
if (updateEl) {
|
|
30
|
+
updateEl.textContent =
|
|
31
|
+
"⚡ v" +
|
|
32
|
+
latestVersion +
|
|
33
|
+
" available — npx poke-browser@latest";
|
|
34
|
+
updateEl.style.display = "block";
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
.catch(() => {});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function normalizePort(value) {
|
|
42
|
+
const n = Number(value);
|
|
43
|
+
if (Number.isFinite(n) && n > 0 && n < 65536) return Math.trunc(n);
|
|
44
|
+
return DEFAULT_PORT;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @param {{ port?: unknown; wsPort?: unknown; wsUrl?: unknown }} stored
|
|
49
|
+
*/
|
|
50
|
+
function resolvedWsUrlString(stored) {
|
|
51
|
+
if (typeof stored.wsUrl === "string" && stored.wsUrl.trim()) {
|
|
52
|
+
return stored.wsUrl.trim();
|
|
53
|
+
}
|
|
54
|
+
const port =
|
|
55
|
+
typeof stored.port === "number"
|
|
56
|
+
? normalizePort(stored.port)
|
|
57
|
+
: normalizePort(stored.wsPort);
|
|
58
|
+
return `ws://localhost:${port}`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function enterEditMode() {
|
|
62
|
+
if (!urlDisplay || !urlEditBtn || !urlInput || !urlOkBtn || !urlCancelBtn) return;
|
|
63
|
+
urlDisplay.classList.add("hidden");
|
|
64
|
+
urlEditBtn.classList.add("hidden");
|
|
65
|
+
urlInput.classList.remove("hidden");
|
|
66
|
+
urlOkBtn.classList.remove("hidden");
|
|
67
|
+
urlCancelBtn.classList.remove("hidden");
|
|
68
|
+
urlInput.value = urlDisplay.textContent ?? "";
|
|
69
|
+
urlInput.focus();
|
|
70
|
+
urlInput.select();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function exitEditMode() {
|
|
74
|
+
if (!urlDisplay || !urlEditBtn || !urlInput || !urlOkBtn || !urlCancelBtn) return;
|
|
75
|
+
urlDisplay.classList.remove("hidden");
|
|
76
|
+
urlEditBtn.classList.remove("hidden");
|
|
77
|
+
urlInput.classList.add("hidden");
|
|
78
|
+
urlOkBtn.classList.add("hidden");
|
|
79
|
+
urlCancelBtn.classList.add("hidden");
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function saveUrlFromInput() {
|
|
83
|
+
if (!urlInput || !urlDisplay) return;
|
|
84
|
+
const newUrl = urlInput.value.trim();
|
|
85
|
+
if (!newUrl) return;
|
|
86
|
+
await chrome.storage.local.set({ wsUrl: newUrl });
|
|
87
|
+
urlDisplay.textContent = newUrl;
|
|
88
|
+
exitEditMode();
|
|
89
|
+
try {
|
|
90
|
+
await chrome.runtime.sendMessage({ action: "reconnect", wsUrl: newUrl });
|
|
91
|
+
} catch {
|
|
92
|
+
/* extension invalidated */
|
|
93
|
+
}
|
|
94
|
+
await syncFromBackgroundStatus();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function applyStatus(status) {
|
|
98
|
+
if (!statusDot || !statusText) return;
|
|
99
|
+
statusDot.classList.remove("connected", "connecting", "disconnected");
|
|
100
|
+
if (status === "connected") {
|
|
101
|
+
statusDot.classList.add("connected");
|
|
102
|
+
statusText.textContent = "Connected";
|
|
103
|
+
disconnectedHint?.classList.add("hidden");
|
|
104
|
+
} else if (status === "connecting") {
|
|
105
|
+
statusDot.classList.add("connecting");
|
|
106
|
+
statusText.textContent = "Connecting…";
|
|
107
|
+
disconnectedHint?.classList.add("hidden");
|
|
108
|
+
} else {
|
|
109
|
+
statusDot.classList.add("disconnected");
|
|
110
|
+
statusText.textContent = "Disconnected";
|
|
111
|
+
disconnectedHint?.classList.remove("hidden");
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async function syncFromBackgroundStatus() {
|
|
116
|
+
try {
|
|
117
|
+
const state = await chrome.runtime.sendMessage({ type: "POKE_GET_STATE" });
|
|
118
|
+
if (state && typeof state.status === "string") {
|
|
119
|
+
applyStatus(state.status);
|
|
120
|
+
}
|
|
121
|
+
} catch {
|
|
122
|
+
applyStatus("disconnected");
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function prependLogLine(text) {
|
|
127
|
+
const list = document.getElementById("log-list");
|
|
128
|
+
if (!list) return;
|
|
129
|
+
const line = document.createElement("div");
|
|
130
|
+
line.className = "log-line";
|
|
131
|
+
line.textContent = text;
|
|
132
|
+
list.insertBefore(line, list.firstChild);
|
|
133
|
+
while (list.children.length > LOG_LIST_MAX) {
|
|
134
|
+
list.removeChild(list.lastChild);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async function load() {
|
|
139
|
+
if (versionEl) {
|
|
140
|
+
versionEl.textContent = `v${version}`;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const stored = await chrome.storage.local.get(["enabled", "port", "wsPort", "wsUrl"]);
|
|
144
|
+
if (urlDisplay) {
|
|
145
|
+
urlDisplay.textContent = resolvedWsUrlString(stored);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const enabled = typeof stored.enabled === "boolean" ? stored.enabled : true;
|
|
149
|
+
if (mcpEnabled) mcpEnabled.checked = enabled;
|
|
150
|
+
|
|
151
|
+
await syncFromBackgroundStatus();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
chrome.runtime.onMessage.addListener((msg) => {
|
|
155
|
+
if (msg?.type === "POKE_STATUS" && typeof msg.status === "string") {
|
|
156
|
+
applyStatus(msg.status);
|
|
157
|
+
}
|
|
158
|
+
if (msg?.type === "POKE_LOG_UPDATE") {
|
|
159
|
+
void syncFromBackgroundStatus();
|
|
160
|
+
}
|
|
161
|
+
if (msg?.type === "log" && typeof msg.message === "string") {
|
|
162
|
+
prependLogLine(msg.message);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
logsToggle?.addEventListener("click", () => {
|
|
167
|
+
const expanded = logsSection?.classList.toggle("expanded");
|
|
168
|
+
logsToggle?.setAttribute("aria-expanded", expanded ? "true" : "false");
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
urlEditBtn?.addEventListener("click", () => {
|
|
172
|
+
enterEditMode();
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
urlCancelBtn?.addEventListener("click", () => {
|
|
176
|
+
exitEditMode();
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
urlOkBtn?.addEventListener("click", () => {
|
|
180
|
+
void saveUrlFromInput();
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
urlInput?.addEventListener("keydown", (e) => {
|
|
184
|
+
if (e.key === "Enter") {
|
|
185
|
+
e.preventDefault();
|
|
186
|
+
void saveUrlFromInput();
|
|
187
|
+
} else if (e.key === "Escape") {
|
|
188
|
+
e.preventDefault();
|
|
189
|
+
exitEditMode();
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
mcpEnabled?.addEventListener("change", async () => {
|
|
194
|
+
const enabled = mcpEnabled.checked;
|
|
195
|
+
await chrome.storage.local.set({ enabled });
|
|
196
|
+
try {
|
|
197
|
+
await chrome.runtime.sendMessage({ action: "setPokeBrowserEnabled", enabled });
|
|
198
|
+
} catch {
|
|
199
|
+
/* extension invalidated */
|
|
200
|
+
}
|
|
201
|
+
await syncFromBackgroundStatus();
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
checkForUpdates();
|
|
205
|
+
void load();
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "poke-browser",
|
|
3
|
+
"version": "0.2.8",
|
|
4
|
+
"description": "MCP server + WebSocket bridge for the poke-browser Chrome extension",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=18"
|
|
8
|
+
},
|
|
9
|
+
"bin": {
|
|
10
|
+
"poke-browser": "./cli.mjs"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist",
|
|
14
|
+
"cli.mjs",
|
|
15
|
+
"README.md",
|
|
16
|
+
"extension"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc",
|
|
20
|
+
"start": "node dist/index.js",
|
|
21
|
+
"start:tunnel": "node cli.mjs",
|
|
22
|
+
"dev": "npx tsx src/index.ts",
|
|
23
|
+
"prepublishOnly": "npm run build",
|
|
24
|
+
"serve": "node ./dist/index.js",
|
|
25
|
+
"inspector": "npx @modelcontextprotocol/inspector node dist/index.js",
|
|
26
|
+
"inspector:dev": "npx @modelcontextprotocol/inspector node cli.mjs",
|
|
27
|
+
"test": "vitest run tests/",
|
|
28
|
+
"test:watch": "vitest tests/",
|
|
29
|
+
"prepack": "cp -r ../extension ./extension 2>/dev/null || true",
|
|
30
|
+
"postpack": "rm -rf ./extension"
|
|
31
|
+
},
|
|
32
|
+
"keywords": [
|
|
33
|
+
"poke",
|
|
34
|
+
"mcp",
|
|
35
|
+
"browser",
|
|
36
|
+
"chrome",
|
|
37
|
+
"automation"
|
|
38
|
+
],
|
|
39
|
+
"license": "MIT",
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
45
|
+
"ws": "^8.18.1",
|
|
46
|
+
"zod": "^3.24.2"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/express": "^5.0.0",
|
|
50
|
+
"@types/node": "^22.13.10",
|
|
51
|
+
"@types/ws": "^8.18.1",
|
|
52
|
+
"tsx": "^4.19.3",
|
|
53
|
+
"typescript": "^5.8.2",
|
|
54
|
+
"vitest": "^3.0.9"
|
|
55
|
+
}
|
|
56
|
+
}
|