@jobshimo/browser-link 0.27.1 → 0.28.1

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 (52) hide show
  1. package/dist/activity/db.d.ts +27 -0
  2. package/dist/activity/db.js +98 -0
  3. package/dist/activity/db.js.map +1 -0
  4. package/dist/activity/extract.d.ts +32 -0
  5. package/dist/activity/extract.js +126 -0
  6. package/dist/activity/extract.js.map +1 -0
  7. package/dist/activity/paths.d.ts +16 -0
  8. package/dist/activity/paths.js +24 -0
  9. package/dist/activity/paths.js.map +1 -0
  10. package/dist/activity/queries.d.ts +100 -0
  11. package/dist/activity/queries.js +286 -0
  12. package/dist/activity/queries.js.map +1 -0
  13. package/dist/activity/types.d.ts +119 -0
  14. package/dist/activity/types.js +41 -0
  15. package/dist/activity/types.js.map +1 -0
  16. package/dist/bridge/ws-bridge.d.ts +18 -1
  17. package/dist/bridge/ws-bridge.js +107 -0
  18. package/dist/bridge/ws-bridge.js.map +1 -1
  19. package/dist/cdp/inpage/deep-query.d.ts +1 -1
  20. package/dist/cdp/inpage/deep-query.js +1 -1
  21. package/dist/cdp/inpage/dom-helpers.d.ts +1 -1
  22. package/dist/cdp/inpage/dom-helpers.js +1 -1
  23. package/dist/cli.js +24 -0
  24. package/dist/cli.js.map +1 -1
  25. package/dist/commands/activity.d.ts +11 -0
  26. package/dist/commands/activity.js +347 -0
  27. package/dist/commands/activity.js.map +1 -0
  28. package/dist/config.d.ts +26 -0
  29. package/dist/config.js.map +1 -1
  30. package/dist/extension/activity.d.ts +201 -0
  31. package/dist/extension/activity.html +662 -0
  32. package/dist/extension/activity.js +555 -0
  33. package/dist/extension/activity.js.map +1 -0
  34. package/dist/extension/background.js +146 -0
  35. package/dist/extension/background.js.map +1 -1
  36. package/dist/extension/inpage/deep-query.d.ts +1 -1
  37. package/dist/extension/inpage/deep-query.js +1 -1
  38. package/dist/extension/inpage/dom-helpers.d.ts +1 -1
  39. package/dist/extension/inpage/dom-helpers.js +1 -1
  40. package/dist/extension/manifest.json +3 -2
  41. package/dist/extension/popup.html +58 -0
  42. package/dist/extension/popup.js +11 -0
  43. package/dist/extension/popup.js.map +1 -1
  44. package/dist/messages.d.ts +80 -2
  45. package/dist/server.js +30 -0
  46. package/dist/server.js.map +1 -1
  47. package/dist/tools/browser-definitions.js +50 -2
  48. package/dist/tools/browser-definitions.js.map +1 -1
  49. package/dist/tools/browser-dispatch.d.ts +34 -7
  50. package/dist/tools/browser-dispatch.js +107 -1
  51. package/dist/tools/browser-dispatch.js.map +1 -1
  52. package/package.json +2 -2
@@ -0,0 +1,662 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>browser-link — Activity</title>
7
+ <style>
8
+ /* Same slate-blue system as popup.html, extended for a window that
9
+ * stays open: a dark variant (this panel is watched for long stretches)
10
+ * and a denser row rhythm than a 320px popup can carry. Only the tokens
11
+ * change between themes — every rule below is written once. */
12
+ :root {
13
+ --brand-from: #5b8def;
14
+ --brand-to: #2a5fd6;
15
+ --brand-ring: rgba(29, 78, 216, 0.32);
16
+
17
+ --bg: #ffffff;
18
+ --bg-soft: #f8fafc;
19
+ --bg-raised: #ffffff;
20
+ --border: #e2e8f0;
21
+ --border-soft: #eef2f7;
22
+
23
+ --text: #0f172a;
24
+ --text-muted: #64748b;
25
+ --text-faint: #94a3b8;
26
+
27
+ --ok-fg: #047857;
28
+ --ok-soft: #ecfdf5;
29
+ --error-fg: #b91c1c;
30
+ --error-soft: #fef2f2;
31
+ --warn-fg: #b45309;
32
+ --warn-soft: #fffbeb;
33
+
34
+ --shadow: 0 1px 0 rgba(15, 23, 42, 0.04), 0 8px 24px rgba(15, 23, 42, 0.06);
35
+ }
36
+
37
+ @media (prefers-color-scheme: dark) {
38
+ :root {
39
+ --bg: #0b1120;
40
+ --bg-soft: #111a2e;
41
+ --bg-raised: #131d33;
42
+ --border: #1e293b;
43
+ --border-soft: #172033;
44
+
45
+ --text: #e2e8f0;
46
+ --text-muted: #94a3b8;
47
+ --text-faint: #64748b;
48
+
49
+ --ok-fg: #34d399;
50
+ --ok-soft: rgba(16, 185, 129, 0.12);
51
+ --error-fg: #f87171;
52
+ --error-soft: rgba(239, 68, 68, 0.12);
53
+ --warn-fg: #fbbf24;
54
+ --warn-soft: rgba(245, 158, 11, 0.12);
55
+
56
+ --shadow: 0 1px 0 rgba(0, 0, 0, 0.3), 0 8px 24px rgba(0, 0, 0, 0.35);
57
+ }
58
+ }
59
+
60
+ * {
61
+ box-sizing: border-box;
62
+ }
63
+
64
+ html,
65
+ body {
66
+ margin: 0;
67
+ padding: 0;
68
+ height: 100%;
69
+ font-family:
70
+ ui-sans-serif,
71
+ -apple-system,
72
+ BlinkMacSystemFont,
73
+ 'Inter',
74
+ 'Segoe UI',
75
+ system-ui,
76
+ sans-serif;
77
+ font-size: 13px;
78
+ color: var(--text);
79
+ background: var(--bg);
80
+ -webkit-font-smoothing: antialiased;
81
+ }
82
+
83
+ body {
84
+ display: flex;
85
+ flex-direction: column;
86
+ }
87
+
88
+ /* ---------- header ---------- */
89
+
90
+ header {
91
+ display: flex;
92
+ align-items: center;
93
+ gap: 12px;
94
+ padding: 14px 18px;
95
+ border-bottom: 1px solid var(--border);
96
+ background: var(--bg-raised);
97
+ flex: 0 0 auto;
98
+ }
99
+
100
+ .mark {
101
+ width: 26px;
102
+ height: 26px;
103
+ border-radius: 8px;
104
+ background: linear-gradient(135deg, var(--brand-from), var(--brand-to));
105
+ box-shadow: 0 0 0 3px var(--brand-ring);
106
+ flex: 0 0 auto;
107
+ }
108
+
109
+ .titles {
110
+ display: flex;
111
+ flex-direction: column;
112
+ gap: 1px;
113
+ margin-right: auto;
114
+ }
115
+
116
+ h1 {
117
+ margin: 0;
118
+ font-size: 14px;
119
+ font-weight: 650;
120
+ letter-spacing: -0.01em;
121
+ }
122
+
123
+ .subtitle {
124
+ font-size: 11.5px;
125
+ color: var(--text-muted);
126
+ }
127
+
128
+ /* ---------- controls ---------- */
129
+
130
+ button,
131
+ select,
132
+ input[type='search'] {
133
+ font: inherit;
134
+ color: var(--text);
135
+ background: var(--bg-soft);
136
+ border: 1px solid var(--border);
137
+ border-radius: 8px;
138
+ padding: 6px 10px;
139
+ cursor: pointer;
140
+ transition:
141
+ border-color 0.12s ease,
142
+ background 0.12s ease;
143
+ }
144
+
145
+ button:hover,
146
+ select:hover {
147
+ border-color: var(--brand-from);
148
+ }
149
+
150
+ button:focus-visible,
151
+ select:focus-visible,
152
+ input:focus-visible {
153
+ outline: 2px solid var(--brand-from);
154
+ outline-offset: 1px;
155
+ }
156
+
157
+ input[type='search'] {
158
+ cursor: text;
159
+ min-width: 190px;
160
+ }
161
+
162
+ button.primary {
163
+ color: #fff;
164
+ background: linear-gradient(135deg, var(--brand-from), var(--brand-to));
165
+ border-color: transparent;
166
+ font-weight: 600;
167
+ }
168
+
169
+ button[aria-pressed='true'] {
170
+ border-color: var(--brand-from);
171
+ background: var(--brand-ring);
172
+ font-weight: 600;
173
+ }
174
+
175
+ .live-dot {
176
+ display: inline-block;
177
+ width: 7px;
178
+ height: 7px;
179
+ border-radius: 50%;
180
+ margin-right: 6px;
181
+ background: var(--text-faint);
182
+ vertical-align: middle;
183
+ }
184
+
185
+ button[aria-pressed='true'] .live-dot {
186
+ background: var(--ok-fg);
187
+ animation: pulse 1.8s ease-in-out infinite;
188
+ }
189
+
190
+ @keyframes pulse {
191
+ 0%,
192
+ 100% {
193
+ opacity: 1;
194
+ }
195
+ 50% {
196
+ opacity: 0.35;
197
+ }
198
+ }
199
+
200
+ @media (prefers-reduced-motion: reduce) {
201
+ button[aria-pressed='true'] .live-dot {
202
+ animation: none;
203
+ }
204
+ }
205
+
206
+ /* ---------- usage strip ---------- */
207
+
208
+ .usage {
209
+ display: flex;
210
+ align-items: center;
211
+ gap: 26px;
212
+ padding: 11px 18px;
213
+ border-bottom: 1px solid var(--border);
214
+ background: var(--bg-raised);
215
+ flex: 0 0 auto;
216
+ }
217
+
218
+ .usage-figure {
219
+ display: flex;
220
+ flex-direction: column;
221
+ gap: 1px;
222
+ line-height: 1.15;
223
+ }
224
+
225
+ .usage-value {
226
+ font-size: 16px;
227
+ font-weight: 650;
228
+ letter-spacing: -0.01em;
229
+ font-variant-numeric: tabular-nums;
230
+ }
231
+
232
+ .usage-label {
233
+ font-size: 10px;
234
+ font-weight: 600;
235
+ letter-spacing: 0.07em;
236
+ text-transform: uppercase;
237
+ color: var(--text-faint);
238
+ }
239
+
240
+ /* Reads as a fuel gauge, because that is the question it answers: how
241
+ close is the trail to the point where it drops its oldest entries. */
242
+ .usage-meter {
243
+ position: relative;
244
+ flex: 1 1 auto;
245
+ max-width: 320px;
246
+ height: 20px;
247
+ border-radius: 999px;
248
+ background: var(--bg-soft);
249
+ border: 1px solid var(--border);
250
+ overflow: hidden;
251
+ }
252
+
253
+ .usage-meter-fill {
254
+ height: 100%;
255
+ width: 0;
256
+ background: linear-gradient(90deg, var(--brand-from), var(--brand-to));
257
+ transition: width 0.3s ease;
258
+ }
259
+
260
+ .usage-meter.warn .usage-meter-fill {
261
+ background: linear-gradient(90deg, #f59e0b, #b45309);
262
+ }
263
+
264
+ .usage-meter-text {
265
+ position: absolute;
266
+ inset: 0;
267
+ display: flex;
268
+ align-items: center;
269
+ justify-content: center;
270
+ font-size: 10.5px;
271
+ font-weight: 600;
272
+ color: var(--text-muted);
273
+ mix-blend-mode: normal;
274
+ }
275
+
276
+ .danger-ghost {
277
+ margin-left: auto;
278
+ color: var(--error-fg);
279
+ border-color: var(--border);
280
+ }
281
+
282
+ .danger-ghost:hover {
283
+ border-color: var(--error-fg);
284
+ background: var(--error-soft);
285
+ }
286
+
287
+ /* ---------- purge panel ---------- */
288
+
289
+ .purge {
290
+ flex: 0 0 auto;
291
+ padding: 12px 18px 14px;
292
+ border-bottom: 1px solid var(--border);
293
+ background: var(--error-soft);
294
+ }
295
+
296
+ .purge-row {
297
+ display: flex;
298
+ align-items: center;
299
+ gap: 8px;
300
+ flex-wrap: wrap;
301
+ }
302
+
303
+ .purge-row label {
304
+ font-size: 11px;
305
+ font-weight: 600;
306
+ letter-spacing: 0.05em;
307
+ text-transform: uppercase;
308
+ color: var(--text-muted);
309
+ }
310
+
311
+ .purge input[type='date'] {
312
+ font: inherit;
313
+ color: var(--text);
314
+ background: var(--bg);
315
+ border: 1px solid var(--border);
316
+ border-radius: 8px;
317
+ padding: 5px 8px;
318
+ }
319
+
320
+ .chip {
321
+ font-size: 11.5px;
322
+ padding: 5px 9px;
323
+ background: var(--bg);
324
+ }
325
+
326
+ .purge-confirm {
327
+ margin-top: 10px;
328
+ display: flex;
329
+ flex-direction: column;
330
+ gap: 8px;
331
+ }
332
+
333
+ .purge-confirm p {
334
+ margin: 0;
335
+ font-size: 12.5px;
336
+ color: var(--text);
337
+ }
338
+
339
+ .purge-actions {
340
+ display: flex;
341
+ gap: 8px;
342
+ }
343
+
344
+ button.danger {
345
+ color: #fff;
346
+ background: var(--error-fg);
347
+ border-color: transparent;
348
+ font-weight: 650;
349
+ }
350
+
351
+ button.danger:disabled {
352
+ opacity: 0.45;
353
+ cursor: not-allowed;
354
+ }
355
+
356
+ button.danger:not(:disabled):hover {
357
+ filter: brightness(1.08);
358
+ }
359
+
360
+ .purge-warning {
361
+ font-size: 11.5px !important;
362
+ color: var(--text-muted) !important;
363
+ }
364
+
365
+ .purge-warning code {
366
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
367
+ font-size: 11px;
368
+ }
369
+
370
+ /* ---------- filter bar ---------- */
371
+
372
+ .filters {
373
+ display: flex;
374
+ align-items: center;
375
+ gap: 8px;
376
+ flex-wrap: wrap;
377
+ padding: 10px 18px;
378
+ border-bottom: 1px solid var(--border);
379
+ background: var(--bg-soft);
380
+ flex: 0 0 auto;
381
+ }
382
+
383
+ .filters .spacer {
384
+ margin-left: auto;
385
+ }
386
+
387
+ .count {
388
+ font-size: 11.5px;
389
+ color: var(--text-muted);
390
+ white-space: nowrap;
391
+ }
392
+
393
+ /* ---------- table ---------- */
394
+
395
+ .scroll {
396
+ flex: 1 1 auto;
397
+ overflow: auto;
398
+ }
399
+
400
+ table {
401
+ width: 100%;
402
+ border-collapse: collapse;
403
+ }
404
+
405
+ thead th {
406
+ position: sticky;
407
+ top: 0;
408
+ z-index: 1;
409
+ text-align: left;
410
+ font-size: 10.5px;
411
+ font-weight: 650;
412
+ letter-spacing: 0.06em;
413
+ text-transform: uppercase;
414
+ color: var(--text-faint);
415
+ background: var(--bg);
416
+ padding: 9px 12px;
417
+ border-bottom: 1px solid var(--border);
418
+ white-space: nowrap;
419
+ }
420
+
421
+ tbody td {
422
+ padding: 8px 12px;
423
+ border-bottom: 1px solid var(--border-soft);
424
+ vertical-align: top;
425
+ }
426
+
427
+ tbody tr:hover {
428
+ background: var(--bg-soft);
429
+ }
430
+
431
+ tbody tr.failed td {
432
+ background: var(--error-soft);
433
+ }
434
+
435
+ .when {
436
+ color: var(--text-muted);
437
+ white-space: nowrap;
438
+ font-variant-numeric: tabular-nums;
439
+ }
440
+
441
+ .agent {
442
+ display: inline-block;
443
+ padding: 1px 7px;
444
+ border-radius: 999px;
445
+ font-size: 11px;
446
+ font-weight: 600;
447
+ background: var(--brand-ring);
448
+ color: var(--text);
449
+ white-space: nowrap;
450
+ }
451
+
452
+ .tool {
453
+ font-weight: 600;
454
+ white-space: nowrap;
455
+ }
456
+
457
+ .target,
458
+ .payload {
459
+ font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
460
+ font-size: 11.5px;
461
+ color: var(--text-muted);
462
+ word-break: break-all;
463
+ }
464
+
465
+ .payload {
466
+ display: block;
467
+ margin-top: 3px;
468
+ color: var(--text-faint);
469
+ }
470
+
471
+ .url {
472
+ display: block;
473
+ margin-top: 3px;
474
+ font-size: 11px;
475
+ color: var(--text-faint);
476
+ word-break: break-all;
477
+ }
478
+
479
+ .outcome {
480
+ white-space: nowrap;
481
+ font-variant-numeric: tabular-nums;
482
+ }
483
+
484
+ .outcome.ok {
485
+ color: var(--ok-fg);
486
+ }
487
+
488
+ .outcome.error {
489
+ color: var(--error-fg);
490
+ font-weight: 600;
491
+ }
492
+
493
+ .flow-tag {
494
+ display: inline-block;
495
+ margin-left: 6px;
496
+ padding: 0 6px;
497
+ border-radius: 999px;
498
+ font-size: 10px;
499
+ font-weight: 600;
500
+ background: var(--warn-soft);
501
+ color: var(--warn-fg);
502
+ }
503
+
504
+ /* ---------- states ---------- */
505
+
506
+ .state {
507
+ display: flex;
508
+ flex-direction: column;
509
+ align-items: center;
510
+ justify-content: center;
511
+ gap: 6px;
512
+ padding: 64px 24px;
513
+ text-align: center;
514
+ color: var(--text-muted);
515
+ }
516
+
517
+ .state strong {
518
+ color: var(--text);
519
+ font-size: 14px;
520
+ }
521
+
522
+ .state code {
523
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
524
+ font-size: 12px;
525
+ background: var(--bg-soft);
526
+ border: 1px solid var(--border);
527
+ border-radius: 6px;
528
+ padding: 1px 6px;
529
+ }
530
+
531
+ footer {
532
+ flex: 0 0 auto;
533
+ display: flex;
534
+ align-items: center;
535
+ gap: 10px;
536
+ padding: 8px 18px;
537
+ border-top: 1px solid var(--border);
538
+ background: var(--bg-raised);
539
+ font-size: 11.5px;
540
+ color: var(--text-muted);
541
+ }
542
+
543
+ .hidden {
544
+ display: none !important;
545
+ }
546
+ </style>
547
+ </head>
548
+ <body>
549
+ <header>
550
+ <div class="mark" aria-hidden="true"></div>
551
+ <div class="titles">
552
+ <h1>Activity</h1>
553
+ <span class="subtitle" id="subtitle">Everything agents did in your browser</span>
554
+ </div>
555
+ <button id="live" type="button" aria-pressed="true" title="Keep the newest actions streaming in">
556
+ <span class="live-dot" aria-hidden="true"></span>Live
557
+ </button>
558
+ <button id="refresh" type="button" title="Reload now">Refresh</button>
559
+ <button id="export" class="primary" type="button" title="Download the visible trail">
560
+ Export
561
+ </button>
562
+ </header>
563
+
564
+ <!-- Size and counts, always on screen. The whole reason this strip is
565
+ permanent rather than tucked behind a menu: a trail that grows without
566
+ the user seeing it grow is the failure mode this feature exists to
567
+ prevent. -->
568
+ <div class="usage" id="usage">
569
+ <div class="usage-figure">
570
+ <span class="usage-value" id="usage-rows">—</span>
571
+ <span class="usage-label">actions</span>
572
+ </div>
573
+ <div class="usage-figure">
574
+ <span class="usage-value" id="usage-size">—</span>
575
+ <span class="usage-label">on disk</span>
576
+ </div>
577
+ <div class="usage-figure">
578
+ <span class="usage-value" id="usage-span">—</span>
579
+ <span class="usage-label">oldest entry</span>
580
+ </div>
581
+ <div class="usage-meter" title="How full the trail is before it starts pruning its oldest entries">
582
+ <div class="usage-meter-fill" id="usage-fill"></div>
583
+ <span class="usage-meter-text" id="usage-cap"></span>
584
+ </div>
585
+ <button id="purge-toggle" type="button" class="danger-ghost">Purge…</button>
586
+ </div>
587
+
588
+ <!-- Purge panel. Collapsed by default; a destructive control that is always
589
+ expanded is a destructive control someone eventually hits by accident. -->
590
+ <div class="purge" id="purge-panel" hidden>
591
+ <div class="purge-row">
592
+ <label for="purge-from">From</label>
593
+ <input id="purge-from" type="date" />
594
+ <label for="purge-to">To</label>
595
+ <input id="purge-to" type="date" />
596
+ <button id="purge-preset-30d" type="button" class="chip">Older than 30 days</button>
597
+ <button id="purge-preset-7d" type="button" class="chip">Older than 7 days</button>
598
+ <button id="purge-preset-all" type="button" class="chip">Everything</button>
599
+ <span class="spacer" style="margin-left: auto"></span>
600
+ <button id="purge-cancel" type="button">Close</button>
601
+ </div>
602
+ <div class="purge-confirm" id="purge-confirm">
603
+ <p id="purge-summary">Pick a range. Leave both dates empty to purge the whole trail.</p>
604
+ <div class="purge-actions">
605
+ <button id="purge-check" type="button">Check how many</button>
606
+ <button id="purge-run" type="button" class="danger" disabled>
607
+ Delete permanently
608
+ </button>
609
+ </div>
610
+ <p class="purge-warning">
611
+ <strong>This cannot be undone.</strong> Deleted rows are gone from
612
+ <code>activity.db</code> — they are not in the recycle bin and not recoverable from an
613
+ earlier export unless you already made one.
614
+ </p>
615
+ </div>
616
+ </div>
617
+
618
+ <div class="filters">
619
+ <select id="filter-agent" aria-label="Filter by agent">
620
+ <option value="">All agents</option>
621
+ </select>
622
+ <select id="filter-tool" aria-label="Filter by tool">
623
+ <option value="">All tools</option>
624
+ </select>
625
+ <button id="filter-failed" type="button" aria-pressed="false" title="Show only actions that errored">
626
+ Failures only
627
+ </button>
628
+ <input id="filter-text" type="search" placeholder="Filter selector, URL or payload…" aria-label="Free-text filter" />
629
+ <button id="filter-reset" type="button">Reset</button>
630
+ <span class="spacer"></span>
631
+ <span class="count" id="count"></span>
632
+ </div>
633
+
634
+ <div class="scroll">
635
+ <table id="table" class="hidden">
636
+ <thead>
637
+ <tr>
638
+ <th scope="col">When</th>
639
+ <th scope="col">Agent</th>
640
+ <th scope="col">Tool</th>
641
+ <th scope="col">Target</th>
642
+ <th scope="col">Result</th>
643
+ </tr>
644
+ </thead>
645
+ <tbody id="rows"></tbody>
646
+ </table>
647
+
648
+ <div class="state" id="state">
649
+ <strong id="state-title">Loading…</strong>
650
+ <span id="state-detail"></span>
651
+ </div>
652
+ </div>
653
+
654
+ <footer>
655
+ <span id="status">—</span>
656
+ <span class="spacer" style="margin-left: auto"></span>
657
+ <span id="hint">Trail is stored locally in activity.db · never uploaded</span>
658
+ </footer>
659
+
660
+ <script type="module" src="activity.js"></script>
661
+ </body>
662
+ </html>