hypha-debugger 0.1.4 → 0.1.5
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/dist/debugger.d.ts +12 -0
- package/dist/hypha-debugger.js +212 -16
- package/dist/hypha-debugger.js.map +1 -1
- package/dist/hypha-debugger.min.js +4 -4
- package/dist/hypha-debugger.min.js.map +1 -1
- package/dist/hypha-debugger.mjs +212 -16
- package/dist/hypha-debugger.mjs.map +1 -1
- package/dist/services/navigate.d.ts +11 -1
- package/package.json +1 -1
package/dist/hypha-debugger.mjs
CHANGED
|
@@ -2127,20 +2127,123 @@ executeScript.__schema__ = {
|
|
|
2127
2127
|
};
|
|
2128
2128
|
|
|
2129
2129
|
/**
|
|
2130
|
-
* Navigation service.
|
|
2130
|
+
* Navigation service with auto-reconnect support.
|
|
2131
|
+
*
|
|
2132
|
+
* For agent-triggered reload() and same-origin navigate(), we use a "soft"
|
|
2133
|
+
* approach: fetch the target HTML, inject the debugger <script> tag, then
|
|
2134
|
+
* replace the document via document.write(). The injected script auto-starts
|
|
2135
|
+
* from sessionStorage config, so the debugger reconnects with the same
|
|
2136
|
+
* workspace and service ID. The agent's URL stays stable.
|
|
2137
|
+
*
|
|
2138
|
+
* For cross-origin navigate(), goBack(), and goForward(), we fall back to
|
|
2139
|
+
* normal navigation. The debugger config is saved to sessionStorage so
|
|
2140
|
+
* re-clicking the bookmarklet reconnects seamlessly.
|
|
2131
2141
|
*/
|
|
2142
|
+
const STORAGE_KEY$1 = "__hypha_debugger_config__";
|
|
2143
|
+
/** Read the saved script URL from sessionStorage, or fall back to CDN. */
|
|
2144
|
+
function getScriptUrl() {
|
|
2145
|
+
try {
|
|
2146
|
+
const raw = sessionStorage.getItem(STORAGE_KEY$1);
|
|
2147
|
+
if (raw) {
|
|
2148
|
+
const config = JSON.parse(raw);
|
|
2149
|
+
if (config.script_url)
|
|
2150
|
+
return config.script_url;
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
catch {
|
|
2154
|
+
// ignore
|
|
2155
|
+
}
|
|
2156
|
+
return "https://cdn.jsdelivr.net/npm/hypha-debugger/dist/hypha-debugger.min.js";
|
|
2157
|
+
}
|
|
2158
|
+
/**
|
|
2159
|
+
* Inject the debugger loader script into HTML before </body> (or append).
|
|
2160
|
+
* Also injects a tiny inline script that reads sessionStorage and passes
|
|
2161
|
+
* config to the debugger via a global, so autoStart() picks it up.
|
|
2162
|
+
*/
|
|
2163
|
+
function injectLoader(html, scriptUrl) {
|
|
2164
|
+
const loader = `<script src="${scriptUrl}"><\/script>`;
|
|
2165
|
+
if (html.includes("</body>")) {
|
|
2166
|
+
return html.replace("</body>", loader + "\n</body>");
|
|
2167
|
+
}
|
|
2168
|
+
if (html.includes("</html>")) {
|
|
2169
|
+
return html.replace("</html>", loader + "\n</html>");
|
|
2170
|
+
}
|
|
2171
|
+
return html + "\n" + loader;
|
|
2172
|
+
}
|
|
2173
|
+
/**
|
|
2174
|
+
* Perform a soft page replacement: fetch HTML, inject debugger script,
|
|
2175
|
+
* replace the document. Returns false if it can't be done (caller should
|
|
2176
|
+
* fall back to hard navigation).
|
|
2177
|
+
*/
|
|
2178
|
+
function softReplace(url, pushState) {
|
|
2179
|
+
const scriptUrl = getScriptUrl();
|
|
2180
|
+
fetch(url, { credentials: "same-origin", cache: "reload" })
|
|
2181
|
+
.then((response) => {
|
|
2182
|
+
if (!response.ok)
|
|
2183
|
+
throw new Error(`HTTP ${response.status}`);
|
|
2184
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
2185
|
+
if (!contentType.includes("text/html")) {
|
|
2186
|
+
throw new Error("Not HTML");
|
|
2187
|
+
}
|
|
2188
|
+
return response.text();
|
|
2189
|
+
})
|
|
2190
|
+
.then((html) => {
|
|
2191
|
+
const modified = injectLoader(html, scriptUrl);
|
|
2192
|
+
document.open();
|
|
2193
|
+
document.write(modified);
|
|
2194
|
+
document.close();
|
|
2195
|
+
if (pushState) {
|
|
2196
|
+
try {
|
|
2197
|
+
history.pushState({}, "", pushState);
|
|
2198
|
+
}
|
|
2199
|
+
catch {
|
|
2200
|
+
// ignore — URL might already match
|
|
2201
|
+
}
|
|
2202
|
+
}
|
|
2203
|
+
})
|
|
2204
|
+
.catch(() => {
|
|
2205
|
+
// Soft replace failed — fall back to hard navigation
|
|
2206
|
+
if (pushState) {
|
|
2207
|
+
window.location.href = pushState;
|
|
2208
|
+
}
|
|
2209
|
+
else {
|
|
2210
|
+
window.location.reload();
|
|
2211
|
+
}
|
|
2212
|
+
});
|
|
2213
|
+
}
|
|
2214
|
+
// ── navigate ──────────────────────────────────────────────────────────
|
|
2132
2215
|
function navigate(url) {
|
|
2133
2216
|
try {
|
|
2134
|
-
|
|
2135
|
-
|
|
2217
|
+
const targetUrl = new URL(url, location.href);
|
|
2218
|
+
const sameOrigin = targetUrl.origin === location.origin;
|
|
2219
|
+
if (sameOrigin) {
|
|
2220
|
+
// Soft navigate: fetch + inject + document.write, then pushState
|
|
2221
|
+
// Schedule after RPC response is sent
|
|
2222
|
+
setTimeout(() => softReplace(targetUrl.href, targetUrl.href), 150);
|
|
2223
|
+
return {
|
|
2224
|
+
success: true,
|
|
2225
|
+
message: `Navigating to ${url} (debugger will auto-reconnect)`,
|
|
2226
|
+
};
|
|
2227
|
+
}
|
|
2228
|
+
else {
|
|
2229
|
+
// Cross-origin: can't soft navigate, fall back to hard
|
|
2230
|
+
window.location.href = url;
|
|
2231
|
+
return {
|
|
2232
|
+
success: true,
|
|
2233
|
+
message: `Navigating to ${url} (cross-origin, debugger will disconnect)`,
|
|
2234
|
+
};
|
|
2235
|
+
}
|
|
2136
2236
|
}
|
|
2137
2237
|
catch (err) {
|
|
2138
|
-
return {
|
|
2238
|
+
return {
|
|
2239
|
+
success: false,
|
|
2240
|
+
message: `Navigation failed: ${err.message ?? err}`,
|
|
2241
|
+
};
|
|
2139
2242
|
}
|
|
2140
2243
|
}
|
|
2141
2244
|
navigate.__schema__ = {
|
|
2142
2245
|
name: "navigate",
|
|
2143
|
-
description: "Navigate the browser to a new URL.",
|
|
2246
|
+
description: "Navigate the browser to a new URL. For same-origin URLs, the debugger auto-reconnects. Cross-origin navigation will disconnect the debugger.",
|
|
2144
2247
|
parameters: {
|
|
2145
2248
|
type: "object",
|
|
2146
2249
|
properties: {
|
|
@@ -2152,18 +2255,25 @@ navigate.__schema__ = {
|
|
|
2152
2255
|
required: ["url"],
|
|
2153
2256
|
},
|
|
2154
2257
|
};
|
|
2258
|
+
// ── goBack / goForward ───────────────────────────────────────────────
|
|
2155
2259
|
function goBack() {
|
|
2156
2260
|
try {
|
|
2157
2261
|
window.history.back();
|
|
2158
|
-
return {
|
|
2262
|
+
return {
|
|
2263
|
+
success: true,
|
|
2264
|
+
message: "Navigated back (re-click bookmarklet to reconnect if needed)",
|
|
2265
|
+
};
|
|
2159
2266
|
}
|
|
2160
2267
|
catch (err) {
|
|
2161
|
-
return {
|
|
2268
|
+
return {
|
|
2269
|
+
success: false,
|
|
2270
|
+
message: `Back navigation failed: ${err.message ?? err}`,
|
|
2271
|
+
};
|
|
2162
2272
|
}
|
|
2163
2273
|
}
|
|
2164
2274
|
goBack.__schema__ = {
|
|
2165
2275
|
name: "goBack",
|
|
2166
|
-
description: "Navigate back in browser history.",
|
|
2276
|
+
description: "Navigate back in browser history. The debugger may disconnect — re-click the bookmarklet to reconnect.",
|
|
2167
2277
|
parameters: {
|
|
2168
2278
|
type: "object",
|
|
2169
2279
|
properties: {},
|
|
@@ -2172,7 +2282,10 @@ goBack.__schema__ = {
|
|
|
2172
2282
|
function goForward() {
|
|
2173
2283
|
try {
|
|
2174
2284
|
window.history.forward();
|
|
2175
|
-
return {
|
|
2285
|
+
return {
|
|
2286
|
+
success: true,
|
|
2287
|
+
message: "Navigated forward (re-click bookmarklet to reconnect if needed)",
|
|
2288
|
+
};
|
|
2176
2289
|
}
|
|
2177
2290
|
catch (err) {
|
|
2178
2291
|
return {
|
|
@@ -2183,16 +2296,21 @@ function goForward() {
|
|
|
2183
2296
|
}
|
|
2184
2297
|
goForward.__schema__ = {
|
|
2185
2298
|
name: "goForward",
|
|
2186
|
-
description: "Navigate forward in browser history.",
|
|
2299
|
+
description: "Navigate forward in browser history. The debugger may disconnect — re-click the bookmarklet to reconnect.",
|
|
2187
2300
|
parameters: {
|
|
2188
2301
|
type: "object",
|
|
2189
2302
|
properties: {},
|
|
2190
2303
|
},
|
|
2191
2304
|
};
|
|
2305
|
+
// ── reload ───────────────────────────────────────────────────────────
|
|
2192
2306
|
function reload() {
|
|
2193
2307
|
try {
|
|
2194
|
-
|
|
2195
|
-
|
|
2308
|
+
// Schedule soft reload after RPC response is sent
|
|
2309
|
+
setTimeout(() => softReplace(location.href), 150);
|
|
2310
|
+
return {
|
|
2311
|
+
success: true,
|
|
2312
|
+
message: "Reloading page (debugger will auto-reconnect)",
|
|
2313
|
+
};
|
|
2196
2314
|
}
|
|
2197
2315
|
catch (err) {
|
|
2198
2316
|
return { success: false, message: `Reload failed: ${err.message ?? err}` };
|
|
@@ -2200,7 +2318,7 @@ function reload() {
|
|
|
2200
2318
|
}
|
|
2201
2319
|
reload.__schema__ = {
|
|
2202
2320
|
name: "reload",
|
|
2203
|
-
description: "Reload the current page.",
|
|
2321
|
+
description: "Reload the current page. The debugger auto-reconnects after reload using soft page replacement.",
|
|
2204
2322
|
parameters: {
|
|
2205
2323
|
type: "object",
|
|
2206
2324
|
properties: {},
|
|
@@ -5311,12 +5429,15 @@ function randomHex(bytes = 8) {
|
|
|
5311
5429
|
crypto.getRandomValues(arr);
|
|
5312
5430
|
return Array.from(arr, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
5313
5431
|
}
|
|
5432
|
+
/** sessionStorage key for persisting debugger config across reloads. */
|
|
5433
|
+
const STORAGE_KEY = "__hypha_debugger_config__";
|
|
5314
5434
|
class HyphaDebugger {
|
|
5315
5435
|
constructor(config) {
|
|
5316
5436
|
this.overlay = null;
|
|
5317
5437
|
this.cursor = null;
|
|
5318
5438
|
this.server = null;
|
|
5319
5439
|
this.serviceInfo = null;
|
|
5440
|
+
this.boundBeforeUnload = null;
|
|
5320
5441
|
const requireToken = config.require_token ?? false;
|
|
5321
5442
|
// Always append random suffix unless user provided a custom id.
|
|
5322
5443
|
let serviceId = config.service_id ?? "web-debugger";
|
|
@@ -5376,6 +5497,10 @@ class HyphaDebugger {
|
|
|
5376
5497
|
// Store globally
|
|
5377
5498
|
w.__HYPHA_DEBUGGER__ = w.__HYPHA_DEBUGGER__ ?? {};
|
|
5378
5499
|
w.__HYPHA_DEBUGGER__.instance = this;
|
|
5500
|
+
// Persist config to sessionStorage for auto-reconnect after reload
|
|
5501
|
+
this.saveConfigToStorage();
|
|
5502
|
+
this.boundBeforeUnload = () => this.saveConfigToStorage();
|
|
5503
|
+
window.addEventListener("beforeunload", this.boundBeforeUnload);
|
|
5379
5504
|
return session;
|
|
5380
5505
|
}
|
|
5381
5506
|
catch (err) {
|
|
@@ -5391,6 +5516,11 @@ class HyphaDebugger {
|
|
|
5391
5516
|
}
|
|
5392
5517
|
}
|
|
5393
5518
|
async destroy() {
|
|
5519
|
+
// Remove beforeunload listener
|
|
5520
|
+
if (this.boundBeforeUnload) {
|
|
5521
|
+
window.removeEventListener("beforeunload", this.boundBeforeUnload);
|
|
5522
|
+
this.boundBeforeUnload = null;
|
|
5523
|
+
}
|
|
5394
5524
|
try {
|
|
5395
5525
|
if (this.serviceInfo && this.server) {
|
|
5396
5526
|
await this.server.unregisterService(this.serviceInfo.id);
|
|
@@ -5399,6 +5529,13 @@ class HyphaDebugger {
|
|
|
5399
5529
|
catch {
|
|
5400
5530
|
// Ignore unregister errors on cleanup
|
|
5401
5531
|
}
|
|
5532
|
+
// Clear sessionStorage config (explicit destroy = user wants to stop)
|
|
5533
|
+
try {
|
|
5534
|
+
sessionStorage.removeItem(STORAGE_KEY);
|
|
5535
|
+
}
|
|
5536
|
+
catch {
|
|
5537
|
+
// ignore
|
|
5538
|
+
}
|
|
5402
5539
|
disposeController();
|
|
5403
5540
|
this.cursor?.destroy();
|
|
5404
5541
|
this.cursor = null;
|
|
@@ -5410,6 +5547,48 @@ class HyphaDebugger {
|
|
|
5410
5547
|
delete w.__HYPHA_DEBUGGER__.session;
|
|
5411
5548
|
}
|
|
5412
5549
|
}
|
|
5550
|
+
/**
|
|
5551
|
+
* Persist debugger config to sessionStorage so the debugger can
|
|
5552
|
+
* auto-reconnect after a page reload (soft reload injects the script,
|
|
5553
|
+
* autoStart() reads this config).
|
|
5554
|
+
*/
|
|
5555
|
+
saveConfigToStorage() {
|
|
5556
|
+
try {
|
|
5557
|
+
const data = {
|
|
5558
|
+
server_url: this.config.server_url,
|
|
5559
|
+
workspace: this.server?.config?.workspace ?? this.config.workspace,
|
|
5560
|
+
token: this.config.token,
|
|
5561
|
+
service_id: this.config.service_id,
|
|
5562
|
+
service_name: this.config.service_name,
|
|
5563
|
+
show_ui: this.config.show_ui,
|
|
5564
|
+
visibility: this.config.visibility,
|
|
5565
|
+
require_token: this.config.require_token,
|
|
5566
|
+
script_url: this.detectScriptUrl(),
|
|
5567
|
+
};
|
|
5568
|
+
sessionStorage.setItem(STORAGE_KEY, JSON.stringify(data));
|
|
5569
|
+
}
|
|
5570
|
+
catch {
|
|
5571
|
+
// sessionStorage might be unavailable (private browsing, full quota)
|
|
5572
|
+
}
|
|
5573
|
+
}
|
|
5574
|
+
/**
|
|
5575
|
+
* Detect the URL of the currently loaded hypha-debugger script.
|
|
5576
|
+
* Used by navigate.ts to inject the correct script after soft reload.
|
|
5577
|
+
*/
|
|
5578
|
+
detectScriptUrl() {
|
|
5579
|
+
try {
|
|
5580
|
+
const scripts = document.querySelectorAll("script[src]");
|
|
5581
|
+
for (const s of Array.from(scripts)) {
|
|
5582
|
+
if (s.src && s.src.includes("hypha-debugger")) {
|
|
5583
|
+
return s.src;
|
|
5584
|
+
}
|
|
5585
|
+
}
|
|
5586
|
+
}
|
|
5587
|
+
catch {
|
|
5588
|
+
// ignore
|
|
5589
|
+
}
|
|
5590
|
+
return "https://cdn.jsdelivr.net/npm/hypha-debugger/dist/hypha-debugger.min.js";
|
|
5591
|
+
}
|
|
5413
5592
|
/**
|
|
5414
5593
|
* Generate token, build service URL, update overlay instructions, and
|
|
5415
5594
|
* return a DebugSession.
|
|
@@ -5665,6 +5844,26 @@ async function startDebugger(config) {
|
|
|
5665
5844
|
function autoStart() {
|
|
5666
5845
|
if (typeof window === "undefined" || typeof document === "undefined")
|
|
5667
5846
|
return;
|
|
5847
|
+
// Skip if already started
|
|
5848
|
+
if (window.__HYPHA_DEBUGGER__?.instance)
|
|
5849
|
+
return;
|
|
5850
|
+
// Check sessionStorage for saved config (auto-reconnect after soft reload)
|
|
5851
|
+
try {
|
|
5852
|
+
const saved = sessionStorage.getItem("__hypha_debugger_config__");
|
|
5853
|
+
if (saved) {
|
|
5854
|
+
const savedConfig = JSON.parse(saved);
|
|
5855
|
+
if (savedConfig.server_url) {
|
|
5856
|
+
console.log("[hypha-debugger] Reconnecting from saved session...");
|
|
5857
|
+
startDebugger(savedConfig).catch((err) => {
|
|
5858
|
+
console.error("[hypha-debugger] Auto-reconnect failed:", err);
|
|
5859
|
+
});
|
|
5860
|
+
return;
|
|
5861
|
+
}
|
|
5862
|
+
}
|
|
5863
|
+
}
|
|
5864
|
+
catch {
|
|
5865
|
+
// sessionStorage not available or parse error — continue to script tag detection
|
|
5866
|
+
}
|
|
5668
5867
|
// Find our own script tag
|
|
5669
5868
|
const scripts = document.querySelectorAll("script[src]");
|
|
5670
5869
|
let scriptEl = null;
|
|
@@ -5677,9 +5876,6 @@ function autoStart() {
|
|
|
5677
5876
|
// Skip if data-manual is set
|
|
5678
5877
|
if (scriptEl?.hasAttribute("data-manual"))
|
|
5679
5878
|
return;
|
|
5680
|
-
// Skip if already started
|
|
5681
|
-
if (window.__HYPHA_DEBUGGER__?.instance)
|
|
5682
|
-
return;
|
|
5683
5879
|
const serverUrl = scriptEl?.getAttribute("data-server-url") ?? "https://hypha.aicell.io";
|
|
5684
5880
|
const config = {
|
|
5685
5881
|
server_url: serverUrl,
|