opendevbrowser 0.0.11 → 0.0.15

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.
Files changed (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +289 -28
  3. package/dist/chunk-JVBMT2O5.js +7173 -0
  4. package/dist/chunk-JVBMT2O5.js.map +1 -0
  5. package/dist/cli/index.js +3690 -275
  6. package/dist/cli/index.js.map +1 -1
  7. package/dist/index.js +1080 -2857
  8. package/dist/index.js.map +1 -1
  9. package/dist/opendevbrowser.js +1080 -2857
  10. package/dist/opendevbrowser.js.map +1 -1
  11. package/extension/dist/annotate-content.css +237 -0
  12. package/extension/dist/annotate-content.js +934 -0
  13. package/extension/dist/background.js +1291 -8
  14. package/extension/dist/logging.js +50 -0
  15. package/extension/dist/ops/dom-bridge.js +355 -0
  16. package/extension/dist/ops/ops-runtime.js +1249 -0
  17. package/extension/dist/ops/ops-session-store.js +189 -0
  18. package/extension/dist/ops/redaction.js +52 -0
  19. package/extension/dist/ops/snapshot-builder.js +4 -0
  20. package/extension/dist/ops/snapshot-shared.js +220 -0
  21. package/extension/dist/popup.js +398 -21
  22. package/extension/dist/relay-settings.js +3 -1
  23. package/extension/dist/services/CDPRouter.js +501 -103
  24. package/extension/dist/services/ConnectionManager.js +464 -57
  25. package/extension/dist/services/NativePortManager.js +182 -0
  26. package/extension/dist/services/RelayClient.js +227 -26
  27. package/extension/dist/services/TabManager.js +81 -0
  28. package/extension/dist/services/TargetSessionMap.js +146 -0
  29. package/extension/dist/services/cdp-router-commands.js +203 -0
  30. package/extension/dist/services/url-restrictions.js +41 -0
  31. package/extension/dist/types.js +3 -1
  32. package/extension/icons/icon128.png +0 -0
  33. package/extension/icons/icon16.png +0 -0
  34. package/extension/icons/icon32.png +0 -0
  35. package/extension/icons/icon48.png +0 -0
  36. package/extension/manifest.json +17 -3
  37. package/extension/popup.html +469 -65
  38. package/package.json +2 -2
  39. package/skills/AGENTS.md +34 -61
  40. package/skills/data-extraction/SKILL.md +95 -103
  41. package/skills/form-testing/SKILL.md +75 -82
  42. package/skills/login-automation/SKILL.md +76 -66
  43. package/skills/opendevbrowser-best-practices/SKILL.md +90 -49
  44. package/skills/opendevbrowser-continuity-ledger/SKILL.md +57 -23
  45. package/dist/chunk-R5VUZEUU.js +0 -128
  46. package/dist/chunk-R5VUZEUU.js.map +0 -1
  47. package/extension/dist/popup.jsx +0 -150
@@ -1,108 +1,512 @@
1
1
  <!doctype html>
2
- <html>
2
+ <html lang="en">
3
3
  <head>
4
4
  <meta charset="utf-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <meta name="color-scheme" content="dark" />
6
7
  <title>OpenDevBrowser</title>
7
8
  <style>
9
+ :root {
10
+ color-scheme: dark;
11
+ --bg-deep: #060910;
12
+ --bg-surface: #0b1220;
13
+ --panel: rgba(12, 20, 33, 0.74);
14
+ --panel-strong: rgba(15, 24, 40, 0.86);
15
+ --stroke: rgba(255, 255, 255, 0.12);
16
+ --stroke-strong: rgba(255, 255, 255, 0.2);
17
+ --text: #e8edf6;
18
+ --muted: #9aa6bd;
19
+ --accent: #20d5c6;
20
+ --accent-2: #6ee7ff;
21
+ --accent-soft: rgba(32, 213, 198, 0.22);
22
+ --danger: #f16b4e;
23
+ --shadow: 0 14px 32px rgba(3, 8, 18, 0.55);
24
+ }
25
+
26
+ * {
27
+ box-sizing: border-box;
28
+ }
29
+
8
30
  body {
9
- font-family: Arial, sans-serif;
10
31
  margin: 0;
11
- padding: 12px;
12
- min-width: 280px;
32
+ min-width: 336px;
33
+ position: relative;
34
+ overflow: hidden;
35
+ background:
36
+ radial-gradient(circle at 10% 20%, rgba(32, 213, 198, 0.2), transparent 40%),
37
+ radial-gradient(circle at 90% 0%, rgba(110, 231, 255, 0.2), transparent 35%),
38
+ linear-gradient(160deg, var(--bg-deep) 0%, var(--bg-surface) 100%);
39
+ color: var(--text);
40
+ font-family: "Space Grotesk", "Manrope", "Sora", "Avenir Next", "Segoe UI", sans-serif;
41
+ letter-spacing: 0.01em;
13
42
  }
14
- h1 {
15
- font-size: 14px;
16
- margin: 0 0 8px;
43
+
44
+ body::after {
45
+ content: "";
46
+ position: absolute;
47
+ inset: -40% -10% auto auto;
48
+ width: 220px;
49
+ height: 220px;
50
+ background: radial-gradient(circle, rgba(32, 213, 198, 0.18), transparent 70%);
51
+ filter: blur(20px);
52
+ pointer-events: none;
17
53
  }
18
- .status-container {
54
+
55
+ .app {
56
+ display: flex;
57
+ flex-direction: column;
58
+ gap: 14px;
59
+ padding: 16px;
60
+ animation: rise 400ms ease both;
61
+ }
62
+
63
+ @keyframes rise {
64
+ from {
65
+ opacity: 0;
66
+ transform: translateY(6px);
67
+ }
68
+ to {
69
+ opacity: 1;
70
+ transform: translateY(0);
71
+ }
72
+ }
73
+
74
+ .app-header {
19
75
  display: flex;
20
76
  align-items: center;
21
- gap: 8px;
22
- margin-bottom: 12px;
77
+ justify-content: space-between;
78
+ gap: 12px;
23
79
  }
24
- .status-indicator {
80
+
81
+ .brand {
82
+ display: flex;
83
+ align-items: center;
84
+ gap: 10px;
85
+ }
86
+
87
+ .brand-mark {
25
88
  width: 12px;
26
89
  height: 12px;
90
+ border-radius: 4px;
91
+ background: linear-gradient(135deg, var(--accent) 0%, var(--accent-2) 100%);
92
+ box-shadow: 0 0 14px rgba(32, 213, 198, 0.55);
93
+ }
94
+
95
+ .brand-title {
96
+ font-size: 13px;
97
+ font-weight: 600;
98
+ }
99
+
100
+ .brand-subtitle {
101
+ font-size: 9px;
102
+ color: var(--muted);
103
+ text-transform: uppercase;
104
+ letter-spacing: 0.22em;
105
+ margin-top: 2px;
106
+ }
107
+
108
+ .status-pill {
109
+ display: inline-flex;
110
+ align-items: center;
111
+ gap: 6px;
112
+ padding: 4px 10px;
113
+ border: 1px solid var(--stroke);
114
+ background: rgba(255, 255, 255, 0.06);
115
+ border-radius: 999px;
116
+ font-size: 10px;
117
+ color: var(--muted);
118
+ backdrop-filter: blur(10px);
119
+ transition: border-color 0.2s ease, background-color 0.2s ease, color 0.2s ease;
120
+ }
121
+
122
+ .status-pill.connected {
123
+ color: var(--text);
124
+ border-color: rgba(32, 213, 198, 0.5);
125
+ background: var(--accent-soft);
126
+ }
127
+
128
+ .status-indicator {
129
+ width: 8px;
130
+ height: 8px;
27
131
  border-radius: 50%;
28
- background-color: #dc3545;
29
- transition: background-color 0.3s ease;
132
+ background: var(--danger);
133
+ box-shadow: 0 0 8px rgba(241, 107, 78, 0.4);
134
+ transition: background-color 0.2s ease, box-shadow 0.2s ease;
30
135
  }
136
+
31
137
  .status-indicator.connected {
32
- background-color: #28a745;
138
+ background: var(--accent);
139
+ box-shadow: 0 0 12px rgba(32, 213, 198, 0.55);
33
140
  }
34
- #status {
35
- font-size: 12px;
141
+
142
+ .panel {
143
+ position: relative;
144
+ display: flex;
145
+ flex-direction: column;
146
+ gap: 10px;
147
+ padding: 12px;
148
+ border-radius: 14px;
149
+ border: 1px solid var(--stroke);
150
+ background: var(--panel);
151
+ box-shadow: var(--shadow);
152
+ backdrop-filter: blur(18px) saturate(120%);
36
153
  }
154
+
155
+ .panel::before {
156
+ content: "";
157
+ position: absolute;
158
+ inset: 0;
159
+ border-radius: 14px;
160
+ pointer-events: none;
161
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.12);
162
+ }
163
+
37
164
  label {
38
- font-size: 11px;
165
+ font-size: 10px;
166
+ color: var(--muted);
39
167
  display: block;
40
- margin-bottom: 4px;
168
+ margin-bottom: 6px;
169
+ }
170
+
171
+ input[type="number"],
172
+ input[type="text"] {
173
+ width: 100%;
174
+ padding: 8px 10px;
175
+ border-radius: 10px;
176
+ border: 1px solid rgba(255, 255, 255, 0.12);
177
+ background: var(--panel-strong);
178
+ color: var(--text);
179
+ font-size: 12px;
180
+ outline: none;
181
+ transition: border-color 0.2s ease, box-shadow 0.2s ease;
182
+ }
183
+
184
+ input[type="number"]:focus,
185
+ input[type="text"]:focus {
186
+ border-color: rgba(32, 213, 198, 0.6);
187
+ box-shadow: 0 0 0 2px rgba(32, 213, 198, 0.12);
41
188
  }
42
- label.toggle {
189
+
190
+ input:disabled {
191
+ opacity: 0.6;
192
+ }
193
+
194
+ .row {
43
195
  display: flex;
44
196
  align-items: center;
45
- gap: 6px;
46
- margin-bottom: 10px;
197
+ justify-content: space-between;
198
+ gap: 10px;
199
+ padding-top: 10px;
200
+ }
201
+
202
+ .row + .row {
203
+ border-top: 1px solid rgba(255, 255, 255, 0.06);
47
204
  }
48
- input {
205
+
206
+ .row-title {
207
+ font-size: 12px;
208
+ color: var(--text);
209
+ }
210
+
211
+ .row-subtitle {
212
+ font-size: 10px;
213
+ color: var(--muted);
214
+ margin-top: 2px;
215
+ }
216
+
217
+ .switch {
218
+ position: relative;
219
+ display: inline-flex;
220
+ align-items: center;
221
+ width: 40px;
222
+ height: 22px;
223
+ }
224
+
225
+ .switch input {
226
+ opacity: 0;
227
+ width: 0;
228
+ height: 0;
229
+ }
230
+
231
+ .slider {
232
+ position: absolute;
233
+ inset: 0;
234
+ background: rgba(255, 255, 255, 0.12);
235
+ border: 1px solid rgba(255, 255, 255, 0.16);
236
+ border-radius: 999px;
237
+ transition: background 0.2s ease, border-color 0.2s ease;
238
+ }
239
+
240
+ .slider::before {
241
+ content: "";
242
+ position: absolute;
243
+ width: 16px;
244
+ height: 16px;
245
+ left: 2px;
246
+ top: 2px;
247
+ border-radius: 999px;
248
+ background: #0b111a;
249
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.4);
250
+ transition: transform 0.2s ease;
251
+ }
252
+
253
+ .switch input:checked + .slider {
254
+ background: rgba(32, 213, 198, 0.3);
255
+ border-color: rgba(32, 213, 198, 0.6);
256
+ }
257
+
258
+ .switch input:checked + .slider::before {
259
+ transform: translateX(18px);
260
+ background: #0f1f22;
261
+ box-shadow: 0 0 0 1px rgba(32, 213, 198, 0.4) inset;
262
+ }
263
+
264
+ .primary {
49
265
  width: 100%;
50
- padding: 6px;
51
- margin-bottom: 10px;
52
- border: 1px solid #ccc;
53
- border-radius: 4px;
54
- box-sizing: border-box;
266
+ padding: 10px 12px;
267
+ border: none;
268
+ border-radius: 12px;
269
+ font-size: 12px;
270
+ font-weight: 600;
271
+ letter-spacing: 0.02em;
272
+ color: #081014;
273
+ background: linear-gradient(135deg, var(--accent) 0%, var(--accent-2) 100%);
274
+ cursor: pointer;
275
+ box-shadow: 0 10px 24px rgba(32, 213, 198, 0.28);
276
+ transition: transform 0.15s ease, box-shadow 0.15s ease;
55
277
  }
56
- input[type="checkbox"] {
57
- width: auto;
58
- margin: 0;
59
- padding: 0;
278
+
279
+ .primary:hover {
280
+ transform: translateY(-1px);
281
+ box-shadow: 0 12px 28px rgba(32, 213, 198, 0.34);
60
282
  }
61
- input:disabled {
62
- background-color: #f5f5f5;
63
- color: #999;
283
+
284
+ .primary:active {
285
+ transform: translateY(0);
64
286
  }
65
- button {
287
+
288
+ .secondary {
66
289
  width: 100%;
67
- padding: 8px;
68
- border: 1px solid #222;
69
- background: #111;
70
- color: #fff;
290
+ padding: 9px 10px;
291
+ border-radius: 12px;
292
+ border: 1px solid var(--stroke);
293
+ background: var(--panel-strong);
294
+ color: var(--text);
295
+ font-size: 11px;
296
+ font-weight: 600;
297
+ letter-spacing: 0.02em;
71
298
  cursor: pointer;
72
- border-radius: 4px;
73
- transition: background-color 0.2s ease;
299
+ transition: transform 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
300
+ }
301
+
302
+ .secondary:hover {
303
+ transform: translateY(-1px);
304
+ border-color: rgba(32, 213, 198, 0.4);
305
+ box-shadow: 0 10px 22px rgba(32, 213, 198, 0.12);
306
+ }
307
+
308
+ .secondary:active {
309
+ transform: translateY(0);
74
310
  }
75
- button:hover {
76
- background: #333;
311
+
312
+ .secondary:disabled {
313
+ opacity: 0.6;
314
+ cursor: not-allowed;
315
+ box-shadow: none;
316
+ transform: none;
317
+ }
318
+
319
+ .field-note {
320
+ font-size: 10px;
321
+ color: var(--muted);
322
+ margin-top: 6px;
77
323
  }
78
- .auto-pair-note {
324
+
325
+ .panel-title {
79
326
  font-size: 10px;
80
- color: #666;
81
- margin-top: -6px;
82
- margin-bottom: 10px;
327
+ text-transform: uppercase;
328
+ letter-spacing: 0.24em;
329
+ color: var(--muted);
330
+ }
331
+
332
+ .health-grid {
333
+ display: grid;
334
+ grid-template-columns: repeat(2, minmax(0, 1fr));
335
+ gap: 8px;
336
+ }
337
+
338
+ .health-item {
339
+ padding: 8px 10px;
340
+ border-radius: 10px;
341
+ border: 1px solid var(--stroke);
342
+ background: var(--panel-strong);
343
+ display: flex;
344
+ flex-direction: column;
345
+ gap: 4px;
346
+ }
347
+
348
+ .health-label {
349
+ font-size: 9px;
350
+ text-transform: uppercase;
351
+ letter-spacing: 0.2em;
352
+ color: var(--muted);
353
+ }
354
+
355
+ .health-value {
356
+ font-size: 12px;
357
+ font-weight: 600;
358
+ color: var(--text);
359
+ }
360
+
361
+ .health-value[data-tone="ok"] {
362
+ color: var(--accent);
363
+ }
364
+
365
+ .health-value[data-tone="warn"] {
366
+ color: #f6c56f;
367
+ }
368
+
369
+ .health-value[data-tone="off"] {
370
+ color: var(--muted);
371
+ }
372
+
373
+ .button-row {
374
+ display: grid;
375
+ grid-template-columns: repeat(2, minmax(0, 1fr));
376
+ gap: 10px;
377
+ }
378
+
379
+ @media (prefers-reduced-motion: reduce) {
380
+ * {
381
+ transition: none !important;
382
+ animation: none !important;
383
+ }
83
384
  }
84
385
  </style>
85
386
  </head>
86
387
  <body>
87
- <h1>OpenDevBrowser</h1>
88
- <div class="status-container">
89
- <div id="statusIndicator" class="status-indicator"></div>
90
- <div id="status">Disconnected</div>
388
+ <div class="app">
389
+ <header class="app-header">
390
+ <div class="brand">
391
+ <div class="brand-mark"></div>
392
+ <div>
393
+ <div class="brand-title">OpenDevBrowser</div>
394
+ <div class="brand-subtitle">Relay Extension</div>
395
+ </div>
396
+ </div>
397
+ <div id="statusPill" class="status-pill">
398
+ <div id="statusIndicator" class="status-indicator"></div>
399
+ <div id="status">Disconnected</div>
400
+ </div>
401
+ </header>
402
+
403
+ <section class="panel">
404
+ <div class="field">
405
+ <label for="relayPort">Relay port</label>
406
+ <input id="relayPort" type="number" min="1" max="65535" />
407
+ </div>
408
+
409
+ <div class="row">
410
+ <div>
411
+ <div class="row-title">Auto-connect</div>
412
+ <div class="row-subtitle">Reconnect on browser start</div>
413
+ </div>
414
+ <label class="switch">
415
+ <input id="autoConnect" type="checkbox" />
416
+ <span class="slider"></span>
417
+ </label>
418
+ </div>
419
+
420
+ <div class="row">
421
+ <div>
422
+ <div class="row-title">Native fallback (experimental)</div>
423
+ <div class="row-subtitle">Use native messaging host when relay is down</div>
424
+ </div>
425
+ <label class="switch">
426
+ <input id="nativeEnabled" type="checkbox" />
427
+ <span class="slider"></span>
428
+ </label>
429
+ </div>
430
+
431
+ <div class="row">
432
+ <div>
433
+ <div class="row-title">Auto-pair</div>
434
+ <div class="row-subtitle">Fetch token from running plugin</div>
435
+ </div>
436
+ <label class="switch">
437
+ <input id="autoPair" type="checkbox" />
438
+ <span class="slider"></span>
439
+ </label>
440
+ </div>
441
+
442
+ <div class="row">
443
+ <div>
444
+ <div class="row-title">Require pairing token</div>
445
+ <div class="row-subtitle">Disable only for local dev</div>
446
+ </div>
447
+ <label class="switch">
448
+ <input id="pairingEnabled" type="checkbox" />
449
+ <span class="slider"></span>
450
+ </label>
451
+ </div>
452
+
453
+ <div class="field">
454
+ <label for="pairingToken">Pairing token</label>
455
+ <input id="pairingToken" type="text" placeholder="Enter token or enable auto-pair" />
456
+ <div id="statusNote" class="field-note">Local relay only. Tokens stay on-device.</div>
457
+ </div>
458
+ </section>
459
+
460
+ <section class="panel">
461
+ <div class="panel-title">Diagnostics</div>
462
+ <div class="health-grid">
463
+ <div class="health-item">
464
+ <div class="health-label">Relay</div>
465
+ <div id="healthRelay" class="health-value" data-tone="off">Unknown</div>
466
+ </div>
467
+ <div class="health-item">
468
+ <div class="health-label">Handshake</div>
469
+ <div id="healthHandshake" class="health-value" data-tone="off">Unknown</div>
470
+ </div>
471
+ <div class="health-item">
472
+ <div class="health-label">Annotate</div>
473
+ <div id="healthAnnotation" class="health-value" data-tone="off">Unknown</div>
474
+ </div>
475
+ <div class="health-item">
476
+ <div class="health-label">Injected</div>
477
+ <div id="healthInjected" class="health-value" data-tone="off">Unknown</div>
478
+ </div>
479
+ <div class="health-item">
480
+ <div class="health-label">CDP</div>
481
+ <div id="healthCdp" class="health-value" data-tone="off">Unknown</div>
482
+ </div>
483
+ <div class="health-item">
484
+ <div class="health-label">Pairing</div>
485
+ <div id="healthPairing" class="health-value" data-tone="off">Unknown</div>
486
+ </div>
487
+ <div class="health-item">
488
+ <div class="health-label">Native</div>
489
+ <div id="healthNative" class="health-value" data-tone="off">Unknown</div>
490
+ </div>
491
+ </div>
492
+ <div id="healthNote" class="field-note">Health check pending.</div>
493
+ </section>
494
+
495
+ <section class="panel">
496
+ <div class="panel-title">Annotation</div>
497
+ <div class="field">
498
+ <label for="annotationContext">Request</label>
499
+ <input id="annotationContext" type="text" placeholder="Optional context for this review" />
500
+ <div id="annotationNote" class="field-note">No annotations captured yet.</div>
501
+ </div>
502
+ <div class="button-row">
503
+ <button id="annotationStart" class="secondary">Annotate</button>
504
+ <button id="annotationCopy" class="secondary" disabled>Copy payload</button>
505
+ </div>
506
+ </section>
507
+
508
+ <button id="toggle" class="primary">Connect</button>
91
509
  </div>
92
- <label for="relayPort">Relay port</label>
93
- <input id="relayPort" type="number" min="1" max="65535" />
94
- <label class="toggle" for="autoPair">
95
- <input id="autoPair" type="checkbox" />
96
- Auto-Pair (fetch token from plugin)
97
- </label>
98
- <div class="auto-pair-note">When enabled, token is fetched automatically from running plugin</div>
99
- <label class="toggle" for="pairingEnabled">
100
- <input id="pairingEnabled" type="checkbox" />
101
- Require pairing token
102
- </label>
103
- <label for="pairingToken">Pairing token</label>
104
- <input id="pairingToken" type="text" placeholder="Enter token or enable Auto-Pair" />
105
- <button id="toggle">Connect</button>
106
510
  <script type="module" src="dist/popup.js"></script>
107
511
  </body>
108
512
  </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opendevbrowser",
3
- "version": "0.0.11",
3
+ "version": "0.0.15",
4
4
  "description": "OpenCode plugin for browser automation via CDP with snapshot-refs-actions workflow",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -41,7 +41,7 @@
41
41
  "lint": "eslint \"{src,tests}/**/*.ts\"",
42
42
  "test": "vitest run --coverage",
43
43
  "extension:sync": "node scripts/sync-extension-version.mjs",
44
- "extension:build": "npm run extension:sync && tsc -p extension/tsconfig.json",
44
+ "extension:build": "npm run extension:sync && tsc -p extension/tsconfig.json && node scripts/copy-extension-assets.mjs",
45
45
  "extension:pack": "cd extension && zip -r ../opendevbrowser-extension.zip manifest.json popup.html dist/ icons/",
46
46
  "version:check": "node scripts/verify-versions.mjs",
47
47
  "prepack": "npm run version:check && npm run build && npm run extension:build"