@monoes/monobrowse 1.0.0

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 (208) hide show
  1. package/dist/src/browser/action-builder/analyzer.d.ts +11 -0
  2. package/dist/src/browser/action-builder/analyzer.d.ts.map +1 -0
  3. package/dist/src/browser/action-builder/analyzer.js +93 -0
  4. package/dist/src/browser/action-builder/analyzer.js.map +1 -0
  5. package/dist/src/browser/action-builder/types.d.ts +46 -0
  6. package/dist/src/browser/action-builder/types.d.ts.map +1 -0
  7. package/dist/src/browser/action-builder/types.js +2 -0
  8. package/dist/src/browser/action-builder/types.js.map +1 -0
  9. package/dist/src/browser/actions.d.ts +29 -0
  10. package/dist/src/browser/actions.d.ts.map +1 -0
  11. package/dist/src/browser/actions.js +398 -0
  12. package/dist/src/browser/actions.js.map +1 -0
  13. package/dist/src/browser/adapters/gemini.d.ts +3 -0
  14. package/dist/src/browser/adapters/gemini.d.ts.map +1 -0
  15. package/dist/src/browser/adapters/gemini.js +13 -0
  16. package/dist/src/browser/adapters/gemini.js.map +1 -0
  17. package/dist/src/browser/adapters/google.d.ts +3 -0
  18. package/dist/src/browser/adapters/google.d.ts.map +1 -0
  19. package/dist/src/browser/adapters/google.js +17 -0
  20. package/dist/src/browser/adapters/google.js.map +1 -0
  21. package/dist/src/browser/adapters/index.d.ts +15 -0
  22. package/dist/src/browser/adapters/index.d.ts.map +1 -0
  23. package/dist/src/browser/adapters/index.js +18 -0
  24. package/dist/src/browser/adapters/index.js.map +1 -0
  25. package/dist/src/browser/adapters/instagram.d.ts +3 -0
  26. package/dist/src/browser/adapters/instagram.d.ts.map +1 -0
  27. package/dist/src/browser/adapters/instagram.js +14 -0
  28. package/dist/src/browser/adapters/instagram.js.map +1 -0
  29. package/dist/src/browser/adapters/linkedin.d.ts +3 -0
  30. package/dist/src/browser/adapters/linkedin.d.ts.map +1 -0
  31. package/dist/src/browser/adapters/linkedin.js +13 -0
  32. package/dist/src/browser/adapters/linkedin.js.map +1 -0
  33. package/dist/src/browser/adapters/microsoft.d.ts +3 -0
  34. package/dist/src/browser/adapters/microsoft.d.ts.map +1 -0
  35. package/dist/src/browser/adapters/microsoft.js +16 -0
  36. package/dist/src/browser/adapters/microsoft.js.map +1 -0
  37. package/dist/src/browser/adapters/x.d.ts +3 -0
  38. package/dist/src/browser/adapters/x.d.ts.map +1 -0
  39. package/dist/src/browser/adapters/x.js +13 -0
  40. package/dist/src/browser/adapters/x.js.map +1 -0
  41. package/dist/src/browser/batch.d.ts +5 -0
  42. package/dist/src/browser/batch.d.ts.map +1 -0
  43. package/dist/src/browser/batch.js +2 -0
  44. package/dist/src/browser/batch.js.map +1 -0
  45. package/dist/src/browser/browser.d.ts +15 -0
  46. package/dist/src/browser/browser.d.ts.map +1 -0
  47. package/dist/src/browser/browser.js +256 -0
  48. package/dist/src/browser/browser.js.map +1 -0
  49. package/dist/src/browser/cdp.d.ts +18 -0
  50. package/dist/src/browser/cdp.d.ts.map +1 -0
  51. package/dist/src/browser/cdp.js +153 -0
  52. package/dist/src/browser/cdp.js.map +1 -0
  53. package/dist/src/browser/console-log.d.ts +23 -0
  54. package/dist/src/browser/console-log.d.ts.map +1 -0
  55. package/dist/src/browser/console-log.js +104 -0
  56. package/dist/src/browser/console-log.js.map +1 -0
  57. package/dist/src/browser/dashboard/api-types.d.ts +50 -0
  58. package/dist/src/browser/dashboard/api-types.d.ts.map +1 -0
  59. package/dist/src/browser/dashboard/api-types.js +14 -0
  60. package/dist/src/browser/dashboard/api-types.js.map +1 -0
  61. package/dist/src/browser/dashboard/server.d.ts +11 -0
  62. package/dist/src/browser/dashboard/server.d.ts.map +1 -0
  63. package/dist/src/browser/dashboard/server.js +191 -0
  64. package/dist/src/browser/dashboard/server.js.map +1 -0
  65. package/dist/src/browser/dialog.d.ts +12 -0
  66. package/dist/src/browser/dialog.d.ts.map +1 -0
  67. package/dist/src/browser/dialog.js +53 -0
  68. package/dist/src/browser/dialog.js.map +1 -0
  69. package/dist/src/browser/emulation.d.ts +15 -0
  70. package/dist/src/browser/emulation.d.ts.map +1 -0
  71. package/dist/src/browser/emulation.js +62 -0
  72. package/dist/src/browser/emulation.js.map +1 -0
  73. package/dist/src/browser/find.d.ts +22 -0
  74. package/dist/src/browser/find.d.ts.map +1 -0
  75. package/dist/src/browser/find.js +169 -0
  76. package/dist/src/browser/find.js.map +1 -0
  77. package/dist/src/browser/har.d.ts +27 -0
  78. package/dist/src/browser/har.d.ts.map +1 -0
  79. package/dist/src/browser/har.js +161 -0
  80. package/dist/src/browser/har.js.map +1 -0
  81. package/dist/src/browser/index.d.ts +23 -0
  82. package/dist/src/browser/index.d.ts.map +1 -0
  83. package/dist/src/browser/index.js +23 -0
  84. package/dist/src/browser/index.js.map +1 -0
  85. package/dist/src/browser/network.d.ts +32 -0
  86. package/dist/src/browser/network.d.ts.map +1 -0
  87. package/dist/src/browser/network.js +214 -0
  88. package/dist/src/browser/network.js.map +1 -0
  89. package/dist/src/browser/pdf.d.ts +15 -0
  90. package/dist/src/browser/pdf.d.ts.map +1 -0
  91. package/dist/src/browser/pdf.js +27 -0
  92. package/dist/src/browser/pdf.js.map +1 -0
  93. package/dist/src/browser/profiler.d.ts +10 -0
  94. package/dist/src/browser/profiler.d.ts.map +1 -0
  95. package/dist/src/browser/profiler.js +77 -0
  96. package/dist/src/browser/profiler.js.map +1 -0
  97. package/dist/src/browser/record.d.ts +21 -0
  98. package/dist/src/browser/record.d.ts.map +1 -0
  99. package/dist/src/browser/record.js +59 -0
  100. package/dist/src/browser/record.js.map +1 -0
  101. package/dist/src/browser/screenshot.d.ts +15 -0
  102. package/dist/src/browser/screenshot.d.ts.map +1 -0
  103. package/dist/src/browser/screenshot.js +38 -0
  104. package/dist/src/browser/screenshot.js.map +1 -0
  105. package/dist/src/browser/session.d.ts +8 -0
  106. package/dist/src/browser/session.d.ts.map +1 -0
  107. package/dist/src/browser/session.js +91 -0
  108. package/dist/src/browser/session.js.map +1 -0
  109. package/dist/src/browser/snapshot.d.ts +12 -0
  110. package/dist/src/browser/snapshot.d.ts.map +1 -0
  111. package/dist/src/browser/snapshot.js +179 -0
  112. package/dist/src/browser/snapshot.js.map +1 -0
  113. package/dist/src/browser/storage.d.ts +12 -0
  114. package/dist/src/browser/storage.d.ts.map +1 -0
  115. package/dist/src/browser/storage.js +46 -0
  116. package/dist/src/browser/storage.js.map +1 -0
  117. package/dist/src/browser/tabs.d.ts +8 -0
  118. package/dist/src/browser/tabs.d.ts.map +1 -0
  119. package/dist/src/browser/tabs.js +25 -0
  120. package/dist/src/browser/tabs.js.map +1 -0
  121. package/dist/src/browser/trace.d.ts +10 -0
  122. package/dist/src/browser/trace.d.ts.map +1 -0
  123. package/dist/src/browser/trace.js +87 -0
  124. package/dist/src/browser/trace.js.map +1 -0
  125. package/dist/src/browser/types.d.ts +111 -0
  126. package/dist/src/browser/types.d.ts.map +1 -0
  127. package/dist/src/browser/types.js +16 -0
  128. package/dist/src/browser/types.js.map +1 -0
  129. package/dist/src/browser/vitals.d.ts +15 -0
  130. package/dist/src/browser/vitals.d.ts.map +1 -0
  131. package/dist/src/browser/vitals.js +116 -0
  132. package/dist/src/browser/vitals.js.map +1 -0
  133. package/dist/src/browser/wait.d.ts +4 -0
  134. package/dist/src/browser/wait.d.ts.map +1 -0
  135. package/dist/src/browser/wait.js +146 -0
  136. package/dist/src/browser/wait.js.map +1 -0
  137. package/dist/src/browser/workflow/builtin-handlers.d.ts +3 -0
  138. package/dist/src/browser/workflow/builtin-handlers.d.ts.map +1 -0
  139. package/dist/src/browser/workflow/builtin-handlers.js +343 -0
  140. package/dist/src/browser/workflow/builtin-handlers.js.map +1 -0
  141. package/dist/src/browser/workflow/engine.d.ts +10 -0
  142. package/dist/src/browser/workflow/engine.d.ts.map +1 -0
  143. package/dist/src/browser/workflow/engine.js +174 -0
  144. package/dist/src/browser/workflow/engine.js.map +1 -0
  145. package/dist/src/browser/workflow/expression.d.ts +4 -0
  146. package/dist/src/browser/workflow/expression.d.ts.map +1 -0
  147. package/dist/src/browser/workflow/expression.js +89 -0
  148. package/dist/src/browser/workflow/expression.js.map +1 -0
  149. package/dist/src/browser/workflow/index.d.ts +6 -0
  150. package/dist/src/browser/workflow/index.d.ts.map +1 -0
  151. package/dist/src/browser/workflow/index.js +6 -0
  152. package/dist/src/browser/workflow/index.js.map +1 -0
  153. package/dist/src/browser/workflow/store.d.ts +8 -0
  154. package/dist/src/browser/workflow/store.d.ts.map +1 -0
  155. package/dist/src/browser/workflow/store.js +87 -0
  156. package/dist/src/browser/workflow/store.js.map +1 -0
  157. package/dist/src/browser/workflow/types.d.ts +62 -0
  158. package/dist/src/browser/workflow/types.d.ts.map +1 -0
  159. package/dist/src/browser/workflow/types.js +2 -0
  160. package/dist/src/browser/workflow/types.js.map +1 -0
  161. package/dist/src/index.d.ts +7 -0
  162. package/dist/src/index.d.ts.map +1 -0
  163. package/dist/src/index.js +7 -0
  164. package/dist/src/index.js.map +1 -0
  165. package/dist/tsconfig.tsbuildinfo +1 -0
  166. package/package.json +56 -0
  167. package/src/browser/action-builder/analyzer.ts +127 -0
  168. package/src/browser/action-builder/types.ts +17 -0
  169. package/src/browser/actions.ts +518 -0
  170. package/src/browser/adapters/gemini.ts +15 -0
  171. package/src/browser/adapters/google.ts +20 -0
  172. package/src/browser/adapters/index.ts +33 -0
  173. package/src/browser/adapters/instagram.ts +16 -0
  174. package/src/browser/adapters/linkedin.ts +15 -0
  175. package/src/browser/adapters/microsoft.ts +19 -0
  176. package/src/browser/adapters/x.ts +15 -0
  177. package/src/browser/batch.ts +4 -0
  178. package/src/browser/browser.ts +265 -0
  179. package/src/browser/cdp.ts +156 -0
  180. package/src/browser/console-log.ts +120 -0
  181. package/src/browser/dashboard/api-types.ts +71 -0
  182. package/src/browser/dashboard/server.ts +206 -0
  183. package/src/browser/dashboard/ui.html +444 -0
  184. package/src/browser/dialog.ts +63 -0
  185. package/src/browser/emulation.ts +94 -0
  186. package/src/browser/find.ts +225 -0
  187. package/src/browser/har.ts +202 -0
  188. package/src/browser/index.ts +22 -0
  189. package/src/browser/network.ts +244 -0
  190. package/src/browser/pdf.ts +41 -0
  191. package/src/browser/profiler.ts +96 -0
  192. package/src/browser/record.ts +92 -0
  193. package/src/browser/screenshot.ts +67 -0
  194. package/src/browser/session.ts +118 -0
  195. package/src/browser/snapshot.ts +248 -0
  196. package/src/browser/storage.ts +54 -0
  197. package/src/browser/tabs.ts +37 -0
  198. package/src/browser/trace.ts +106 -0
  199. package/src/browser/types.ts +135 -0
  200. package/src/browser/vitals.ts +122 -0
  201. package/src/browser/wait.ts +178 -0
  202. package/src/browser/workflow/builtin-handlers.ts +379 -0
  203. package/src/browser/workflow/engine.ts +202 -0
  204. package/src/browser/workflow/expression.ts +107 -0
  205. package/src/browser/workflow/index.ts +5 -0
  206. package/src/browser/workflow/store.ts +87 -0
  207. package/src/browser/workflow/types.ts +62 -0
  208. package/src/index.ts +6 -0
@@ -0,0 +1,444 @@
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>monomind dashboard</title>
7
+ <style>
8
+ * { margin: 0; padding: 0; box-sizing: border-box; }
9
+ body { background: #0b0b14; color: #ccc; font-family: system-ui,-apple-system,sans-serif; font-size: 13px; height: 100vh; display: flex; flex-direction: column; overflow: hidden; }
10
+
11
+ /* ── top bar ─────────────────────────────────────────────── */
12
+ #topbar {
13
+ background: #12122080;
14
+ border-bottom: 1px solid #1e1e30;
15
+ padding: 0 16px;
16
+ height: 42px;
17
+ display: flex;
18
+ align-items: center;
19
+ gap: 12px;
20
+ flex-shrink: 0;
21
+ backdrop-filter: blur(8px);
22
+ }
23
+ #topbar .logo { font-size: 14px; font-weight: 700; color: #a78bfa; letter-spacing: .5px; margin-right: 6px; }
24
+ #topbar .logo span { color: #555; font-weight: 400; }
25
+ .pill { border-radius: 10px; padding: 1px 8px; font-size: 10px; border: 1px solid; }
26
+ .pill-run { background: #7c3aed22; border-color: #7c3aed55; color: #a78bfa; }
27
+ .pill-done { background: #22c55e15; border-color: #22c55e44; color: #22c55e; }
28
+ .pill-fail { background: #ef444415; border-color: #ef444444; color: #ef4444; }
29
+ #conn { margin-left: auto; font-size: 10px; color: #555; display: flex; align-items: center; gap: 4px; }
30
+ #conn-dot { width: 6px; height: 6px; border-radius: 50%; background: #555; }
31
+
32
+ /* ── layout ──────────────────────────────────────────────── */
33
+ #layout { display: flex; flex: 1; overflow: hidden; }
34
+
35
+ /* ── sidebar ─────────────────────────────────────────────── */
36
+ #sidebar {
37
+ width: 180px;
38
+ background: #0f0f1e;
39
+ border-right: 1px solid #1a1a2a;
40
+ display: flex;
41
+ flex-direction: column;
42
+ flex-shrink: 0;
43
+ padding: 12px 0;
44
+ }
45
+ .nav-section-label {
46
+ font-size: 9px;
47
+ font-weight: 700;
48
+ text-transform: uppercase;
49
+ letter-spacing: 1px;
50
+ color: #3a3a5a;
51
+ padding: 12px 14px 4px;
52
+ }
53
+ .nav-item {
54
+ display: flex;
55
+ align-items: center;
56
+ gap: 8px;
57
+ padding: 6px 14px;
58
+ cursor: pointer;
59
+ color: #666;
60
+ font-size: 12px;
61
+ border-left: 2px solid transparent;
62
+ transition: all .12s;
63
+ user-select: none;
64
+ }
65
+ .nav-item:hover { color: #aaa; background: #ffffff08; }
66
+ .nav-item.active { color: #c4b5fd; border-left-color: #7c3aed; background: #7c3aed12; }
67
+ .nav-item .nav-icon { font-size: 14px; width: 16px; text-align: center; }
68
+ .nav-badge { margin-left: auto; background: #7c3aed33; border-radius: 8px; padding: 0 5px; font-size: 9px; color: #a78bfa; min-width: 16px; text-align: center; }
69
+ .nav-badge.green { background: #22c55e22; color: #22c55e; }
70
+
71
+ /* ── main panel ──────────────────────────────────────────── */
72
+ #main { flex: 1; overflow-y: auto; display: flex; flex-direction: column; }
73
+ .panel { display: none; flex: 1; flex-direction: column; }
74
+ .panel.active { display: flex; }
75
+ .panel-header {
76
+ padding: 14px 18px 10px;
77
+ border-bottom: 1px solid #1a1a2a;
78
+ display: flex;
79
+ align-items: center;
80
+ gap: 10px;
81
+ flex-shrink: 0;
82
+ }
83
+ .panel-title { font-size: 14px; font-weight: 600; color: #ddd; }
84
+ .panel-subtitle { color: #555; font-size: 11px; margin-left: auto; }
85
+
86
+ /* ── workflow cards ──────────────────────────────────────── */
87
+ #wf-cards { padding: 12px; display: grid; gap: 10px; }
88
+ .card {
89
+ background: #131320;
90
+ border: 1px solid #7c3aed30;
91
+ border-radius: 8px;
92
+ padding: 12px 14px;
93
+ transition: border-color .2s;
94
+ }
95
+ .card:hover { border-color: #7c3aed66; }
96
+ .card.done { background: #0e0e1c; border-color: #1e1e30; opacity: .7; }
97
+ .card.done:hover { opacity: .9; border-color: #2a2a40; }
98
+ .card-header { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
99
+ .card-title { color: #e2e2f0; font-weight: 600; font-size: 13px; flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
100
+ .status-badge { font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: .5px; border-radius: 4px; padding: 2px 6px; border: 1px solid; flex-shrink: 0; }
101
+ .s-running { background: #7c3aed18; border-color: #7c3aed55; color: #a78bfa; }
102
+ .s-completed { background: #22c55e15; border-color: #22c55e44; color: #22c55e; }
103
+ .s-failed { background: #ef444415; border-color: #ef444444; color: #ef4444; }
104
+ .s-stopped { background: #f59e0b15; border-color: #f59e0b44; color: #f59e0b; }
105
+ .card-meta { display: flex; gap: 14px; font-size: 10px; color: #444; margin-bottom: 6px; align-items: center; }
106
+ .card-meta .mitem { display: flex; align-items: center; gap: 3px; }
107
+ .stop-btn { margin-left: auto; background: #ef444415; border: 1px solid #ef444433; color: #ef4444; border-radius: 4px; padding: 2px 8px; font-size: 10px; cursor: pointer; transition: background .15s; }
108
+ .stop-btn:hover { background: #ef444430; }
109
+ .progress-track { height: 2px; background: #1a1a2a; border-radius: 2px; overflow: hidden; margin-bottom: 6px; }
110
+ .progress-fill { height: 100%; background: linear-gradient(90deg, #7c3aed, #a78bfa); border-radius: 2px; transition: width .4s; }
111
+ .log-area { background: #09090f; border-radius: 4px; padding: 5px 8px; font-family: monospace; font-size: 10px; color: #555; line-height: 1.6; max-height: 72px; overflow: hidden; }
112
+ .log-ts { color: #2a2a40; }
113
+ .log-ok { color: #22c55e88; }
114
+ .log-err { color: #ef444488; }
115
+ .log-info { color: #3b82f688; }
116
+
117
+ /* ── empty state ─────────────────────────────────────────── */
118
+ .empty {
119
+ flex: 1;
120
+ display: flex;
121
+ flex-direction: column;
122
+ align-items: center;
123
+ justify-content: center;
124
+ color: #333;
125
+ gap: 8px;
126
+ padding: 40px;
127
+ }
128
+ .empty-icon { font-size: 32px; }
129
+ .empty-title { color: #555; font-size: 14px; font-weight: 600; }
130
+ .empty-sub { font-size: 11px; text-align: center; line-height: 1.6; }
131
+
132
+ /* ── agents panel ────────────────────────────────────────── */
133
+ #agents-panel .placeholder,
134
+ #memory-panel .placeholder,
135
+ #sessions-panel .placeholder {
136
+ flex: 1;
137
+ display: flex;
138
+ flex-direction: column;
139
+ align-items: center;
140
+ justify-content: center;
141
+ color: #2a2a3a;
142
+ gap: 8px;
143
+ font-size: 12px;
144
+ }
145
+ #agents-panel .placeholder .big { font-size: 28px; }
146
+ </style>
147
+ </head>
148
+ <body>
149
+
150
+ <!-- ── top bar ─────────────────────────────────────── -->
151
+ <div id="topbar">
152
+ <span class="logo">monomind <span>dashboard</span></span>
153
+ <span class="pill pill-run" id="p-run">● 0</span>
154
+ <span class="pill pill-done" id="p-done">✓ 0</span>
155
+ <span class="pill pill-fail" id="p-fail">✗ 0</span>
156
+ <span id="conn">
157
+ <span id="conn-dot"></span>
158
+ <span id="conn-label">connecting…</span>
159
+ </span>
160
+ </div>
161
+
162
+ <!-- ── layout ──────────────────────────────────────── -->
163
+ <div id="layout">
164
+
165
+ <!-- sidebar -->
166
+ <nav id="sidebar">
167
+ <div class="nav-section-label">Project</div>
168
+ <div class="nav-item active" data-panel="workflows">
169
+ <span class="nav-icon">⚡</span> Workflows
170
+ <span class="nav-badge" id="nb-run">0</span>
171
+ </div>
172
+
173
+ <div class="nav-section-label" style="margin-top:8px">System</div>
174
+ <div class="nav-item" data-panel="agents">
175
+ <span class="nav-icon">🤖</span> Agents
176
+ </div>
177
+ <div class="nav-item" data-panel="memory">
178
+ <span class="nav-icon">🧠</span> Memory
179
+ </div>
180
+ <div class="nav-item" data-panel="sessions">
181
+ <span class="nav-icon">🔗</span> Sessions
182
+ </div>
183
+ </nav>
184
+
185
+ <!-- main -->
186
+ <div id="main">
187
+
188
+ <!-- Workflows panel -->
189
+ <div class="panel active" id="wf-panel">
190
+ <div class="panel-header">
191
+ <span class="panel-title">Workflow Runs</span>
192
+ <span class="panel-subtitle" id="wf-subtitle">0 runs</span>
193
+ </div>
194
+ <div id="wf-cards"></div>
195
+ <div class="empty" id="wf-empty">
196
+ <span class="empty-icon">⚡</span>
197
+ <span class="empty-title">No workflow runs yet</span>
198
+ <span class="empty-sub">Run a workflow with<br><code style="color:#7c3aed">npx monomind browse workflow run &lt;name&gt;</code></span>
199
+ </div>
200
+ </div>
201
+
202
+ <!-- Agents panel -->
203
+ <div class="panel" id="agents-panel">
204
+ <div class="panel-header"><span class="panel-title">Agents</span></div>
205
+ <div class="placeholder">
206
+ <span class="big">🤖</span>
207
+ <span>Agent monitoring coming soon</span>
208
+ </div>
209
+ </div>
210
+
211
+ <!-- Memory panel -->
212
+ <div class="panel" id="memory-panel">
213
+ <div class="panel-header"><span class="panel-title">Memory</span></div>
214
+ <div class="placeholder">
215
+ <span class="big">🧠</span>
216
+ <span>Memory browser coming soon</span>
217
+ </div>
218
+ </div>
219
+
220
+ <!-- Sessions panel -->
221
+ <div class="panel" id="sessions-panel">
222
+ <div class="panel-header"><span class="panel-title">Sessions</span></div>
223
+ <div class="placeholder">
224
+ <span class="big">🔗</span>
225
+ <span>Session viewer coming soon</span>
226
+ </div>
227
+ </div>
228
+
229
+ </div>
230
+ </div>
231
+
232
+ <script>
233
+ // ── nav ────────────────────────────────────────────────────
234
+ document.querySelectorAll('.nav-item').forEach(function(item) {
235
+ item.addEventListener('click', function() {
236
+ var target = item.getAttribute('data-panel');
237
+ document.querySelectorAll('.nav-item').forEach(function(n) { n.classList.remove('active'); });
238
+ item.classList.add('active');
239
+ document.querySelectorAll('.panel').forEach(function(p) { p.classList.remove('active'); });
240
+ var panel = document.getElementById(target === 'workflows' ? 'wf-panel' : target + '-panel');
241
+ if (panel) panel.classList.add('active');
242
+ });
243
+ });
244
+
245
+ // ── state ──────────────────────────────────────────────────
246
+ var runs = {};
247
+ var runLogs = {};
248
+
249
+ function ts(ms) {
250
+ return new Date(ms).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' });
251
+ }
252
+ function esc(s) {
253
+ return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
254
+ }
255
+
256
+ function updateHeader() {
257
+ var running = 0, done = 0, fail = 0;
258
+ for (var id in runs) {
259
+ var s = runs[id].status;
260
+ if (s === 'running') running++;
261
+ else if (s === 'completed') done++;
262
+ else if (s === 'failed') fail++;
263
+ }
264
+ document.getElementById('p-run').textContent = '● ' + running;
265
+ document.getElementById('p-done').textContent = '✓ ' + done;
266
+ document.getElementById('p-fail').textContent = '✗ ' + fail;
267
+ document.getElementById('nb-run').textContent = String(running || Object.keys(runs).length);
268
+ document.getElementById('nb-run').className = 'nav-badge' + (running ? '' : ' green');
269
+ var total = Object.keys(runs).length;
270
+ document.getElementById('wf-subtitle').textContent = total + (total === 1 ? ' run' : ' runs');
271
+ }
272
+
273
+ function progressPct(r) {
274
+ if (!r.itemsTotal || r.itemsTotal <= 0) return r.status === 'completed' ? 100 : 0;
275
+ return Math.min(100, Math.round((r.itemsProcessed / r.itemsTotal) * 100));
276
+ }
277
+
278
+ function statusLabel(r) {
279
+ if (r.status === 'running') return ['s-running', '● running'];
280
+ if (r.status === 'completed') return ['s-completed', '✓ done'];
281
+ if (r.status === 'failed') return ['s-failed', '✗ failed'];
282
+ return ['s-stopped', '⏹ stopped'];
283
+ }
284
+
285
+ function elapsed(r) {
286
+ var ms = (r.endedAt || r.completedAt || Date.now()) - r.startedAt;
287
+ if (ms < 1000) return ms + 'ms';
288
+ if (ms < 60000) return (ms / 1000).toFixed(1) + 's';
289
+ return Math.floor(ms / 60000) + 'm ' + Math.floor((ms % 60000) / 1000) + 's';
290
+ }
291
+
292
+ function renderCards() {
293
+ var cards = document.getElementById('wf-cards');
294
+ var empty = document.getElementById('wf-empty');
295
+ var sorted = Object.values(runs).sort(function(a, b) { return b.startedAt - a.startedAt; });
296
+ if (!sorted.length) {
297
+ cards.innerHTML = '';
298
+ empty.style.display = 'flex';
299
+ return;
300
+ }
301
+ empty.style.display = 'none';
302
+ var pct = 0;
303
+ cards.innerHTML = sorted.map(function(r) {
304
+ var sl = statusLabel(r);
305
+ var isRunning = r.status === 'running';
306
+ pct = progressPct(r);
307
+ var logLines = (runLogs[r.id] || []).slice(-4);
308
+ var logHtml = logLines.length
309
+ ? logLines.map(function(l) {
310
+ var cls = l.type === 'ok' ? 'log-ok' : l.type === 'err' ? 'log-err' : 'log-info';
311
+ return '<div><span class="log-ts">' + esc(l.ts) + '</span> <span class="' + cls + '">' + esc(l.text) + '</span></div>';
312
+ }).join('')
313
+ : '<div style="color:#2a2a3a">no events yet</div>';
314
+ return '<div class="card' + (!isRunning ? ' done' : '') + '">' +
315
+ '<div class="card-header">' +
316
+ '<span class="card-title">' + esc(r.workflowName) + '</span>' +
317
+ '<span class="status-badge ' + sl[0] + '">' + sl[1] + '</span>' +
318
+ '</div>' +
319
+ '<div class="card-meta">' +
320
+ '<span class="mitem">⏱ ' + esc(elapsed(r)) + '</span>' +
321
+ '<span class="mitem">📦 ' + esc(r.itemsProcessed) + '/' + esc(r.itemsTotal || '?') + '</span>' +
322
+ '<span class="mitem" style="color:#3a3a5a">id: ' + esc(r.id.slice(0,8)) + '</span>' +
323
+ (isRunning ? '<button class="stop-btn" onclick="stopRun(\'' + esc(r.id) + '\')">■ stop</button>' : '') +
324
+ '</div>' +
325
+ '<div class="progress-track"><div class="progress-fill" style="width:' + pct + '%"></div></div>' +
326
+ '<div class="log-area">' + logHtml + '</div>' +
327
+ '</div>';
328
+ }).join('');
329
+ }
330
+
331
+ function stopRun(id) {
332
+ fetch('/stop/' + id, { method: 'POST' }).catch(function() {});
333
+ }
334
+
335
+ // ── event handler ──────────────────────────────────────────
336
+ function handleEvent(evt) {
337
+ var id = evt.runId;
338
+ if (!id) return;
339
+ if (!runs[id]) {
340
+ runs[id] = { id: id, workflowId: evt.workflowId, workflowName: evt.workflowName || id,
341
+ status: 'running', startedAt: evt.timestamp || Date.now(),
342
+ itemsProcessed: 0, itemsTotal: 0 };
343
+ }
344
+ if (!runLogs[id]) runLogs[id] = [];
345
+ var r = runs[id];
346
+ var t = ts(evt.timestamp || Date.now());
347
+
348
+ if (evt.eventType === 'run_started') {
349
+ r.status = 'running';
350
+ r.startedAt = evt.timestamp || Date.now();
351
+ } else if (evt.eventType === 'step_started') {
352
+ runLogs[id].push({ ts: t, type: 'info', text: (evt.nodeName || '?') + ' — started' });
353
+ } else if (evt.eventType === 'step_completed') {
354
+ r.itemsProcessed = (r.itemsProcessed || 0) + (evt.itemTotal || 1);
355
+ runLogs[id].push({ ts: t, type: 'ok', text: (evt.nodeName || '?') + ' ✓ ' + (evt.durationMs ? evt.durationMs + 'ms' : '') });
356
+ } else if (evt.eventType === 'step_failed') {
357
+ runLogs[id].push({ ts: t, type: 'err', text: (evt.nodeName || '?') + ' ✗ ' + (evt.error || '') });
358
+ } else if (evt.eventType === 'run_completed') {
359
+ r.status = 'completed';
360
+ r.endedAt = evt.timestamp || Date.now();
361
+ } else if (evt.eventType === 'run_stopped') {
362
+ r.status = evt.error ? 'failed' : 'stopped';
363
+ r.endedAt = evt.timestamp || Date.now();
364
+ }
365
+
366
+ // Trim log
367
+ if (runLogs[id].length > 80) runLogs[id] = runLogs[id].slice(-80);
368
+
369
+ updateHeader();
370
+ renderCards();
371
+ }
372
+
373
+ // ── connection ─────────────────────────────────────────────
374
+ var connDot = document.getElementById('conn-dot');
375
+ var connLabel = document.getElementById('conn-label');
376
+
377
+ function setConnStatus(state) {
378
+ if (state === 'live') {
379
+ connDot.style.background = '#22c55e';
380
+ connLabel.style.color = '#22c55e';
381
+ connLabel.textContent = 'live';
382
+ } else if (state === 'error') {
383
+ connDot.style.background = '#f59e0b';
384
+ connLabel.style.color = '#f59e0b';
385
+ connLabel.textContent = 'reconnecting…';
386
+ } else {
387
+ connDot.style.background = '#555';
388
+ connLabel.style.color = '#555';
389
+ connLabel.textContent = 'connecting…';
390
+ }
391
+ }
392
+
393
+ var _sseRetryDelay = 1000;
394
+ function connectSSE() {
395
+ setConnStatus('connecting');
396
+ var es = new EventSource('/events');
397
+ es.onopen = function() { setConnStatus('live'); _sseRetryDelay = 1000; };
398
+ es.onmessage = function(e) { handleEvent(JSON.parse(e.data)); };
399
+ es.onerror = function() {
400
+ setConnStatus('error');
401
+ es.close();
402
+ setTimeout(connectSSE, Math.min(_sseRetryDelay *= 2, 30000));
403
+ };
404
+ }
405
+
406
+ function connect() {
407
+ setConnStatus('connecting');
408
+ var sock = new WebSocket('ws://' + location.host + '/ws');
409
+ sock.onopen = function() { setConnStatus('live'); };
410
+ sock.onmessage = function(e) {
411
+ var msg = JSON.parse(e.data);
412
+ if (msg.type === 'history') {
413
+ msg.runs.forEach(function(r) {
414
+ runs[r.id] = r;
415
+ // Rebuild minimal log from existing run
416
+ if (!runLogs[r.id]) runLogs[r.id] = [];
417
+ });
418
+ updateHeader();
419
+ renderCards();
420
+ } else {
421
+ handleEvent(msg);
422
+ }
423
+ };
424
+ var wsFailedOnce = false;
425
+ sock.onclose = function() {
426
+ setConnStatus('error');
427
+ if (!wsFailedOnce) {
428
+ wsFailedOnce = true;
429
+ fetch('/events', { method: 'HEAD' }).then(function(r) {
430
+ if (r.ok) { connectSSE(); } else { setTimeout(connect, 2500); }
431
+ }).catch(function() { setTimeout(connect, 2500); });
432
+ } else {
433
+ setTimeout(connect, 2500);
434
+ }
435
+ };
436
+ }
437
+
438
+ // ── init ───────────────────────────────────────────────────
439
+ connect();
440
+ // Refresh elapsed times every 5s for running jobs
441
+ setInterval(function() { if (Object.values(runs).some(function(r){return r.status==='running';})) renderCards(); }, 5000);
442
+ </script>
443
+ </body>
444
+ </html>
@@ -0,0 +1,63 @@
1
+ import type { CdpClient } from './cdp.js';
2
+
3
+ export interface DialogInfo {
4
+ type: 'alert' | 'confirm' | 'prompt' | 'beforeunload';
5
+ message: string;
6
+ defaultPrompt?: string;
7
+ }
8
+
9
+ const _pendingDialogs = new Map<string, DialogInfo | null>();
10
+ const _dialogListeners = new Map<string, Array<() => void>>();
11
+
12
+ export function setupDialogAutoHandling(client: CdpClient, sessionId: string, autoAccept = true): void {
13
+ if (_pendingDialogs.has(sessionId)) return;
14
+ _pendingDialogs.set(sessionId, null);
15
+
16
+ const off1 = client.on('Page.javascriptDialogOpening', async (params, sid) => {
17
+ if (sid !== sessionId) return;
18
+
19
+ const info: DialogInfo = {
20
+ type: params.type as DialogInfo['type'],
21
+ message: params.message as string,
22
+ defaultPrompt: params.defaultPrompt as string | undefined,
23
+ };
24
+
25
+ _pendingDialogs.set(sessionId, info);
26
+
27
+ if (autoAccept) {
28
+ try {
29
+ await client.send('Page.handleJavaScriptDialog', { accept: true }, sessionId);
30
+ } catch { /* dialog may have already been dismissed */ }
31
+ _pendingDialogs.set(sessionId, null);
32
+ }
33
+ });
34
+
35
+ const off2 = client.on('Page.javascriptDialogClosed', (_, sid) => {
36
+ if (sid === sessionId) _pendingDialogs.set(sessionId, null);
37
+ });
38
+
39
+ _dialogListeners.set(sessionId, [off1, off2]);
40
+ }
41
+
42
+ export function teardownDialogHandling(sessionId: string): void {
43
+ const offs = _dialogListeners.get(sessionId);
44
+ if (offs) { for (const off of offs) off(); _dialogListeners.delete(sessionId); }
45
+ _pendingDialogs.delete(sessionId);
46
+ }
47
+
48
+ export async function acceptDialog(client: CdpClient, sessionId: string, text?: string): Promise<void> {
49
+ await client.send('Page.handleJavaScriptDialog', {
50
+ accept: true,
51
+ promptText: text,
52
+ }, sessionId);
53
+ _pendingDialogs.set(sessionId, null);
54
+ }
55
+
56
+ export async function dismissDialog(client: CdpClient, sessionId: string): Promise<void> {
57
+ await client.send('Page.handleJavaScriptDialog', { accept: false }, sessionId);
58
+ _pendingDialogs.set(sessionId, null);
59
+ }
60
+
61
+ export function getDialogStatus(sessionId: string): DialogInfo | null {
62
+ return _pendingDialogs.get(sessionId) ?? null;
63
+ }
@@ -0,0 +1,94 @@
1
+ import type { CdpClient } from './cdp.js';
2
+
3
+ export interface DeviceProfile {
4
+ width: number;
5
+ height: number;
6
+ deviceScaleFactor: number;
7
+ mobile: boolean;
8
+ userAgent: string;
9
+ }
10
+
11
+ const DEVICES: Record<string, DeviceProfile> = {
12
+ 'iPhone 14': {
13
+ width: 390, height: 844, deviceScaleFactor: 3, mobile: true,
14
+ userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1',
15
+ },
16
+ 'iPhone SE': {
17
+ width: 375, height: 667, deviceScaleFactor: 2, mobile: true,
18
+ userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1',
19
+ },
20
+ 'iPad': {
21
+ width: 768, height: 1024, deviceScaleFactor: 2, mobile: true,
22
+ userAgent: 'Mozilla/5.0 (iPad; CPU OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1',
23
+ },
24
+ 'Galaxy S21': {
25
+ width: 360, height: 800, deviceScaleFactor: 3, mobile: true,
26
+ userAgent: 'Mozilla/5.0 (Linux; Android 11; SM-G991B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Mobile Safari/537.36',
27
+ },
28
+ 'Pixel 5': {
29
+ width: 393, height: 851, deviceScaleFactor: 2.75, mobile: true,
30
+ userAgent: 'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Mobile Safari/537.36',
31
+ },
32
+ };
33
+
34
+ export async function emulateDevice(client: CdpClient, sessionId: string, deviceName: string): Promise<void> {
35
+ const device = DEVICES[deviceName];
36
+ if (!device) {
37
+ throw new Error(`Unknown device: "${deviceName}". Available: ${Object.keys(DEVICES).join(', ')}`);
38
+ }
39
+
40
+ await client.send('Emulation.setDeviceMetricsOverride', {
41
+ width: device.width,
42
+ height: device.height,
43
+ deviceScaleFactor: device.deviceScaleFactor,
44
+ mobile: device.mobile,
45
+ }, sessionId);
46
+
47
+ await client.send('Emulation.setUserAgentOverride', { userAgent: device.userAgent }, sessionId);
48
+ await client.send('Emulation.setTouchEmulationEnabled', { enabled: device.mobile }, sessionId);
49
+ }
50
+
51
+ export async function setGeolocation(
52
+ client: CdpClient,
53
+ sessionId: string,
54
+ latitude: number,
55
+ longitude: number,
56
+ accuracy = 100
57
+ ): Promise<void> {
58
+ await client.send('Emulation.setGeolocationOverride', { latitude, longitude, accuracy }, sessionId);
59
+ }
60
+
61
+ export async function setOfflineMode(client: CdpClient, sessionId: string, offline: boolean): Promise<void> {
62
+ await client.send('Network.emulateNetworkConditions', {
63
+ offline,
64
+ latency: 0,
65
+ downloadThroughput: -1,
66
+ uploadThroughput: -1,
67
+ }, sessionId);
68
+ }
69
+
70
+ export async function setColorScheme(
71
+ client: CdpClient,
72
+ sessionId: string,
73
+ scheme: 'dark' | 'light' | 'no-preference'
74
+ ): Promise<void> {
75
+ await client.send('Emulation.setEmulatedMedia', {
76
+ features: [{ name: 'prefers-color-scheme', value: scheme }],
77
+ }, sessionId);
78
+ }
79
+
80
+ export async function setBasicAuth(
81
+ client: CdpClient,
82
+ sessionId: string,
83
+ username: string,
84
+ password: string
85
+ ): Promise<void> {
86
+ const encoded = Buffer.from(`${username}:${password}`).toString('base64');
87
+ await client.send('Network.setExtraHTTPHeaders', {
88
+ headers: { Authorization: `Basic ${encoded}` },
89
+ }, sessionId);
90
+ }
91
+
92
+ export function listDevices(): string[] {
93
+ return Object.keys(DEVICES);
94
+ }