@hanzo/browser-extension 1.7.2
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/.github/workflows/docs.yml +44 -0
- package/.github/workflows/release.yml +55 -0
- package/LICENSE +21 -0
- package/README.md +126 -0
- package/dist/browser-extension/README.md +43 -0
- package/dist/browser-extension/ai-worker.js +30 -0
- package/dist/browser-extension/background-firefox.js +617 -0
- package/dist/browser-extension/background.js +2737 -0
- package/dist/browser-extension/browser-control.js +683 -0
- package/dist/browser-extension/cdp-bridge-server.js +444 -0
- package/dist/browser-extension/chrome/ai-worker.js +571 -0
- package/dist/browser-extension/chrome/background.js +2737 -0
- package/dist/browser-extension/chrome/callback.html +17 -0
- package/dist/browser-extension/chrome/content-script.js +1270 -0
- package/dist/browser-extension/chrome/icon128.png +0 -0
- package/dist/browser-extension/chrome/icon16.png +0 -0
- package/dist/browser-extension/chrome/icon32.png +0 -0
- package/dist/browser-extension/chrome/icon48.png +0 -0
- package/dist/browser-extension/chrome/manifest.json +57 -0
- package/dist/browser-extension/chrome/popup.css +519 -0
- package/dist/browser-extension/chrome/popup.html +242 -0
- package/dist/browser-extension/chrome/popup.js +267 -0
- package/dist/browser-extension/chrome/sidebar.css +1176 -0
- package/dist/browser-extension/chrome/sidebar.html +280 -0
- package/dist/browser-extension/chrome/sidebar.js +778 -0
- package/dist/browser-extension/cli.js +233 -0
- package/dist/browser-extension/content-script.js +1270 -0
- package/dist/browser-extension/firefox/ai-worker.js +571 -0
- package/dist/browser-extension/firefox/background.js +617 -0
- package/dist/browser-extension/firefox/callback.html +17 -0
- package/dist/browser-extension/firefox/content-script.js +1270 -0
- package/dist/browser-extension/firefox/icon128.png +0 -0
- package/dist/browser-extension/firefox/icon16.png +0 -0
- package/dist/browser-extension/firefox/icon32.png +0 -0
- package/dist/browser-extension/firefox/icon48.png +0 -0
- package/dist/browser-extension/firefox/manifest.json +66 -0
- package/dist/browser-extension/firefox/popup.css +519 -0
- package/dist/browser-extension/firefox/popup.html +242 -0
- package/dist/browser-extension/firefox/popup.js +267 -0
- package/dist/browser-extension/firefox/sidebar.css +1176 -0
- package/dist/browser-extension/firefox/sidebar.html +280 -0
- package/dist/browser-extension/firefox/sidebar.js +778 -0
- package/dist/browser-extension/hanzo-browser-extension-firefox-1.1.0.zip +0 -0
- package/dist/browser-extension/icon128.png +0 -0
- package/dist/browser-extension/icon16.png +0 -0
- package/dist/browser-extension/icon32.png +0 -0
- package/dist/browser-extension/icon48.png +0 -0
- package/dist/browser-extension/manifest.json +57 -0
- package/dist/browser-extension/package.json +41 -0
- package/dist/browser-extension/popup.css +468 -0
- package/dist/browser-extension/popup.html +191 -0
- package/dist/browser-extension/popup.js +267 -0
- package/dist/browser-extension/safari/Info.plist +21 -0
- package/dist/browser-extension/safari/ai-worker.js +571 -0
- package/dist/browser-extension/safari/background.js +2737 -0
- package/dist/browser-extension/safari/callback.html +17 -0
- package/dist/browser-extension/safari/content-script.js +1270 -0
- package/dist/browser-extension/safari/icon128.png +0 -0
- package/dist/browser-extension/safari/icon16.png +0 -0
- package/dist/browser-extension/safari/icon32.png +0 -0
- package/dist/browser-extension/safari/icon48.png +0 -0
- package/dist/browser-extension/safari/popup.css +519 -0
- package/dist/browser-extension/safari/popup.html +242 -0
- package/dist/browser-extension/safari/popup.js +267 -0
- package/dist/browser-extension/safari/sidebar.css +1176 -0
- package/dist/browser-extension/safari/sidebar.html +280 -0
- package/dist/browser-extension/safari/sidebar.js +778 -0
- package/dist/browser-extension/sidebar.css +718 -0
- package/dist/browser-extension/sidebar.html +184 -0
- package/dist/browser-extension/sidebar.js +778 -0
- package/dist/browser-extension/webgpu-ai.js +495 -0
- package/dist/cdp-bridge-server.js +411 -0
- package/dist/safari/Hanzo AI/Hanzo AI.xcodeproj/project.pbxproj +1047 -0
- package/dist/safari/Hanzo AI/Hanzo AI.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AccentColor.colorset/Contents.json +11 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/Contents.json +98 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-128@1x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-128@2x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-16@1x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-16@2x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-256@1x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-256@2x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-32@1x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-32@2x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-512@1x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-512@2x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/universal-icon-1024@1x.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/Contents.json +6 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/LargeIcon.imageset/Contents.json +21 -0
- package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/LargeIcon.imageset/icon128.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Resources/Base.lproj/Main.html +20 -0
- package/dist/safari/Hanzo AI/Shared (App)/Resources/Icon.png +0 -0
- package/dist/safari/Hanzo AI/Shared (App)/Resources/Script.js +24 -0
- package/dist/safari/Hanzo AI/Shared (App)/Resources/Style.css +61 -0
- package/dist/safari/Hanzo AI/Shared (App)/ViewController.swift +81 -0
- package/dist/safari/Hanzo AI/Shared (Extension)/SafariWebExtensionHandler.swift +42 -0
- package/dist/safari/Hanzo AI/iOS (App)/AppDelegate.swift +24 -0
- package/dist/safari/Hanzo AI/iOS (App)/Base.lproj/LaunchScreen.storyboard +36 -0
- package/dist/safari/Hanzo AI/iOS (App)/Base.lproj/Main.storyboard +38 -0
- package/dist/safari/Hanzo AI/iOS (App)/Info.plist +27 -0
- package/dist/safari/Hanzo AI/iOS (App)/SceneDelegate.swift +18 -0
- package/dist/safari/Hanzo AI/iOS (Extension)/Info.plist +13 -0
- package/dist/safari/Hanzo AI/macOS (App)/AppDelegate.swift +21 -0
- package/dist/safari/Hanzo AI/macOS (App)/Base.lproj/Main.storyboard +124 -0
- package/dist/safari/Hanzo AI/macOS (App)/Info.plist +8 -0
- package/dist/safari/Hanzo AI/macOS (Extension)/Info.plist +13 -0
- package/docs/_config.yml +13 -0
- package/docs/_layouts/default.html +435 -0
- package/docs/index.md +54 -0
- package/install-extension.sh +75 -0
- package/manifest.json +29 -0
- package/package.json +32 -0
- package/src/ai-worker.js +571 -0
- package/src/callback.html +17 -0
- package/src/chat-widget.tsx +93 -0
- package/src/icon128.png +0 -0
- package/src/icon16.png +0 -0
- package/src/icon32.png +0 -0
- package/src/icon48.png +0 -0
- package/src/manifest-firefox.json +66 -0
- package/src/manifest.json +57 -0
- package/src/package.json +41 -0
- package/src/popup.css +519 -0
- package/src/popup.html +242 -0
- package/src/popup.js +192 -0
- package/src/sidebar.css +1176 -0
- package/src/sidebar.html +280 -0
- package/src/sidebar.js +884 -0
- package/src/webgpu.d.ts +131 -0
- package/test/claude-integration.test.ts +251 -0
- package/test/server-frameworks-edge-cases.test.ts +145 -0
- package/test/server-frameworks.test.ts +395 -0
- package/test-cdp-connection.js +140 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,242 @@
|
|
|
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.0">
|
|
6
|
+
<title>Hanzo AI</title>
|
|
7
|
+
<link rel="stylesheet" href="popup.css">
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div class="container">
|
|
11
|
+
<header>
|
|
12
|
+
<svg class="logo" viewBox="0 0 67 67" xmlns="http://www.w3.org/2000/svg">
|
|
13
|
+
<path d="M22.21 67V44.6369H0V67H22.21Z" fill="currentColor"/>
|
|
14
|
+
<path d="M66.7038 22.3184H22.2534L0.0878906 44.6367H44.4634L66.7038 22.3184Z" fill="currentColor"/>
|
|
15
|
+
<path d="M22.21 0H0V22.3184H22.21V0Z" fill="currentColor"/>
|
|
16
|
+
<path d="M66.7198 0H44.5098V22.3184H66.7198V0Z" fill="currentColor"/>
|
|
17
|
+
<path d="M66.7198 67V44.6369H44.5098V67H66.7198Z" fill="currentColor"/>
|
|
18
|
+
</svg>
|
|
19
|
+
<h1>Hanzo AI</h1>
|
|
20
|
+
</header>
|
|
21
|
+
|
|
22
|
+
<!-- Login Section -->
|
|
23
|
+
<div id="login-section" class="section">
|
|
24
|
+
<h2>Sign In</h2>
|
|
25
|
+
<p class="subtitle">Connect to your Hanzo AI account</p>
|
|
26
|
+
<button id="login-btn" class="btn btn-primary">Sign in with Hanzo</button>
|
|
27
|
+
<p class="help-text">
|
|
28
|
+
<a href="https://hanzo.id/register" target="_blank">Create account</a> •
|
|
29
|
+
<a href="https://hanzo.id/forgot" target="_blank">Forgot password?</a>
|
|
30
|
+
</p>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<!-- Main Section (shown after login) -->
|
|
34
|
+
<div id="main-section" class="section hidden">
|
|
35
|
+
<div class="user-info">
|
|
36
|
+
<img id="user-avatar" class="avatar" src="" alt="">
|
|
37
|
+
<div>
|
|
38
|
+
<div id="user-name" class="user-name"></div>
|
|
39
|
+
<div id="user-email" class="user-email"></div>
|
|
40
|
+
</div>
|
|
41
|
+
<button id="logout-btn" class="btn-icon" title="Sign out">
|
|
42
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
|
43
|
+
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4M16 17l5-5-5-5M21 12H9"/>
|
|
44
|
+
</svg>
|
|
45
|
+
</button>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<div class="divider"></div>
|
|
49
|
+
|
|
50
|
+
<!-- Open Chat Panel -->
|
|
51
|
+
<button id="open-panel" class="btn btn-primary" style="margin-bottom: 16px;">
|
|
52
|
+
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
|
53
|
+
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
|
|
54
|
+
</svg>
|
|
55
|
+
Open Chat Panel
|
|
56
|
+
</button>
|
|
57
|
+
<button id="open-page-overlay" class="btn btn-secondary" style="margin-bottom: 16px;">
|
|
58
|
+
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
|
59
|
+
<path d="M4 4h16v12H8l-4 4V4z"/>
|
|
60
|
+
</svg>
|
|
61
|
+
Toggle On-Page Overlay
|
|
62
|
+
</button>
|
|
63
|
+
|
|
64
|
+
<!-- Features -->
|
|
65
|
+
<div class="features">
|
|
66
|
+
<div class="feature-toggle">
|
|
67
|
+
<label>
|
|
68
|
+
<input type="checkbox" id="source-maps" checked>
|
|
69
|
+
<span class="toggle"></span>
|
|
70
|
+
<div class="feature-info">
|
|
71
|
+
<strong>Source Maps</strong>
|
|
72
|
+
<small>Click-to-code navigation</small>
|
|
73
|
+
</div>
|
|
74
|
+
</label>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
<div class="feature-toggle">
|
|
78
|
+
<label>
|
|
79
|
+
<input type="checkbox" id="webgpu-ai" checked>
|
|
80
|
+
<span class="toggle"></span>
|
|
81
|
+
<div class="feature-info">
|
|
82
|
+
<strong>WebGPU AI</strong>
|
|
83
|
+
<small>Local AI inference</small>
|
|
84
|
+
</div>
|
|
85
|
+
</label>
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
<div class="feature-toggle">
|
|
89
|
+
<label>
|
|
90
|
+
<input type="checkbox" id="browser-control">
|
|
91
|
+
<span class="toggle"></span>
|
|
92
|
+
<div class="feature-info">
|
|
93
|
+
<strong>Browser Control</strong>
|
|
94
|
+
<small>AI browser automation</small>
|
|
95
|
+
</div>
|
|
96
|
+
</label>
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
<div class="feature-toggle">
|
|
100
|
+
<label>
|
|
101
|
+
<input type="checkbox" id="tab-filesystem">
|
|
102
|
+
<span class="toggle"></span>
|
|
103
|
+
<div class="feature-info">
|
|
104
|
+
<strong>Tab Filesystem</strong>
|
|
105
|
+
<small>Mount tabs as files</small>
|
|
106
|
+
</div>
|
|
107
|
+
</label>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
|
|
111
|
+
<div class="divider"></div>
|
|
112
|
+
|
|
113
|
+
<!-- Browser Backend -->
|
|
114
|
+
<div class="setting-group backend-picker">
|
|
115
|
+
<h3>Browser Backend</h3>
|
|
116
|
+
<div class="backend-select-wrapper">
|
|
117
|
+
<select id="browser-backend">
|
|
118
|
+
<option value="auto">Auto (default)</option>
|
|
119
|
+
<option value="this">This Browser</option>
|
|
120
|
+
<option value="firefox">Firefox</option>
|
|
121
|
+
<option value="chrome">Chrome</option>
|
|
122
|
+
<option value="playwright">Playwright Only</option>
|
|
123
|
+
</select>
|
|
124
|
+
</div>
|
|
125
|
+
<div class="backend-status">
|
|
126
|
+
<span class="status-dot" id="bridge-status"></span>
|
|
127
|
+
<span id="bridge-detail" class="status-detail">Checking bridge...</span>
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
|
|
131
|
+
<div class="divider"></div>
|
|
132
|
+
|
|
133
|
+
<!-- Connection Status -->
|
|
134
|
+
<div class="status">
|
|
135
|
+
<div class="status-item">
|
|
136
|
+
<span class="status-dot" id="zap-status"></span>
|
|
137
|
+
<span>ZAP Protocol</span>
|
|
138
|
+
<span id="zap-detail" class="status-detail">Discovering...</span>
|
|
139
|
+
</div>
|
|
140
|
+
<div class="status-item">
|
|
141
|
+
<span class="status-dot" id="mcp-status"></span>
|
|
142
|
+
<span>MCP Server</span>
|
|
143
|
+
<span id="mcp-port" class="status-detail">Fallback</span>
|
|
144
|
+
</div>
|
|
145
|
+
<div class="status-item">
|
|
146
|
+
<span class="status-dot" id="cdp-status"></span>
|
|
147
|
+
<span>CDP Bridge</span>
|
|
148
|
+
<span id="cdp-detail" class="status-detail">Checking...</span>
|
|
149
|
+
</div>
|
|
150
|
+
<div class="status-item">
|
|
151
|
+
<span class="status-dot" id="gpu-status"></span>
|
|
152
|
+
<span>WebGPU</span>
|
|
153
|
+
<span id="gpu-detail" class="status-detail">Checking...</span>
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
|
|
157
|
+
<div class="divider"></div>
|
|
158
|
+
|
|
159
|
+
<!-- Actions -->
|
|
160
|
+
<div class="actions">
|
|
161
|
+
<button id="test-connection" class="btn btn-secondary">Test Connection</button>
|
|
162
|
+
<button id="open-settings" class="btn btn-secondary">Settings</button>
|
|
163
|
+
</div>
|
|
164
|
+
|
|
165
|
+
<!-- Quick Guide -->
|
|
166
|
+
<div class="guide" style="margin-top: 16px;">
|
|
167
|
+
<h3>Quick Guide</h3>
|
|
168
|
+
<p><kbd>Alt</kbd> + <kbd>Click</kbd> any element to navigate to source</p>
|
|
169
|
+
<p><kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>H</kbd> to open Hanzo AI panel</p>
|
|
170
|
+
</div>
|
|
171
|
+
</div>
|
|
172
|
+
|
|
173
|
+
<!-- Settings Section -->
|
|
174
|
+
<div id="settings-section" class="section hidden">
|
|
175
|
+
<div class="settings-header">
|
|
176
|
+
<button id="back-btn" class="btn-icon">
|
|
177
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
|
178
|
+
<path d="M19 12H5M12 19l-7-7 7-7"/>
|
|
179
|
+
</svg>
|
|
180
|
+
</button>
|
|
181
|
+
<h2>Settings</h2>
|
|
182
|
+
</div>
|
|
183
|
+
|
|
184
|
+
<div class="settings-content">
|
|
185
|
+
<div class="setting-group">
|
|
186
|
+
<h3>Connection</h3>
|
|
187
|
+
<label>
|
|
188
|
+
MCP Port
|
|
189
|
+
<input type="number" id="mcp-port-setting" value="3001" min="1024" max="65535">
|
|
190
|
+
</label>
|
|
191
|
+
<label>
|
|
192
|
+
CDP Bridge Port
|
|
193
|
+
<input type="number" id="cdp-port-setting" value="9223" min="1024" max="65535">
|
|
194
|
+
</label>
|
|
195
|
+
<label>
|
|
196
|
+
ZAP Discovery Ports
|
|
197
|
+
<input type="text" id="zap-ports-setting" value="9999,9998,9997,9996,9995" placeholder="Comma-separated ports">
|
|
198
|
+
</label>
|
|
199
|
+
</div>
|
|
200
|
+
|
|
201
|
+
<div class="setting-group">
|
|
202
|
+
<h3>AI Models</h3>
|
|
203
|
+
<label>
|
|
204
|
+
Local Model Path
|
|
205
|
+
<input type="text" id="model-path" placeholder="/models/browser-control-4bit.bin">
|
|
206
|
+
</label>
|
|
207
|
+
<label>
|
|
208
|
+
Model Quantization
|
|
209
|
+
<select id="model-quant">
|
|
210
|
+
<option value="4bit">4-bit (Fastest)</option>
|
|
211
|
+
<option value="8bit">8-bit (Balanced)</option>
|
|
212
|
+
<option value="fp16">FP16 (Best Quality)</option>
|
|
213
|
+
</select>
|
|
214
|
+
</label>
|
|
215
|
+
</div>
|
|
216
|
+
|
|
217
|
+
<div class="setting-group">
|
|
218
|
+
<h3>Browser Control</h3>
|
|
219
|
+
<label>
|
|
220
|
+
<input type="checkbox" id="safe-mode" checked>
|
|
221
|
+
Safe Mode (require confirmation for actions)
|
|
222
|
+
</label>
|
|
223
|
+
<label>
|
|
224
|
+
Max Workers
|
|
225
|
+
<input type="number" id="max-workers" value="3" min="1" max="10">
|
|
226
|
+
</label>
|
|
227
|
+
</div>
|
|
228
|
+
|
|
229
|
+
<button id="save-settings" class="btn btn-primary">Save Settings</button>
|
|
230
|
+
|
|
231
|
+
<p class="help-text" style="margin-top: 16px;">
|
|
232
|
+
<a href="https://console.hanzo.ai" target="_blank">Console</a> •
|
|
233
|
+
<a href="https://billing.hanzo.ai" target="_blank">Billing</a> •
|
|
234
|
+
<a href="https://docs.hanzo.ai" target="_blank">Docs</a>
|
|
235
|
+
</p>
|
|
236
|
+
</div>
|
|
237
|
+
</div>
|
|
238
|
+
</div>
|
|
239
|
+
|
|
240
|
+
<script src="popup.js"></script>
|
|
241
|
+
</body>
|
|
242
|
+
</html>
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
(() => {
|
|
3
|
+
// src/popup.ts
|
|
4
|
+
document.addEventListener("DOMContentLoaded", () => {
|
|
5
|
+
const loginSection = document.getElementById("login-section");
|
|
6
|
+
const mainSection = document.getElementById("main-section");
|
|
7
|
+
const settingsSection = document.getElementById("settings-section");
|
|
8
|
+
const loginBtn = document.getElementById("login-btn");
|
|
9
|
+
const logoutBtn = document.getElementById("logout-btn");
|
|
10
|
+
const openPanel = document.getElementById("open-panel");
|
|
11
|
+
const openPageOverlay = document.getElementById("open-page-overlay");
|
|
12
|
+
const zapStatus = document.getElementById("zap-status");
|
|
13
|
+
const zapDetail = document.getElementById("zap-detail");
|
|
14
|
+
const mcpStatus = document.getElementById("mcp-status");
|
|
15
|
+
const mcpPort = document.getElementById("mcp-port");
|
|
16
|
+
const cdpStatus = document.getElementById("cdp-status");
|
|
17
|
+
const cdpDetail = document.getElementById("cdp-detail");
|
|
18
|
+
const gpuStatus = document.getElementById("gpu-status");
|
|
19
|
+
const gpuDetail = document.getElementById("gpu-detail");
|
|
20
|
+
const openSettings = document.getElementById("open-settings");
|
|
21
|
+
const backBtn = document.getElementById("back-btn");
|
|
22
|
+
const saveSettings = document.getElementById("save-settings");
|
|
23
|
+
const testConnection = document.getElementById("test-connection");
|
|
24
|
+
function checkAuth() {
|
|
25
|
+
mainSection.classList.remove("hidden");
|
|
26
|
+
refreshStatus();
|
|
27
|
+
chrome.runtime.sendMessage({ action: "auth.status" }, (response) => {
|
|
28
|
+
if (chrome.runtime.lastError) return;
|
|
29
|
+
if ((response == null ? void 0 : response.success) && response.authenticated) {
|
|
30
|
+
loginSection.classList.add("hidden");
|
|
31
|
+
if (response.user) {
|
|
32
|
+
const avatar = document.getElementById("user-avatar");
|
|
33
|
+
const name = document.getElementById("user-name");
|
|
34
|
+
const email = document.getElementById("user-email");
|
|
35
|
+
avatar.src = response.user.picture || response.user.avatar || "icon48.png";
|
|
36
|
+
name.textContent = response.user.name || response.user.displayName || "Hanzo User";
|
|
37
|
+
email.textContent = response.user.email || "";
|
|
38
|
+
}
|
|
39
|
+
} else {
|
|
40
|
+
loginSection.classList.remove("hidden");
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
loginBtn == null ? void 0 : loginBtn.addEventListener("click", () => {
|
|
45
|
+
loginBtn.disabled = true;
|
|
46
|
+
loginBtn.textContent = "Signing in...";
|
|
47
|
+
chrome.runtime.sendMessage({ action: "auth.login" }, (response) => {
|
|
48
|
+
loginBtn.disabled = false;
|
|
49
|
+
loginBtn.innerHTML = '<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4M10 17l5-5-5-5M15 12H3"/></svg> Sign in with Hanzo';
|
|
50
|
+
if (response == null ? void 0 : response.success) {
|
|
51
|
+
checkAuth();
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
logoutBtn == null ? void 0 : logoutBtn.addEventListener("click", () => {
|
|
56
|
+
chrome.runtime.sendMessage({ action: "auth.logout" }, () => {
|
|
57
|
+
mainSection.classList.add("hidden");
|
|
58
|
+
loginSection.classList.remove("hidden");
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
openPanel == null ? void 0 : openPanel.addEventListener("click", () => {
|
|
62
|
+
if (chrome.sidePanel) {
|
|
63
|
+
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
|
|
64
|
+
var _a;
|
|
65
|
+
if ((_a = tabs[0]) == null ? void 0 : _a.id) {
|
|
66
|
+
chrome.sidePanel.open({ tabId: tabs[0].id });
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
} else {
|
|
70
|
+
window.close();
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
function isInjectableTab(tab) {
|
|
74
|
+
if (!tab || !tab.id) return false;
|
|
75
|
+
const url = tab.url || "";
|
|
76
|
+
if (!url) return false;
|
|
77
|
+
if (url.startsWith("chrome://")) return false;
|
|
78
|
+
if (url.startsWith("chrome-extension://")) return false;
|
|
79
|
+
if (url.startsWith("devtools://")) return false;
|
|
80
|
+
if (url.startsWith("edge://")) return false;
|
|
81
|
+
if (url.startsWith("about:")) return false;
|
|
82
|
+
if (url.startsWith("view-source:")) return false;
|
|
83
|
+
if (url.startsWith("moz-extension://")) return false;
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
openPageOverlay == null ? void 0 : openPageOverlay.addEventListener("click", () => {
|
|
87
|
+
openPageOverlay.textContent = "Opening...";
|
|
88
|
+
openPageOverlay.setAttribute("disabled", "true");
|
|
89
|
+
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
|
|
90
|
+
const activeTab = tabs[0];
|
|
91
|
+
let targetTab = isInjectableTab(activeTab) ? activeTab : null;
|
|
92
|
+
if (!targetTab) {
|
|
93
|
+
chrome.tabs.query({ currentWindow: true }, (allTabs) => {
|
|
94
|
+
targetTab = allTabs.find((tab) => isInjectableTab(tab)) || null;
|
|
95
|
+
if (!(targetTab == null ? void 0 : targetTab.id)) {
|
|
96
|
+
openPageOverlay.textContent = "No valid page tab";
|
|
97
|
+
setTimeout(() => {
|
|
98
|
+
openPageOverlay.textContent = "Toggle On-Page Overlay";
|
|
99
|
+
openPageOverlay.removeAttribute("disabled");
|
|
100
|
+
}, 1200);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
chrome.runtime.sendMessage({ action: "page.overlay.toggle", tabId: targetTab.id }, (response) => {
|
|
104
|
+
if (chrome.runtime.lastError || !(response == null ? void 0 : response.success)) {
|
|
105
|
+
openPageOverlay.textContent = "Unavailable on this page";
|
|
106
|
+
} else {
|
|
107
|
+
const opened = !!(response == null ? void 0 : response.open);
|
|
108
|
+
openPageOverlay.textContent = opened ? "Overlay Opened" : "Overlay Hidden";
|
|
109
|
+
}
|
|
110
|
+
setTimeout(() => {
|
|
111
|
+
openPageOverlay.textContent = "Toggle On-Page Overlay";
|
|
112
|
+
openPageOverlay.removeAttribute("disabled");
|
|
113
|
+
}, 900);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (!(targetTab == null ? void 0 : targetTab.id)) {
|
|
119
|
+
openPageOverlay.textContent = "No Active Tab";
|
|
120
|
+
setTimeout(() => {
|
|
121
|
+
openPageOverlay.textContent = "Toggle On-Page Overlay";
|
|
122
|
+
openPageOverlay.removeAttribute("disabled");
|
|
123
|
+
}, 1200);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
chrome.runtime.sendMessage({ action: "page.overlay.toggle", tabId: targetTab.id }, (response) => {
|
|
127
|
+
if (chrome.runtime.lastError || !(response == null ? void 0 : response.success)) {
|
|
128
|
+
openPageOverlay.textContent = "Unavailable on this page";
|
|
129
|
+
} else {
|
|
130
|
+
const opened = !!(response == null ? void 0 : response.open);
|
|
131
|
+
openPageOverlay.textContent = opened ? "Overlay Opened" : "Overlay Hidden";
|
|
132
|
+
}
|
|
133
|
+
setTimeout(() => {
|
|
134
|
+
openPageOverlay.textContent = "Toggle On-Page Overlay";
|
|
135
|
+
openPageOverlay.removeAttribute("disabled");
|
|
136
|
+
}, 900);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
function refreshStatus() {
|
|
141
|
+
chrome.runtime.sendMessage({ action: "zap.status" }, (response) => {
|
|
142
|
+
var _a, _b;
|
|
143
|
+
if (chrome.runtime.lastError) return;
|
|
144
|
+
if (response == null ? void 0 : response.success) {
|
|
145
|
+
const zap = response.zap;
|
|
146
|
+
if (zap.connected) {
|
|
147
|
+
zapStatus.classList.add("connected");
|
|
148
|
+
zapStatus.classList.remove("disconnected");
|
|
149
|
+
const mcpCount = ((_a = zap.mcps) == null ? void 0 : _a.length) || 0;
|
|
150
|
+
const toolCount = ((_b = zap.mcps) == null ? void 0 : _b.reduce((sum, m) => {
|
|
151
|
+
var _a2;
|
|
152
|
+
return sum + (((_a2 = m.tools) == null ? void 0 : _a2.length) || 0);
|
|
153
|
+
}, 0)) || 0;
|
|
154
|
+
zapDetail.textContent = `${mcpCount} MCP(s), ${toolCount} tools`;
|
|
155
|
+
} else {
|
|
156
|
+
zapStatus.classList.remove("connected");
|
|
157
|
+
zapStatus.classList.add("disconnected");
|
|
158
|
+
zapDetail.textContent = "Not connected";
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
chrome.runtime.sendMessage({ action: "runLocalAI", prompt: "" }, (response) => {
|
|
163
|
+
if (chrome.runtime.lastError) return;
|
|
164
|
+
gpuStatus.classList.add((response == null ? void 0 : response.success) ? "connected" : "disconnected");
|
|
165
|
+
gpuStatus.classList.remove((response == null ? void 0 : response.success) ? "disconnected" : "connected");
|
|
166
|
+
gpuDetail.textContent = (response == null ? void 0 : response.success) ? "Available" : "Not available";
|
|
167
|
+
});
|
|
168
|
+
mcpStatus.classList.add("connected");
|
|
169
|
+
mcpPort.textContent = "Fallback";
|
|
170
|
+
cdpStatus.classList.add("connected");
|
|
171
|
+
cdpDetail.textContent = "Active";
|
|
172
|
+
}
|
|
173
|
+
openSettings == null ? void 0 : openSettings.addEventListener("click", () => {
|
|
174
|
+
mainSection.classList.add("hidden");
|
|
175
|
+
settingsSection.classList.remove("hidden");
|
|
176
|
+
loadSettings();
|
|
177
|
+
});
|
|
178
|
+
backBtn == null ? void 0 : backBtn.addEventListener("click", () => {
|
|
179
|
+
settingsSection.classList.add("hidden");
|
|
180
|
+
mainSection.classList.remove("hidden");
|
|
181
|
+
});
|
|
182
|
+
function loadSettings() {
|
|
183
|
+
chrome.storage.local.get(["mcpPort", "cdpPort", "zapPorts"], (result) => {
|
|
184
|
+
const mcpPortInput = document.getElementById("mcp-port-setting");
|
|
185
|
+
const cdpPortInput = document.getElementById("cdp-port-setting");
|
|
186
|
+
const zapPortsInput = document.getElementById("zap-ports-setting");
|
|
187
|
+
if (mcpPortInput) mcpPortInput.value = result.mcpPort || 3001;
|
|
188
|
+
if (cdpPortInput) cdpPortInput.value = result.cdpPort || 9223;
|
|
189
|
+
if (zapPortsInput) zapPortsInput.value = (result.zapPorts || [9999, 9998, 9997, 9996, 9995]).join(",");
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
saveSettings == null ? void 0 : saveSettings.addEventListener("click", () => {
|
|
193
|
+
var _a, _b, _c;
|
|
194
|
+
const mcpPortVal = parseInt((_a = document.getElementById("mcp-port-setting")) == null ? void 0 : _a.value) || 3001;
|
|
195
|
+
const cdpPortVal = parseInt((_b = document.getElementById("cdp-port-setting")) == null ? void 0 : _b.value) || 9223;
|
|
196
|
+
const zapPortsVal = (((_c = document.getElementById("zap-ports-setting")) == null ? void 0 : _c.value) || "").split(",").map((p) => parseInt(p.trim())).filter((p) => p > 0 && p < 65536);
|
|
197
|
+
chrome.storage.local.set({
|
|
198
|
+
mcpPort: mcpPortVal,
|
|
199
|
+
cdpPort: cdpPortVal,
|
|
200
|
+
zapPorts: zapPortsVal.length ? zapPortsVal : [9999, 9998, 9997, 9996, 9995]
|
|
201
|
+
}, () => {
|
|
202
|
+
saveSettings.textContent = "Saved!";
|
|
203
|
+
setTimeout(() => {
|
|
204
|
+
saveSettings.textContent = "Save Settings";
|
|
205
|
+
}, 1500);
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
testConnection == null ? void 0 : testConnection.addEventListener("click", () => {
|
|
209
|
+
testConnection.textContent = "Testing...";
|
|
210
|
+
testConnection.disabled = true;
|
|
211
|
+
chrome.runtime.sendMessage({ action: "zap.discover" }, () => {
|
|
212
|
+
refreshStatus();
|
|
213
|
+
testConnection.textContent = "Test Connection";
|
|
214
|
+
testConnection.disabled = false;
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
["source-maps", "webgpu-ai", "browser-control", "tab-filesystem"].forEach((id) => {
|
|
218
|
+
const checkbox = document.getElementById(id);
|
|
219
|
+
if (checkbox) {
|
|
220
|
+
chrome.storage.local.get([id], (result) => {
|
|
221
|
+
checkbox.checked = result[id] !== false;
|
|
222
|
+
});
|
|
223
|
+
checkbox.addEventListener("change", () => {
|
|
224
|
+
chrome.storage.local.set({ [id]: checkbox.checked });
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
const backendSelect = document.getElementById("browser-backend");
|
|
229
|
+
const bridgeStatus = document.getElementById("bridge-status");
|
|
230
|
+
const bridgeDetail = document.getElementById("bridge-detail");
|
|
231
|
+
function loadBackendPreference() {
|
|
232
|
+
chrome.storage.sync.get(["browserBackend"], (result) => {
|
|
233
|
+
if (backendSelect && result.browserBackend) {
|
|
234
|
+
backendSelect.value = result.browserBackend;
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
function refreshBridgeStatus() {
|
|
239
|
+
chrome.runtime.sendMessage({ action: "bridge.status" }, (response) => {
|
|
240
|
+
if (chrome.runtime.lastError) return;
|
|
241
|
+
if ((response == null ? void 0 : response.success) && response.connected) {
|
|
242
|
+
bridgeStatus == null ? void 0 : bridgeStatus.classList.add("connected");
|
|
243
|
+
bridgeStatus == null ? void 0 : bridgeStatus.classList.remove("disconnected");
|
|
244
|
+
const browsers = response.browsers || [];
|
|
245
|
+
bridgeDetail.textContent = browsers.length ? `Connected: ${browsers.join(", ")}` : "Connected";
|
|
246
|
+
} else {
|
|
247
|
+
bridgeStatus == null ? void 0 : bridgeStatus.classList.remove("connected");
|
|
248
|
+
bridgeStatus == null ? void 0 : bridgeStatus.classList.add("disconnected");
|
|
249
|
+
bridgeDetail.textContent = "Not connected";
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
backendSelect == null ? void 0 : backendSelect.addEventListener("change", () => {
|
|
254
|
+
const value = backendSelect.value;
|
|
255
|
+
chrome.storage.sync.set({ browserBackend: value });
|
|
256
|
+
chrome.runtime.sendMessage({
|
|
257
|
+
action: "config.save",
|
|
258
|
+
key: "browserBackend",
|
|
259
|
+
value
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
loadBackendPreference();
|
|
263
|
+
refreshBridgeStatus();
|
|
264
|
+
checkAuth();
|
|
265
|
+
});
|
|
266
|
+
})();
|
|
267
|
+
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vc3JjL3BvcHVwLnRzIl0sCiAgInNvdXJjZXNDb250ZW50IjogWyIvLyBIYW56byBBSSBcdTIwMTQgUG9wdXAgU2NyaXB0XG4vLyBVc2VzIGJhY2tncm91bmQgbWVzc2FnZSBwYXNzaW5nIGZvciBhdXRoIChub3QgZGlyZWN0IHN0b3JhZ2UgYWNjZXNzKS5cblxuZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignRE9NQ29udGVudExvYWRlZCcsICgpID0+IHtcbiAgY29uc3QgbG9naW5TZWN0aW9uID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2xvZ2luLXNlY3Rpb24nKTtcbiAgY29uc3QgbWFpblNlY3Rpb24gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnbWFpbi1zZWN0aW9uJyk7XG4gIGNvbnN0IHNldHRpbmdzU2VjdGlvbiA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdzZXR0aW5ncy1zZWN0aW9uJyk7XG5cbiAgLy8gQXV0aFxuICBjb25zdCBsb2dpbkJ0biA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdsb2dpbi1idG4nKTtcbiAgY29uc3QgbG9nb3V0QnRuID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2xvZ291dC1idG4nKTtcblxuICAvLyBPcGVuIHBhbmVsXG4gIGNvbnN0IG9wZW5QYW5lbCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdvcGVuLXBhbmVsJyk7XG4gIGNvbnN0IG9wZW5QYWdlT3ZlcmxheSA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdvcGVuLXBhZ2Utb3ZlcmxheScpO1xuXG4gIC8vIFN0YXR1cyBkb3RzXG4gIGNvbnN0IHphcFN0YXR1cyA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd6YXAtc3RhdHVzJyk7XG4gIGNvbnN0IHphcERldGFpbCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd6YXAtZGV0YWlsJyk7XG4gIGNvbnN0IG1jcFN0YXR1cyA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdtY3Atc3RhdHVzJyk7XG4gIGNvbnN0IG1jcFBvcnQgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnbWNwLXBvcnQnKTtcbiAgY29uc3QgY2RwU3RhdHVzID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2NkcC1zdGF0dXMnKTtcbiAgY29uc3QgY2RwRGV0YWlsID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2NkcC1kZXRhaWwnKTtcbiAgY29uc3QgZ3B1U3RhdHVzID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2dwdS1zdGF0dXMnKTtcbiAgY29uc3QgZ3B1RGV0YWlsID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2dwdS1kZXRhaWwnKTtcblxuICAvLyBTZXR0aW5nc1xuICBjb25zdCBvcGVuU2V0dGluZ3MgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnb3Blbi1zZXR0aW5ncycpO1xuICBjb25zdCBiYWNrQnRuID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ2JhY2stYnRuJyk7XG4gIGNvbnN0IHNhdmVTZXR0aW5ncyA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdzYXZlLXNldHRpbmdzJyk7XG4gIGNvbnN0IHRlc3RDb25uZWN0aW9uID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3Rlc3QtY29ubmVjdGlvbicpO1xuXG4gIC8vIC0tLSBBdXRoIHZpYSBiYWNrZ3JvdW5kIC0tLVxuICBmdW5jdGlvbiBjaGVja0F1dGgoKSB7XG4gICAgLy8gQWx3YXlzIHNob3cgbWFpbiBzZWN0aW9uIGFuZCBzdGF0dXMgKHRvb2xzIHdvcmsgd2l0aG91dCBsb2dpbilcbiAgICBtYWluU2VjdGlvbi5jbGFzc0xpc3QucmVtb3ZlKCdoaWRkZW4nKTtcbiAgICByZWZyZXNoU3RhdHVzKCk7XG5cbiAgICBjaHJvbWUucnVudGltZS5zZW5kTWVzc2FnZSh7IGFjdGlvbjogJ2F1dGguc3RhdHVzJyB9LCAocmVzcG9uc2UpID0+IHtcbiAgICAgIGlmIChjaHJvbWUucnVudGltZS5sYXN0RXJyb3IpIHJldHVybjtcbiAgICAgIGlmIChyZXNwb25zZT8uc3VjY2VzcyAmJiByZXNwb25zZS5hdXRoZW50aWNhdGVkKSB7XG4gICAgICAgIGxvZ2luU2VjdGlvbi5jbGFzc0xpc3QuYWRkKCdoaWRkZW4nKTtcbiAgICAgICAgaWYgKHJlc3BvbnNlLnVzZXIpIHtcbiAgICAgICAgICBjb25zdCBhdmF0YXIgPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgndXNlci1hdmF0YXInKTtcbiAgICAgICAgICBjb25zdCBuYW1lID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3VzZXItbmFtZScpO1xuICAgICAgICAgIGNvbnN0IGVtYWlsID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3VzZXItZW1haWwnKTtcbiAgICAgICAgICBhdmF0YXIuc3JjID0gcmVzcG9uc2UudXNlci5waWN0dXJlIHx8IHJlc3BvbnNlLnVzZXIuYXZhdGFyIHx8ICdpY29uNDgucG5nJztcbiAgICAgICAgICBuYW1lLnRleHRDb250ZW50ID0gcmVzcG9uc2UudXNlci5uYW1lIHx8IHJlc3BvbnNlLnVzZXIuZGlzcGxheU5hbWUgfHwgJ0hhbnpvIFVzZXInO1xuICAgICAgICAgIGVtYWlsLnRleHRDb250ZW50ID0gcmVzcG9uc2UudXNlci5lbWFpbCB8fCAnJztcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgbG9naW5TZWN0aW9uLmNsYXNzTGlzdC5yZW1vdmUoJ2hpZGRlbicpO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG5cbiAgbG9naW5CdG4/LmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgKCkgPT4ge1xuICAgIGxvZ2luQnRuLmRpc2FibGVkID0gdHJ1ZTtcbiAgICBsb2dpbkJ0bi50ZXh0Q29udGVudCA9ICdTaWduaW5nIGluLi4uJztcbiAgICBjaHJvbWUucnVudGltZS5zZW5kTWVzc2FnZSh7IGFjdGlvbjogJ2F1dGgubG9naW4nIH0sIChyZXNwb25zZSkgPT4ge1xuICAgICAgbG9naW5CdG4uZGlzYWJsZWQgPSBmYWxzZTtcbiAgICAgIGxvZ2luQnRuLmlubmVySFRNTCA9ICc8c3ZnIGNsYXNzPVwiaWNvblwiIHZpZXdCb3g9XCIwIDAgMjQgMjRcIiBmaWxsPVwibm9uZVwiIHN0cm9rZT1cImN1cnJlbnRDb2xvclwiPjxwYXRoIGQ9XCJNMTUgM2g0YTIgMiAwIDAgMSAyIDJ2MTRhMiAyIDAgMCAxLTIgMmgtNE0xMCAxN2w1LTUtNS01TTE1IDEySDNcIi8+PC9zdmc+IFNpZ24gaW4gd2l0aCBIYW56byc7XG4gICAgICBpZiAocmVzcG9uc2U/LnN1Y2Nlc3MpIHtcbiAgICAgICAgY2hlY2tBdXRoKCk7XG4gICAgICB9XG4gICAgfSk7XG4gIH0pO1xuXG4gIGxvZ291dEJ0bj8uYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCAoKSA9PiB7XG4gICAgY2hyb21lLnJ1bnRpbWUuc2VuZE1lc3NhZ2UoeyBhY3Rpb246ICdhdXRoLmxvZ291dCcgfSwgKCkgPT4ge1xuICAgICAgbWFpblNlY3Rpb24uY2xhc3NMaXN0LmFkZCgnaGlkZGVuJyk7XG4gICAgICBsb2dpblNlY3Rpb24uY2xhc3NMaXN0LnJlbW92ZSgnaGlkZGVuJyk7XG4gICAgfSk7XG4gIH0pO1xuXG4gIC8vIC0tLSBPcGVuIHNpZGUgcGFuZWwgLS0tXG4gIG9wZW5QYW5lbD8uYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCAoKSA9PiB7XG4gICAgaWYgKGNocm9tZS5zaWRlUGFuZWwpIHtcbiAgICAgIGNocm9tZS50YWJzLnF1ZXJ5KHsgYWN0aXZlOiB0cnVlLCBjdXJyZW50V2luZG93OiB0cnVlIH0sICh0YWJzKSA9PiB7XG4gICAgICAgIGlmICh0YWJzWzBdPy5pZCkge1xuICAgICAgICAgIGNocm9tZS5zaWRlUGFuZWwub3Blbih7IHRhYklkOiB0YWJzWzBdLmlkIH0pO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gRmlyZWZveDogc2lkZWJhcl9hY3Rpb24gaXMgYXV0b21hdGljXG4gICAgICB3aW5kb3cuY2xvc2UoKTtcbiAgICB9XG4gIH0pO1xuXG4gIGZ1bmN0aW9uIGlzSW5qZWN0YWJsZVRhYih0YWI6IGNocm9tZS50YWJzLlRhYiB8IHVuZGVmaW5lZCk6IGJvb2xlYW4ge1xuICAgIGlmICghdGFiIHx8ICF0YWIuaWQpIHJldHVybiBmYWxzZTtcbiAgICBjb25zdCB1cmwgPSB0YWIudXJsIHx8ICcnO1xuICAgIGlmICghdXJsKSByZXR1cm4gZmFsc2U7XG4gICAgaWYgKHVybC5zdGFydHNXaXRoKCdjaHJvbWU6Ly8nKSkgcmV0dXJuIGZhbHNlO1xuICAgIGlmICh1cmwuc3RhcnRzV2l0aCgnY2hyb21lLWV4dGVuc2lvbjovLycpKSByZXR1cm4gZmFsc2U7XG4gICAgaWYgKHVybC5zdGFydHNXaXRoKCdkZXZ0b29sczovLycpKSByZXR1cm4gZmFsc2U7XG4gICAgaWYgKHVybC5zdGFydHNXaXRoKCdlZGdlOi8vJykpIHJldHVybiBmYWxzZTtcbiAgICBpZiAodXJsLnN0YXJ0c1dpdGgoJ2Fib3V0OicpKSByZXR1cm4gZmFsc2U7XG4gICAgaWYgKHVybC5zdGFydHNXaXRoKCd2aWV3LXNvdXJjZTonKSkgcmV0dXJuIGZhbHNlO1xuICAgIGlmICh1cmwuc3RhcnRzV2l0aCgnbW96LWV4dGVuc2lvbjovLycpKSByZXR1cm4gZmFsc2U7XG4gICAgcmV0dXJuIHRydWU7XG4gIH1cblxuICBvcGVuUGFnZU92ZXJsYXk/LmFkZEV2ZW50TGlzdGVuZXIoJ2NsaWNrJywgKCkgPT4ge1xuICAgIG9wZW5QYWdlT3ZlcmxheS50ZXh0Q29udGVudCA9ICdPcGVuaW5nLi4uJztcbiAgICBvcGVuUGFnZU92ZXJsYXkuc2V0QXR0cmlidXRlKCdkaXNhYmxlZCcsICd0cnVlJyk7XG5cbiAgICBjaHJvbWUudGFicy5xdWVyeSh7IGFjdGl2ZTogdHJ1ZSwgY3VycmVudFdpbmRvdzogdHJ1ZSB9LCAodGFicykgPT4ge1xuICAgICAgY29uc3QgYWN0aXZlVGFiID0gdGFic1swXTtcbiAgICAgIGxldCB0YXJnZXRUYWIgPSBpc0luamVjdGFibGVUYWIoYWN0aXZlVGFiKSA/IGFjdGl2ZVRhYiA6IG51bGw7XG5cbiAgICAgIGlmICghdGFyZ2V0VGFiKSB7XG4gICAgICAgIGNocm9tZS50YWJzLnF1ZXJ5KHsgY3VycmVudFdpbmRvdzogdHJ1ZSB9LCAoYWxsVGFicykgPT4ge1xuICAgICAgICAgIHRhcmdldFRhYiA9IGFsbFRhYnMuZmluZCgodGFiKSA9PiBpc0luamVjdGFibGVUYWIodGFiKSkgfHwgbnVsbDtcbiAgICAgICAgICBpZiAoIXRhcmdldFRhYj8uaWQpIHtcbiAgICAgICAgICAgIG9wZW5QYWdlT3ZlcmxheS50ZXh0Q29udGVudCA9ICdObyB2YWxpZCBwYWdlIHRhYic7XG4gICAgICAgICAgICBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgICAgICAgb3BlblBhZ2VPdmVybGF5LnRleHRDb250ZW50ID0gJ1RvZ2dsZSBPbi1QYWdlIE92ZXJsYXknO1xuICAgICAgICAgICAgICBvcGVuUGFnZU92ZXJsYXkucmVtb3ZlQXR0cmlidXRlKCdkaXNhYmxlZCcpO1xuICAgICAgICAgICAgfSwgMTIwMCk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgY2hyb21lLnJ1bnRpbWUuc2VuZE1lc3NhZ2UoeyBhY3Rpb246ICdwYWdlLm92ZXJsYXkudG9nZ2xlJywgdGFiSWQ6IHRhcmdldFRhYi5pZCB9LCAocmVzcG9uc2UpID0+IHtcbiAgICAgICAgICAgIGlmIChjaHJvbWUucnVudGltZS5sYXN0RXJyb3IgfHwgIXJlc3BvbnNlPy5zdWNjZXNzKSB7XG4gICAgICAgICAgICAgIG9wZW5QYWdlT3ZlcmxheS50ZXh0Q29udGVudCA9ICdVbmF2YWlsYWJsZSBvbiB0aGlzIHBhZ2UnO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgY29uc3Qgb3BlbmVkID0gISFyZXNwb25zZT8ub3BlbjtcbiAgICAgICAgICAgICAgb3BlblBhZ2VPdmVybGF5LnRleHRDb250ZW50ID0gb3BlbmVkID8gJ092ZXJsYXkgT3BlbmVkJyA6ICdPdmVybGF5IEhpZGRlbic7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgICAgICAgb3BlblBhZ2VPdmVybGF5LnRleHRDb250ZW50ID0gJ1RvZ2dsZSBPbi1QYWdlIE92ZXJsYXknO1xuICAgICAgICAgICAgICBvcGVuUGFnZU92ZXJsYXkucmVtb3ZlQXR0cmlidXRlKCdkaXNhYmxlZCcpO1xuICAgICAgICAgICAgfSwgOTAwKTtcbiAgICAgICAgICB9KTtcbiAgICAgICAgfSk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cblxuICAgICAgaWYgKCF0YXJnZXRUYWI/LmlkKSB7XG4gICAgICAgIG9wZW5QYWdlT3ZlcmxheS50ZXh0Q29udGVudCA9ICdObyBBY3RpdmUgVGFiJztcbiAgICAgICAgc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgICAgb3BlblBhZ2VPdmVybGF5LnRleHRDb250ZW50ID0gJ1RvZ2dsZSBPbi1QYWdlIE92ZXJsYXknO1xuICAgICAgICAgIG9wZW5QYWdlT3ZlcmxheS5yZW1vdmVBdHRyaWJ1dGUoJ2Rpc2FibGVkJyk7XG4gICAgICAgIH0sIDEyMDApO1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG5cbiAgICAgIGNocm9tZS5ydW50aW1lLnNlbmRNZXNzYWdlKHsgYWN0aW9uOiAncGFnZS5vdmVybGF5LnRvZ2dsZScsIHRhYklkOiB0YXJnZXRUYWIuaWQgfSwgKHJlc3BvbnNlKSA9PiB7XG4gICAgICAgIGlmIChjaHJvbWUucnVudGltZS5sYXN0RXJyb3IgfHwgIXJlc3BvbnNlPy5zdWNjZXNzKSB7XG4gICAgICAgICAgb3BlblBhZ2VPdmVybGF5LnRleHRDb250ZW50ID0gJ1VuYXZhaWxhYmxlIG9uIHRoaXMgcGFnZSc7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgY29uc3Qgb3BlbmVkID0gISFyZXNwb25zZT8ub3BlbjtcbiAgICAgICAgICBvcGVuUGFnZU92ZXJsYXkudGV4dENvbnRlbnQgPSBvcGVuZWQgPyAnT3ZlcmxheSBPcGVuZWQnIDogJ092ZXJsYXkgSGlkZGVuJztcbiAgICAgICAgfVxuICAgICAgICBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgICBvcGVuUGFnZU92ZXJsYXkudGV4dENvbnRlbnQgPSAnVG9nZ2xlIE9uLVBhZ2UgT3ZlcmxheSc7XG4gICAgICAgICAgb3BlblBhZ2VPdmVybGF5LnJlbW92ZUF0dHJpYnV0ZSgnZGlzYWJsZWQnKTtcbiAgICAgICAgfSwgOTAwKTtcbiAgICAgIH0pO1xuICAgIH0pO1xuICB9KTtcblxuICAvLyAtLS0gU3RhdHVzIC0tLVxuICBmdW5jdGlvbiByZWZyZXNoU3RhdHVzKCkge1xuICAgIC8vIFpBUCBzdGF0dXNcbiAgICBjaHJvbWUucnVudGltZS5zZW5kTWVzc2FnZSh7IGFjdGlvbjogJ3phcC5zdGF0dXMnIH0sIChyZXNwb25zZSkgPT4ge1xuICAgICAgaWYgKGNocm9tZS5ydW50aW1lLmxhc3RFcnJvcikgcmV0dXJuO1xuICAgICAgaWYgKHJlc3BvbnNlPy5zdWNjZXNzKSB7XG4gICAgICAgIGNvbnN0IHphcCA9IHJlc3BvbnNlLnphcDtcbiAgICAgICAgaWYgKHphcC5jb25uZWN0ZWQpIHtcbiAgICAgICAgICB6YXBTdGF0dXMuY2xhc3NMaXN0LmFkZCgnY29ubmVjdGVkJyk7XG4gICAgICAgICAgemFwU3RhdHVzLmNsYXNzTGlzdC5yZW1vdmUoJ2Rpc2Nvbm5lY3RlZCcpO1xuICAgICAgICAgIGNvbnN0IG1jcENvdW50ID0gemFwLm1jcHM/Lmxlbmd0aCB8fCAwO1xuICAgICAgICAgIGNvbnN0IHRvb2xDb3VudCA9IHphcC5tY3BzPy5yZWR1Y2UoKHN1bSwgbSkgPT4gc3VtICsgKG0udG9vbHM/Lmxlbmd0aCB8fCAwKSwgMCkgfHwgMDtcbiAgICAgICAgICB6YXBEZXRhaWwudGV4dENvbnRlbnQgPSBgJHttY3BDb3VudH0gTUNQKHMpLCAke3Rvb2xDb3VudH0gdG9vbHNgO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHphcFN0YXR1cy5jbGFzc0xpc3QucmVtb3ZlKCdjb25uZWN0ZWQnKTtcbiAgICAgICAgICB6YXBTdGF0dXMuY2xhc3NMaXN0LmFkZCgnZGlzY29ubmVjdGVkJyk7XG4gICAgICAgICAgemFwRGV0YWlsLnRleHRDb250ZW50ID0gJ05vdCBjb25uZWN0ZWQnO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSk7XG5cbiAgICAvLyBHUFUgc3RhdHVzXG4gICAgY2hyb21lLnJ1bnRpbWUuc2VuZE1lc3NhZ2UoeyBhY3Rpb246ICdydW5Mb2NhbEFJJywgcHJvbXB0OiAnJyB9LCAocmVzcG9uc2UpID0+IHtcbiAgICAgIGlmIChjaHJvbWUucnVudGltZS5sYXN0RXJyb3IpIHJldHVybjtcbiAgICAgIGdwdVN0YXR1cy5jbGFzc0xpc3QuYWRkKHJlc3BvbnNlPy5zdWNjZXNzID8gJ2Nvbm5lY3RlZCcgOiAnZGlzY29ubmVjdGVkJyk7XG4gICAgICBncHVTdGF0dXMuY2xhc3NMaXN0LnJlbW92ZShyZXNwb25zZT8uc3VjY2VzcyA/ICdkaXNjb25uZWN0ZWQnIDogJ2Nvbm5lY3RlZCcpO1xuICAgICAgZ3B1RGV0YWlsLnRleHRDb250ZW50ID0gcmVzcG9uc2U/LnN1Y2Nlc3MgPyAnQXZhaWxhYmxlJyA6ICdOb3QgYXZhaWxhYmxlJztcbiAgICB9KTtcblxuICAgIC8vIE1DUC9DRFAgXHUyMDE0IHNldCBhcyBhY3RpdmUgZm9yIG5vdyAoY2hlY2tlZCB2aWEgWkFQKVxuICAgIG1jcFN0YXR1cy5jbGFzc0xpc3QuYWRkKCdjb25uZWN0ZWQnKTtcbiAgICBtY3BQb3J0LnRleHRDb250ZW50ID0gJ0ZhbGxiYWNrJztcbiAgICBjZHBTdGF0dXMuY2xhc3NMaXN0LmFkZCgnY29ubmVjdGVkJyk7XG4gICAgY2RwRGV0YWlsLnRleHRDb250ZW50ID0gJ0FjdGl2ZSc7XG4gIH1cblxuICAvLyAtLS0gU2V0dGluZ3MgLS0tXG4gIG9wZW5TZXR0aW5ncz8uYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCAoKSA9PiB7XG4gICAgbWFpblNlY3Rpb24uY2xhc3NMaXN0LmFkZCgnaGlkZGVuJyk7XG4gICAgc2V0dGluZ3NTZWN0aW9uLmNsYXNzTGlzdC5yZW1vdmUoJ2hpZGRlbicpO1xuICAgIGxvYWRTZXR0aW5ncygpO1xuICB9KTtcblxuICBiYWNrQnRuPy5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsICgpID0+IHtcbiAgICBzZXR0aW5nc1NlY3Rpb24uY2xhc3NMaXN0LmFkZCgnaGlkZGVuJyk7XG4gICAgbWFpblNlY3Rpb24uY2xhc3NMaXN0LnJlbW92ZSgnaGlkZGVuJyk7XG4gIH0pO1xuXG4gIGZ1bmN0aW9uIGxvYWRTZXR0aW5ncygpIHtcbiAgICBjaHJvbWUuc3RvcmFnZS5sb2NhbC5nZXQoWydtY3BQb3J0JywgJ2NkcFBvcnQnLCAnemFwUG9ydHMnXSwgKHJlc3VsdCkgPT4ge1xuICAgICAgY29uc3QgbWNwUG9ydElucHV0ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ21jcC1wb3J0LXNldHRpbmcnKTtcbiAgICAgIGNvbnN0IGNkcFBvcnRJbnB1dCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdjZHAtcG9ydC1zZXR0aW5nJyk7XG4gICAgICBjb25zdCB6YXBQb3J0c0lucHV0ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3phcC1wb3J0cy1zZXR0aW5nJyk7XG5cbiAgICAgIGlmIChtY3BQb3J0SW5wdXQpIG1jcFBvcnRJbnB1dC52YWx1ZSA9IHJlc3VsdC5tY3BQb3J0IHx8IDMwMDE7XG4gICAgICBpZiAoY2RwUG9ydElucHV0KSBjZHBQb3J0SW5wdXQudmFsdWUgPSByZXN1bHQuY2RwUG9ydCB8fCA5MjIzO1xuICAgICAgaWYgKHphcFBvcnRzSW5wdXQpIHphcFBvcnRzSW5wdXQudmFsdWUgPSAocmVzdWx0LnphcFBvcnRzIHx8IFs5OTk5LCA5OTk4LCA5OTk3LCA5OTk2LCA5OTk1XSkuam9pbignLCcpO1xuICAgIH0pO1xuICB9XG5cbiAgc2F2ZVNldHRpbmdzPy5hZGRFdmVudExpc3RlbmVyKCdjbGljaycsICgpID0+IHtcbiAgICBjb25zdCBtY3BQb3J0VmFsID0gcGFyc2VJbnQoZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ21jcC1wb3J0LXNldHRpbmcnKT8udmFsdWUpIHx8IDMwMDE7XG4gICAgY29uc3QgY2RwUG9ydFZhbCA9IHBhcnNlSW50KGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdjZHAtcG9ydC1zZXR0aW5nJyk/LnZhbHVlKSB8fCA5MjIzO1xuICAgIGNvbnN0IHphcFBvcnRzVmFsID0gKGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd6YXAtcG9ydHMtc2V0dGluZycpPy52YWx1ZSB8fCAnJylcbiAgICAgIC5zcGxpdCgnLCcpLm1hcChwID0+IHBhcnNlSW50KHAudHJpbSgpKSkuZmlsdGVyKHAgPT4gcCA+IDAgJiYgcCA8IDY1NTM2KTtcblxuICAgIGNocm9tZS5zdG9yYWdlLmxvY2FsLnNldCh7XG4gICAgICBtY3BQb3J0OiBtY3BQb3J0VmFsLFxuICAgICAgY2RwUG9ydDogY2RwUG9ydFZhbCxcbiAgICAgIHphcFBvcnRzOiB6YXBQb3J0c1ZhbC5sZW5ndGggPyB6YXBQb3J0c1ZhbCA6IFs5OTk5LCA5OTk4LCA5OTk3LCA5OTk2LCA5OTk1XSxcbiAgICB9LCAoKSA9PiB7XG4gICAgICBzYXZlU2V0dGluZ3MudGV4dENvbnRlbnQgPSAnU2F2ZWQhJztcbiAgICAgIHNldFRpbWVvdXQoKCkgPT4geyBzYXZlU2V0dGluZ3MudGV4dENvbnRlbnQgPSAnU2F2ZSBTZXR0aW5ncyc7IH0sIDE1MDApO1xuICAgIH0pO1xuICB9KTtcblxuICAvLyAtLS0gVGVzdCBDb25uZWN0aW9uIC0tLVxuICB0ZXN0Q29ubmVjdGlvbj8uYWRkRXZlbnRMaXN0ZW5lcignY2xpY2snLCAoKSA9PiB7XG4gICAgdGVzdENvbm5lY3Rpb24udGV4dENvbnRlbnQgPSAnVGVzdGluZy4uLic7XG4gICAgdGVzdENvbm5lY3Rpb24uZGlzYWJsZWQgPSB0cnVlO1xuXG4gICAgY2hyb21lLnJ1bnRpbWUuc2VuZE1lc3NhZ2UoeyBhY3Rpb246ICd6YXAuZGlzY292ZXInIH0sICgpID0+IHtcbiAgICAgIHJlZnJlc2hTdGF0dXMoKTtcbiAgICAgIHRlc3RDb25uZWN0aW9uLnRleHRDb250ZW50ID0gJ1Rlc3QgQ29ubmVjdGlvbic7XG4gICAgICB0ZXN0Q29ubmVjdGlvbi5kaXNhYmxlZCA9IGZhbHNlO1xuICAgIH0pO1xuICB9KTtcblxuICAvLyBGZWF0dXJlIHRvZ2dsZXNcbiAgWydzb3VyY2UtbWFwcycsICd3ZWJncHUtYWknLCAnYnJvd3Nlci1jb250cm9sJywgJ3RhYi1maWxlc3lzdGVtJ10uZm9yRWFjaChpZCA9PiB7XG4gICAgY29uc3QgY2hlY2tib3ggPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZChpZCk7XG4gICAgaWYgKGNoZWNrYm94KSB7XG4gICAgICBjaHJvbWUuc3RvcmFnZS5sb2NhbC5nZXQoW2lkXSwgKHJlc3VsdCkgPT4ge1xuICAgICAgICBjaGVja2JveC5jaGVja2VkID0gcmVzdWx0W2lkXSAhPT0gZmFsc2U7IC8vIGRlZmF1bHQgb25cbiAgICAgIH0pO1xuICAgICAgY2hlY2tib3guYWRkRXZlbnRMaXN0ZW5lcignY2hhbmdlJywgKCkgPT4ge1xuICAgICAgICBjaHJvbWUuc3RvcmFnZS5sb2NhbC5zZXQoeyBbaWRdOiBjaGVja2JveC5jaGVja2VkIH0pO1xuICAgICAgfSk7XG4gICAgfVxuICB9KTtcblxuICAvLyAtLS0gQnJvd3NlciBCYWNrZW5kIFBpY2tlciAtLS1cbiAgY29uc3QgYmFja2VuZFNlbGVjdCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdicm93c2VyLWJhY2tlbmQnKSBhcyBIVE1MU2VsZWN0RWxlbWVudCB8IG51bGw7XG4gIGNvbnN0IGJyaWRnZVN0YXR1cyA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdicmlkZ2Utc3RhdHVzJyk7XG4gIGNvbnN0IGJyaWRnZURldGFpbCA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCdicmlkZ2UtZGV0YWlsJyk7XG5cbiAgZnVuY3Rpb24gbG9hZEJhY2tlbmRQcmVmZXJlbmNlKCkge1xuICAgIGNocm9tZS5zdG9yYWdlLnN5bmMuZ2V0KFsnYnJvd3NlckJhY2tlbmQnXSwgKHJlc3VsdCkgPT4ge1xuICAgICAgaWYgKGJhY2tlbmRTZWxlY3QgJiYgcmVzdWx0LmJyb3dzZXJCYWNrZW5kKSB7XG4gICAgICAgIGJhY2tlbmRTZWxlY3QudmFsdWUgPSByZXN1bHQuYnJvd3NlckJhY2tlbmQ7XG4gICAgICB9XG4gICAgfSk7XG4gIH1cblxuICBmdW5jdGlvbiByZWZyZXNoQnJpZGdlU3RhdHVzKCkge1xuICAgIGNocm9tZS5ydW50aW1lLnNlbmRNZXNzYWdlKHsgYWN0aW9uOiAnYnJpZGdlLnN0YXR1cycgfSwgKHJlc3BvbnNlKSA9PiB7XG4gICAgICBpZiAoY2hyb21lLnJ1bnRpbWUubGFzdEVycm9yKSByZXR1cm47XG4gICAgICBpZiAocmVzcG9uc2U/LnN1Y2Nlc3MgJiYgcmVzcG9uc2UuY29ubmVjdGVkKSB7XG4gICAgICAgIGJyaWRnZVN0YXR1cz8uY2xhc3NMaXN0LmFkZCgnY29ubmVjdGVkJyk7XG4gICAgICAgIGJyaWRnZVN0YXR1cz8uY2xhc3NMaXN0LnJlbW92ZSgnZGlzY29ubmVjdGVkJyk7XG4gICAgICAgIGNvbnN0IGJyb3dzZXJzID0gcmVzcG9uc2UuYnJvd3NlcnMgfHwgW107XG4gICAgICAgIGJyaWRnZURldGFpbC50ZXh0Q29udGVudCA9IGJyb3dzZXJzLmxlbmd0aFxuICAgICAgICAgID8gYENvbm5lY3RlZDogJHticm93c2Vycy5qb2luKCcsICcpfWBcbiAgICAgICAgICA6ICdDb25uZWN0ZWQnO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgYnJpZGdlU3RhdHVzPy5jbGFzc0xpc3QucmVtb3ZlKCdjb25uZWN0ZWQnKTtcbiAgICAgICAgYnJpZGdlU3RhdHVzPy5jbGFzc0xpc3QuYWRkKCdkaXNjb25uZWN0ZWQnKTtcbiAgICAgICAgYnJpZGdlRGV0YWlsLnRleHRDb250ZW50ID0gJ05vdCBjb25uZWN0ZWQnO1xuICAgICAgfVxuICAgIH0pO1xuICB9XG5cbiAgYmFja2VuZFNlbGVjdD8uYWRkRXZlbnRMaXN0ZW5lcignY2hhbmdlJywgKCkgPT4ge1xuICAgIGNvbnN0IHZhbHVlID0gYmFja2VuZFNlbGVjdC52YWx1ZTtcblxuICAgIC8vIFNhdmUgdG8gY2hyb21lLnN0b3JhZ2Uuc3luYyAoY3Jvc3MtZGV2aWNlKVxuICAgIGNocm9tZS5zdG9yYWdlLnN5bmMuc2V0KHsgYnJvd3NlckJhY2tlbmQ6IHZhbHVlIH0pO1xuXG4gICAgLy8gRm9yd2FyZCB0byBiYWNrZ3JvdW5kIHRvIHBlcnNpc3QgdmlhIENEUCBicmlkZ2UgKyBzeW5jIHRvIElBTVxuICAgIGNocm9tZS5ydW50aW1lLnNlbmRNZXNzYWdlKHtcbiAgICAgIGFjdGlvbjogJ2NvbmZpZy5zYXZlJyxcbiAgICAgIGtleTogJ2Jyb3dzZXJCYWNrZW5kJyxcbiAgICAgIHZhbHVlLFxuICAgIH0pO1xuICB9KTtcblxuICBsb2FkQmFja2VuZFByZWZlcmVuY2UoKTtcbiAgcmVmcmVzaEJyaWRnZVN0YXR1cygpO1xuXG4gIC8vIEluaXRcbiAgY2hlY2tBdXRoKCk7XG59KTtcbiJdLAogICJtYXBwaW5ncyI6ICI7OztBQUdBLFdBQVMsaUJBQWlCLG9CQUFvQixNQUFNO0FBQ2xELFVBQU0sZUFBZSxTQUFTLGVBQWUsZUFBZTtBQUM1RCxVQUFNLGNBQWMsU0FBUyxlQUFlLGNBQWM7QUFDMUQsVUFBTSxrQkFBa0IsU0FBUyxlQUFlLGtCQUFrQjtBQUdsRSxVQUFNLFdBQVcsU0FBUyxlQUFlLFdBQVc7QUFDcEQsVUFBTSxZQUFZLFNBQVMsZUFBZSxZQUFZO0FBR3RELFVBQU0sWUFBWSxTQUFTLGVBQWUsWUFBWTtBQUN0RCxVQUFNLGtCQUFrQixTQUFTLGVBQWUsbUJBQW1CO0FBR25FLFVBQU0sWUFBWSxTQUFTLGVBQWUsWUFBWTtBQUN0RCxVQUFNLFlBQVksU0FBUyxlQUFlLFlBQVk7QUFDdEQsVUFBTSxZQUFZLFNBQVMsZUFBZSxZ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QUUsR0FBRyxDQUFDLFdBQVc7QUFDekMsbUJBQVMsVUFBVSxPQUFPLEVBQUUsTUFBTTtBQUFBLFFBQ3BDLENBQUM7QUFDRCxpQkFBUyxpQkFBaUIsVUFBVSxNQUFNO0FBQ3hDLGlCQUFPLFFBQVEsTUFBTSxJQUFJLEVBQUUsQ0FBQyxFQUFFLEdBQUcsU0FBUyxRQUFRLENBQUM7QUFBQSxRQUNyRCxDQUFDO0FBQUEsTUFDSDtBQUFBLElBQ0YsQ0FBQztBQUdELFVBQU0sZ0JBQWdCLFNBQVMsZUFBZSxpQkFBaUI7QUFDL0QsVUFBTSxlQUFlLFNBQVMsZUFBZSxlQUFlO0FBQzVELFVBQU0sZUFBZSxTQUFTLGVBQWUsZUFBZTtBQUU1RCxhQUFTLHdCQUF3QjtBQUMvQixhQUFPLFFBQVEsS0FBSyxJQUFJLENBQUMsZ0JBQWdCLEdBQUcsQ0FBQyxXQUFXO0FBQ3RELFlBQUksaUJBQWlCLE9BQU8sZ0JBQWdCO0FBQzFDLHdCQUFjLFFBQVEsT0FBTztBQUFBLFFBQy9CO0FBQUEsTUFDRixDQUFDO0FBQUEsSUFDSDtBQUVBLGFBQVMsc0JBQXNCO0FBQzdCLGFBQU8sUUFBUSxZQUFZLEVBQUUsUUFBUSxnQkFBZ0IsR0FBRyxDQUFDLGFBQWE7QUFDcEUsWUFBSSxPQUFPLFFBQVEsVUFBVztBQUM5QixhQUFJLHFDQUFVLFlBQVcsU0FBUyxXQUFXO0FBQzNDLHVEQUFjLFVBQVUsSUFBSTtBQUM1Qix1REFBYyxVQUFVLE9BQU87QUFDL0IsZ0JBQU0sV0FBVyxTQUFTLFlBQVksQ0FBQztBQUN2Qyx1QkFBYSxjQUFjLFNBQVMsU0FDaEMsY0FBYyxTQUFTLEtBQUssSUFBSSxDQUFDLEtBQ2pDO0FBQUEsUUFDTixPQUFPO0FBQ0wsdURBQWMsVUFBVSxPQUFPO0FBQy9CLHVEQUFjLFVBQVUsSUFBSTtBQUM1Qix1QkFBYSxjQUFjO0FBQUEsUUFDN0I7QUFBQSxNQUNGLENBQUM7QUFBQSxJQUNIO0FBRUEsbURBQWUsaUJBQWlCLFVBQVUsTUFBTTtBQUM5QyxZQUFNLFFBQVEsY0FBYztBQUc1QixhQUFPLFFBQVEsS0FBSyxJQUFJLEVBQUUsZ0JBQWdCLE1BQU0sQ0FBQztBQUdqRCxhQUFPLFFBQVEsWUFBWTtBQUFBLFFBQ3pCLFFBQVE7QUFBQSxRQUNSLEtBQUs7QUFBQSxRQUNMO0FBQUEsTUFDRixDQUFDO0FBQUEsSUFDSDtBQUVBLDBCQUFzQjtBQUN0Qix3QkFBb0I7QUFHcEIsY0FBVTtBQUFBLEVBQ1osQ0FBQzsiLAogICJuYW1lcyI6IFsiX2EiXQp9Cg==
|