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/debugger.d.ts
CHANGED
|
@@ -45,9 +45,21 @@ export declare class HyphaDebugger {
|
|
|
45
45
|
private cursor;
|
|
46
46
|
private server;
|
|
47
47
|
private serviceInfo;
|
|
48
|
+
private boundBeforeUnload;
|
|
48
49
|
constructor(config: DebuggerConfig);
|
|
49
50
|
start(): Promise<DebugSession>;
|
|
50
51
|
destroy(): Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Persist debugger config to sessionStorage so the debugger can
|
|
54
|
+
* auto-reconnect after a page reload (soft reload injects the script,
|
|
55
|
+
* autoStart() reads this config).
|
|
56
|
+
*/
|
|
57
|
+
private saveConfigToStorage;
|
|
58
|
+
/**
|
|
59
|
+
* Detect the URL of the currently loaded hypha-debugger script.
|
|
60
|
+
* Used by navigate.ts to inject the correct script after soft reload.
|
|
61
|
+
*/
|
|
62
|
+
private detectScriptUrl;
|
|
51
63
|
/**
|
|
52
64
|
* Generate token, build service URL, update overlay instructions, and
|
|
53
65
|
* return a DebugSession.
|
package/dist/hypha-debugger.js
CHANGED
|
@@ -10584,20 +10584,123 @@
|
|
|
10584
10584
|
};
|
|
10585
10585
|
|
|
10586
10586
|
/**
|
|
10587
|
-
* Navigation service.
|
|
10587
|
+
* Navigation service with auto-reconnect support.
|
|
10588
|
+
*
|
|
10589
|
+
* For agent-triggered reload() and same-origin navigate(), we use a "soft"
|
|
10590
|
+
* approach: fetch the target HTML, inject the debugger <script> tag, then
|
|
10591
|
+
* replace the document via document.write(). The injected script auto-starts
|
|
10592
|
+
* from sessionStorage config, so the debugger reconnects with the same
|
|
10593
|
+
* workspace and service ID. The agent's URL stays stable.
|
|
10594
|
+
*
|
|
10595
|
+
* For cross-origin navigate(), goBack(), and goForward(), we fall back to
|
|
10596
|
+
* normal navigation. The debugger config is saved to sessionStorage so
|
|
10597
|
+
* re-clicking the bookmarklet reconnects seamlessly.
|
|
10588
10598
|
*/
|
|
10599
|
+
const STORAGE_KEY$1 = "__hypha_debugger_config__";
|
|
10600
|
+
/** Read the saved script URL from sessionStorage, or fall back to CDN. */
|
|
10601
|
+
function getScriptUrl() {
|
|
10602
|
+
try {
|
|
10603
|
+
const raw = sessionStorage.getItem(STORAGE_KEY$1);
|
|
10604
|
+
if (raw) {
|
|
10605
|
+
const config = JSON.parse(raw);
|
|
10606
|
+
if (config.script_url)
|
|
10607
|
+
return config.script_url;
|
|
10608
|
+
}
|
|
10609
|
+
}
|
|
10610
|
+
catch {
|
|
10611
|
+
// ignore
|
|
10612
|
+
}
|
|
10613
|
+
return "https://cdn.jsdelivr.net/npm/hypha-debugger/dist/hypha-debugger.min.js";
|
|
10614
|
+
}
|
|
10615
|
+
/**
|
|
10616
|
+
* Inject the debugger loader script into HTML before </body> (or append).
|
|
10617
|
+
* Also injects a tiny inline script that reads sessionStorage and passes
|
|
10618
|
+
* config to the debugger via a global, so autoStart() picks it up.
|
|
10619
|
+
*/
|
|
10620
|
+
function injectLoader(html, scriptUrl) {
|
|
10621
|
+
const loader = `<script src="${scriptUrl}"><\/script>`;
|
|
10622
|
+
if (html.includes("</body>")) {
|
|
10623
|
+
return html.replace("</body>", loader + "\n</body>");
|
|
10624
|
+
}
|
|
10625
|
+
if (html.includes("</html>")) {
|
|
10626
|
+
return html.replace("</html>", loader + "\n</html>");
|
|
10627
|
+
}
|
|
10628
|
+
return html + "\n" + loader;
|
|
10629
|
+
}
|
|
10630
|
+
/**
|
|
10631
|
+
* Perform a soft page replacement: fetch HTML, inject debugger script,
|
|
10632
|
+
* replace the document. Returns false if it can't be done (caller should
|
|
10633
|
+
* fall back to hard navigation).
|
|
10634
|
+
*/
|
|
10635
|
+
function softReplace(url, pushState) {
|
|
10636
|
+
const scriptUrl = getScriptUrl();
|
|
10637
|
+
fetch(url, { credentials: "same-origin", cache: "reload" })
|
|
10638
|
+
.then((response) => {
|
|
10639
|
+
if (!response.ok)
|
|
10640
|
+
throw new Error(`HTTP ${response.status}`);
|
|
10641
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
10642
|
+
if (!contentType.includes("text/html")) {
|
|
10643
|
+
throw new Error("Not HTML");
|
|
10644
|
+
}
|
|
10645
|
+
return response.text();
|
|
10646
|
+
})
|
|
10647
|
+
.then((html) => {
|
|
10648
|
+
const modified = injectLoader(html, scriptUrl);
|
|
10649
|
+
document.open();
|
|
10650
|
+
document.write(modified);
|
|
10651
|
+
document.close();
|
|
10652
|
+
if (pushState) {
|
|
10653
|
+
try {
|
|
10654
|
+
history.pushState({}, "", pushState);
|
|
10655
|
+
}
|
|
10656
|
+
catch {
|
|
10657
|
+
// ignore — URL might already match
|
|
10658
|
+
}
|
|
10659
|
+
}
|
|
10660
|
+
})
|
|
10661
|
+
.catch(() => {
|
|
10662
|
+
// Soft replace failed — fall back to hard navigation
|
|
10663
|
+
if (pushState) {
|
|
10664
|
+
window.location.href = pushState;
|
|
10665
|
+
}
|
|
10666
|
+
else {
|
|
10667
|
+
window.location.reload();
|
|
10668
|
+
}
|
|
10669
|
+
});
|
|
10670
|
+
}
|
|
10671
|
+
// ── navigate ──────────────────────────────────────────────────────────
|
|
10589
10672
|
function navigate(url) {
|
|
10590
10673
|
try {
|
|
10591
|
-
|
|
10592
|
-
|
|
10674
|
+
const targetUrl = new URL(url, location.href);
|
|
10675
|
+
const sameOrigin = targetUrl.origin === location.origin;
|
|
10676
|
+
if (sameOrigin) {
|
|
10677
|
+
// Soft navigate: fetch + inject + document.write, then pushState
|
|
10678
|
+
// Schedule after RPC response is sent
|
|
10679
|
+
setTimeout(() => softReplace(targetUrl.href, targetUrl.href), 150);
|
|
10680
|
+
return {
|
|
10681
|
+
success: true,
|
|
10682
|
+
message: `Navigating to ${url} (debugger will auto-reconnect)`,
|
|
10683
|
+
};
|
|
10684
|
+
}
|
|
10685
|
+
else {
|
|
10686
|
+
// Cross-origin: can't soft navigate, fall back to hard
|
|
10687
|
+
window.location.href = url;
|
|
10688
|
+
return {
|
|
10689
|
+
success: true,
|
|
10690
|
+
message: `Navigating to ${url} (cross-origin, debugger will disconnect)`,
|
|
10691
|
+
};
|
|
10692
|
+
}
|
|
10593
10693
|
}
|
|
10594
10694
|
catch (err) {
|
|
10595
|
-
return {
|
|
10695
|
+
return {
|
|
10696
|
+
success: false,
|
|
10697
|
+
message: `Navigation failed: ${err.message ?? err}`,
|
|
10698
|
+
};
|
|
10596
10699
|
}
|
|
10597
10700
|
}
|
|
10598
10701
|
navigate.__schema__ = {
|
|
10599
10702
|
name: "navigate",
|
|
10600
|
-
description: "Navigate the browser to a new URL.",
|
|
10703
|
+
description: "Navigate the browser to a new URL. For same-origin URLs, the debugger auto-reconnects. Cross-origin navigation will disconnect the debugger.",
|
|
10601
10704
|
parameters: {
|
|
10602
10705
|
type: "object",
|
|
10603
10706
|
properties: {
|
|
@@ -10609,18 +10712,25 @@
|
|
|
10609
10712
|
required: ["url"],
|
|
10610
10713
|
},
|
|
10611
10714
|
};
|
|
10715
|
+
// ── goBack / goForward ───────────────────────────────────────────────
|
|
10612
10716
|
function goBack() {
|
|
10613
10717
|
try {
|
|
10614
10718
|
window.history.back();
|
|
10615
|
-
return {
|
|
10719
|
+
return {
|
|
10720
|
+
success: true,
|
|
10721
|
+
message: "Navigated back (re-click bookmarklet to reconnect if needed)",
|
|
10722
|
+
};
|
|
10616
10723
|
}
|
|
10617
10724
|
catch (err) {
|
|
10618
|
-
return {
|
|
10725
|
+
return {
|
|
10726
|
+
success: false,
|
|
10727
|
+
message: `Back navigation failed: ${err.message ?? err}`,
|
|
10728
|
+
};
|
|
10619
10729
|
}
|
|
10620
10730
|
}
|
|
10621
10731
|
goBack.__schema__ = {
|
|
10622
10732
|
name: "goBack",
|
|
10623
|
-
description: "Navigate back in browser history.",
|
|
10733
|
+
description: "Navigate back in browser history. The debugger may disconnect — re-click the bookmarklet to reconnect.",
|
|
10624
10734
|
parameters: {
|
|
10625
10735
|
type: "object",
|
|
10626
10736
|
properties: {},
|
|
@@ -10629,7 +10739,10 @@
|
|
|
10629
10739
|
function goForward() {
|
|
10630
10740
|
try {
|
|
10631
10741
|
window.history.forward();
|
|
10632
|
-
return {
|
|
10742
|
+
return {
|
|
10743
|
+
success: true,
|
|
10744
|
+
message: "Navigated forward (re-click bookmarklet to reconnect if needed)",
|
|
10745
|
+
};
|
|
10633
10746
|
}
|
|
10634
10747
|
catch (err) {
|
|
10635
10748
|
return {
|
|
@@ -10640,16 +10753,21 @@
|
|
|
10640
10753
|
}
|
|
10641
10754
|
goForward.__schema__ = {
|
|
10642
10755
|
name: "goForward",
|
|
10643
|
-
description: "Navigate forward in browser history.",
|
|
10756
|
+
description: "Navigate forward in browser history. The debugger may disconnect — re-click the bookmarklet to reconnect.",
|
|
10644
10757
|
parameters: {
|
|
10645
10758
|
type: "object",
|
|
10646
10759
|
properties: {},
|
|
10647
10760
|
},
|
|
10648
10761
|
};
|
|
10762
|
+
// ── reload ───────────────────────────────────────────────────────────
|
|
10649
10763
|
function reload() {
|
|
10650
10764
|
try {
|
|
10651
|
-
|
|
10652
|
-
|
|
10765
|
+
// Schedule soft reload after RPC response is sent
|
|
10766
|
+
setTimeout(() => softReplace(location.href), 150);
|
|
10767
|
+
return {
|
|
10768
|
+
success: true,
|
|
10769
|
+
message: "Reloading page (debugger will auto-reconnect)",
|
|
10770
|
+
};
|
|
10653
10771
|
}
|
|
10654
10772
|
catch (err) {
|
|
10655
10773
|
return { success: false, message: `Reload failed: ${err.message ?? err}` };
|
|
@@ -10657,7 +10775,7 @@
|
|
|
10657
10775
|
}
|
|
10658
10776
|
reload.__schema__ = {
|
|
10659
10777
|
name: "reload",
|
|
10660
|
-
description: "Reload the current page.",
|
|
10778
|
+
description: "Reload the current page. The debugger auto-reconnects after reload using soft page replacement.",
|
|
10661
10779
|
parameters: {
|
|
10662
10780
|
type: "object",
|
|
10663
10781
|
properties: {},
|
|
@@ -13768,12 +13886,15 @@
|
|
|
13768
13886
|
crypto.getRandomValues(arr);
|
|
13769
13887
|
return Array.from(arr, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
13770
13888
|
}
|
|
13889
|
+
/** sessionStorage key for persisting debugger config across reloads. */
|
|
13890
|
+
const STORAGE_KEY = "__hypha_debugger_config__";
|
|
13771
13891
|
class HyphaDebugger {
|
|
13772
13892
|
constructor(config) {
|
|
13773
13893
|
this.overlay = null;
|
|
13774
13894
|
this.cursor = null;
|
|
13775
13895
|
this.server = null;
|
|
13776
13896
|
this.serviceInfo = null;
|
|
13897
|
+
this.boundBeforeUnload = null;
|
|
13777
13898
|
const requireToken = config.require_token ?? false;
|
|
13778
13899
|
// Always append random suffix unless user provided a custom id.
|
|
13779
13900
|
let serviceId = config.service_id ?? "web-debugger";
|
|
@@ -13833,6 +13954,10 @@
|
|
|
13833
13954
|
// Store globally
|
|
13834
13955
|
w.__HYPHA_DEBUGGER__ = w.__HYPHA_DEBUGGER__ ?? {};
|
|
13835
13956
|
w.__HYPHA_DEBUGGER__.instance = this;
|
|
13957
|
+
// Persist config to sessionStorage for auto-reconnect after reload
|
|
13958
|
+
this.saveConfigToStorage();
|
|
13959
|
+
this.boundBeforeUnload = () => this.saveConfigToStorage();
|
|
13960
|
+
window.addEventListener("beforeunload", this.boundBeforeUnload);
|
|
13836
13961
|
return session;
|
|
13837
13962
|
}
|
|
13838
13963
|
catch (err) {
|
|
@@ -13848,6 +13973,11 @@
|
|
|
13848
13973
|
}
|
|
13849
13974
|
}
|
|
13850
13975
|
async destroy() {
|
|
13976
|
+
// Remove beforeunload listener
|
|
13977
|
+
if (this.boundBeforeUnload) {
|
|
13978
|
+
window.removeEventListener("beforeunload", this.boundBeforeUnload);
|
|
13979
|
+
this.boundBeforeUnload = null;
|
|
13980
|
+
}
|
|
13851
13981
|
try {
|
|
13852
13982
|
if (this.serviceInfo && this.server) {
|
|
13853
13983
|
await this.server.unregisterService(this.serviceInfo.id);
|
|
@@ -13856,6 +13986,13 @@
|
|
|
13856
13986
|
catch {
|
|
13857
13987
|
// Ignore unregister errors on cleanup
|
|
13858
13988
|
}
|
|
13989
|
+
// Clear sessionStorage config (explicit destroy = user wants to stop)
|
|
13990
|
+
try {
|
|
13991
|
+
sessionStorage.removeItem(STORAGE_KEY);
|
|
13992
|
+
}
|
|
13993
|
+
catch {
|
|
13994
|
+
// ignore
|
|
13995
|
+
}
|
|
13859
13996
|
disposeController();
|
|
13860
13997
|
this.cursor?.destroy();
|
|
13861
13998
|
this.cursor = null;
|
|
@@ -13867,6 +14004,48 @@
|
|
|
13867
14004
|
delete w.__HYPHA_DEBUGGER__.session;
|
|
13868
14005
|
}
|
|
13869
14006
|
}
|
|
14007
|
+
/**
|
|
14008
|
+
* Persist debugger config to sessionStorage so the debugger can
|
|
14009
|
+
* auto-reconnect after a page reload (soft reload injects the script,
|
|
14010
|
+
* autoStart() reads this config).
|
|
14011
|
+
*/
|
|
14012
|
+
saveConfigToStorage() {
|
|
14013
|
+
try {
|
|
14014
|
+
const data = {
|
|
14015
|
+
server_url: this.config.server_url,
|
|
14016
|
+
workspace: this.server?.config?.workspace ?? this.config.workspace,
|
|
14017
|
+
token: this.config.token,
|
|
14018
|
+
service_id: this.config.service_id,
|
|
14019
|
+
service_name: this.config.service_name,
|
|
14020
|
+
show_ui: this.config.show_ui,
|
|
14021
|
+
visibility: this.config.visibility,
|
|
14022
|
+
require_token: this.config.require_token,
|
|
14023
|
+
script_url: this.detectScriptUrl(),
|
|
14024
|
+
};
|
|
14025
|
+
sessionStorage.setItem(STORAGE_KEY, JSON.stringify(data));
|
|
14026
|
+
}
|
|
14027
|
+
catch {
|
|
14028
|
+
// sessionStorage might be unavailable (private browsing, full quota)
|
|
14029
|
+
}
|
|
14030
|
+
}
|
|
14031
|
+
/**
|
|
14032
|
+
* Detect the URL of the currently loaded hypha-debugger script.
|
|
14033
|
+
* Used by navigate.ts to inject the correct script after soft reload.
|
|
14034
|
+
*/
|
|
14035
|
+
detectScriptUrl() {
|
|
14036
|
+
try {
|
|
14037
|
+
const scripts = document.querySelectorAll("script[src]");
|
|
14038
|
+
for (const s of Array.from(scripts)) {
|
|
14039
|
+
if (s.src && s.src.includes("hypha-debugger")) {
|
|
14040
|
+
return s.src;
|
|
14041
|
+
}
|
|
14042
|
+
}
|
|
14043
|
+
}
|
|
14044
|
+
catch {
|
|
14045
|
+
// ignore
|
|
14046
|
+
}
|
|
14047
|
+
return "https://cdn.jsdelivr.net/npm/hypha-debugger/dist/hypha-debugger.min.js";
|
|
14048
|
+
}
|
|
13870
14049
|
/**
|
|
13871
14050
|
* Generate token, build service URL, update overlay instructions, and
|
|
13872
14051
|
* return a DebugSession.
|
|
@@ -14122,6 +14301,26 @@
|
|
|
14122
14301
|
function autoStart() {
|
|
14123
14302
|
if (typeof window === "undefined" || typeof document === "undefined")
|
|
14124
14303
|
return;
|
|
14304
|
+
// Skip if already started
|
|
14305
|
+
if (window.__HYPHA_DEBUGGER__?.instance)
|
|
14306
|
+
return;
|
|
14307
|
+
// Check sessionStorage for saved config (auto-reconnect after soft reload)
|
|
14308
|
+
try {
|
|
14309
|
+
const saved = sessionStorage.getItem("__hypha_debugger_config__");
|
|
14310
|
+
if (saved) {
|
|
14311
|
+
const savedConfig = JSON.parse(saved);
|
|
14312
|
+
if (savedConfig.server_url) {
|
|
14313
|
+
console.log("[hypha-debugger] Reconnecting from saved session...");
|
|
14314
|
+
startDebugger(savedConfig).catch((err) => {
|
|
14315
|
+
console.error("[hypha-debugger] Auto-reconnect failed:", err);
|
|
14316
|
+
});
|
|
14317
|
+
return;
|
|
14318
|
+
}
|
|
14319
|
+
}
|
|
14320
|
+
}
|
|
14321
|
+
catch {
|
|
14322
|
+
// sessionStorage not available or parse error — continue to script tag detection
|
|
14323
|
+
}
|
|
14125
14324
|
// Find our own script tag
|
|
14126
14325
|
const scripts = document.querySelectorAll("script[src]");
|
|
14127
14326
|
let scriptEl = null;
|
|
@@ -14134,9 +14333,6 @@
|
|
|
14134
14333
|
// Skip if data-manual is set
|
|
14135
14334
|
if (scriptEl?.hasAttribute("data-manual"))
|
|
14136
14335
|
return;
|
|
14137
|
-
// Skip if already started
|
|
14138
|
-
if (window.__HYPHA_DEBUGGER__?.instance)
|
|
14139
|
-
return;
|
|
14140
14336
|
const serverUrl = scriptEl?.getAttribute("data-server-url") ?? "https://hypha.aicell.io";
|
|
14141
14337
|
const config = {
|
|
14142
14338
|
server_url: serverUrl,
|