@botim/mp-debug-sdk 0.4.1 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -3
- package/dist/index.cjs +239 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +32 -1
- package/dist/index.d.ts +32 -1
- package/dist/index.js +239 -8
- package/dist/index.js.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +12 -0
- package/dist/types.d.ts +12 -0
- package/dist/types.js.map +1 -1
- package/package.json +7 -3
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ Mini-programs run on user devices in environments you can't easily attach a debu
|
|
|
8
8
|
|
|
9
9
|
- **Live logs** of `console.*`, `fetch`, `XMLHttpRequest`, and uncaught errors.
|
|
10
10
|
- **AI-observable sessions** — every event indexed by `(miniProgramId, deviceId, sid)` so resolver agents can pull errors and reproduce bugs without a human in the loop.
|
|
11
|
-
- **Safe AI command channel** — agents can `reload`, `dump-state`, `set-feature-flag`, `screenshot`, `ping`, or any custom command you allow. Anything not registered is rejected.
|
|
11
|
+
- **Safe AI command channel** — agents can `reload`, `dump-state`, `set-feature-flag`, `screenshot`, `ping`, `exec` (default-on remote REPL), or any custom command you allow. Anything not registered is rejected.
|
|
12
12
|
- **Built-in redaction** before the event ever enters the in-memory buffer.
|
|
13
13
|
|
|
14
14
|
## Install
|
|
@@ -128,7 +128,58 @@ The SDK posts directly to `endpoint` — there's no proxy required. Your relay m
|
|
|
128
128
|
CORS_ORIGINS=https://my-mp.example.com,https://staging.example.com
|
|
129
129
|
```
|
|
130
130
|
|
|
131
|
-
## 5.
|
|
131
|
+
## 5. Remote REPL (`exec`) — default-on
|
|
132
|
+
|
|
133
|
+
Once `enableRemoteDebug` returns, an attached agent can send a JS snippet to the device and read back the value, captured `console.*` output, or thrown error. No host wiring needed.
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
# From any terminal that can reach your relay:
|
|
137
|
+
curl -sX POST "https://debug.botim.dev/v1/mp/<MP_ID>/devices/<DEVICE_ID>/commands" \
|
|
138
|
+
-H 'content-type: application/json' \
|
|
139
|
+
-d '{"name":"exec","args":{"code":"console.log(\"hi\"); return window.location.href"}}'
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Result event:
|
|
143
|
+
|
|
144
|
+
```jsonc
|
|
145
|
+
{
|
|
146
|
+
"type": "command-ack",
|
|
147
|
+
"payload": {
|
|
148
|
+
"command": "exec",
|
|
149
|
+
"ok": true,
|
|
150
|
+
"result": {
|
|
151
|
+
"value": "https://my-mp.example.com/page",
|
|
152
|
+
"logs": [{"method":"log","args":["hi"],"ts": 1735324800123}],
|
|
153
|
+
"durationMs": 2
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Inside the snippet, `BOT`, `window`, and `document` are bound as locals (with `BOT` resolved best-effort from `window.BOT` or Angular DI; `null` if unavailable). Top-level `await` works. Code is capped at 8 KB and 30 s.
|
|
160
|
+
|
|
161
|
+
**To opt out** (e.g. you ship a non-debug release that still uses the SDK for telemetry only):
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
await enableRemoteDebug({
|
|
165
|
+
// ...,
|
|
166
|
+
builtins: { exec: false },
|
|
167
|
+
});
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
**To inject extra locals** for your app:
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
await enableRemoteDebug({
|
|
174
|
+
// ...,
|
|
175
|
+
builtins: { exec: { globals: { app: myApp, store: myStore } } },
|
|
176
|
+
});
|
|
177
|
+
// agent can then call: { code: "return store.getState().user" }
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
> Pre-buffer redaction (headers, JWT/long-token regex) is applied to `result.value` and `result.logs` before they leave the device buffer. The existing prod consent gate (`hostToken` or `userOptIn`) gates whether attach happens at all. See `docs/recipes/exec.md` for deeper notes.
|
|
181
|
+
|
|
182
|
+
## 6. Custom commands (optional)
|
|
132
183
|
|
|
133
184
|
Any AI agent with the right scope can request a command. The SDK only runs commands registered via `registerCommand`:
|
|
134
185
|
|
|
@@ -147,7 +198,7 @@ handle.registerCommand('set-locale', async (args) => {
|
|
|
147
198
|
|
|
148
199
|
Anything not registered gets a `command-rejected` event with reason `unknown-command`.
|
|
149
200
|
|
|
150
|
-
##
|
|
201
|
+
## 7. Lifecycle
|
|
151
202
|
|
|
152
203
|
```ts
|
|
153
204
|
await handle.flush(); // force-send buffered events
|
package/dist/index.cjs
CHANGED
|
@@ -272,7 +272,7 @@ function resolveAgainstEndpoint(url, base) {
|
|
|
272
272
|
function detectDeviceInfo(app, override) {
|
|
273
273
|
const ua = typeof navigator !== "undefined" ? navigator.userAgent : void 0;
|
|
274
274
|
return {
|
|
275
|
-
deviceId: override?.deviceId ??
|
|
275
|
+
deviceId: override?.deviceId ?? loadOrCreateDeviceId(),
|
|
276
276
|
platform: override?.platform ?? detectPlatform(ua),
|
|
277
277
|
osVersion: override?.osVersion,
|
|
278
278
|
appName: override?.appName ?? app.name,
|
|
@@ -288,6 +288,21 @@ function detectPlatform(ua) {
|
|
|
288
288
|
if (/Mozilla|Chrome|Safari|Firefox/i.test(ua)) return "web";
|
|
289
289
|
return "unknown";
|
|
290
290
|
}
|
|
291
|
+
var DEVICE_ID_STORAGE_KEY = "botim-debug-sdk:device-id";
|
|
292
|
+
function loadOrCreateDeviceId() {
|
|
293
|
+
try {
|
|
294
|
+
const ls = typeof localStorage !== "undefined" ? localStorage : null;
|
|
295
|
+
if (ls) {
|
|
296
|
+
const stored = ls.getItem(DEVICE_ID_STORAGE_KEY);
|
|
297
|
+
if (stored && stored.length > 0) return stored;
|
|
298
|
+
const fresh = generateDeviceId();
|
|
299
|
+
ls.setItem(DEVICE_ID_STORAGE_KEY, fresh);
|
|
300
|
+
return fresh;
|
|
301
|
+
}
|
|
302
|
+
} catch {
|
|
303
|
+
}
|
|
304
|
+
return generateDeviceId();
|
|
305
|
+
}
|
|
291
306
|
function generateDeviceId() {
|
|
292
307
|
const c = typeof crypto !== "undefined" ? crypto : void 0;
|
|
293
308
|
if (c?.randomUUID) return c.randomUUID();
|
|
@@ -562,6 +577,11 @@ function wrapFetch(opts) {
|
|
|
562
577
|
method,
|
|
563
578
|
url,
|
|
564
579
|
status: res.status,
|
|
580
|
+
// statusText carries the human label ("OK", "Not Found"). Pre-HTTP/2
|
|
581
|
+
// responses always have it; HTTP/2+ defines it as empty by spec but
|
|
582
|
+
// most browsers synthesize one from the code, so this is reliable
|
|
583
|
+
// enough to display alongside the status code.
|
|
584
|
+
statusText: res.statusText || void 0,
|
|
565
585
|
durationMs: Date.now() - start,
|
|
566
586
|
resHeaders: headersFromResponse(res),
|
|
567
587
|
resBody
|
|
@@ -575,7 +595,14 @@ function wrapFetch(opts) {
|
|
|
575
595
|
url,
|
|
576
596
|
durationMs: Date.now() - start,
|
|
577
597
|
errorMessage: err instanceof Error ? err.message : String(err),
|
|
578
|
-
errorName: err instanceof Error ? err.name : void 0
|
|
598
|
+
errorName: err instanceof Error ? err.name : void 0,
|
|
599
|
+
// Stack from the rejected promise — points into fetch internals
|
|
600
|
+
// and (when present) the call site that issued the request.
|
|
601
|
+
errorStack: err instanceof Error ? err.stack : void 0,
|
|
602
|
+
// undici frequently wraps the real reason in `cause` (e.g.
|
|
603
|
+
// `TypeError: fetch failed` outside, `Error: ECONNREFUSED` inside).
|
|
604
|
+
// Flatten the chain so operators don't have to dig.
|
|
605
|
+
errorCause: collectCauseChain(err)
|
|
579
606
|
});
|
|
580
607
|
throw err;
|
|
581
608
|
}
|
|
@@ -584,6 +611,24 @@ function wrapFetch(opts) {
|
|
|
584
611
|
target.fetch = original;
|
|
585
612
|
};
|
|
586
613
|
}
|
|
614
|
+
function collectCauseChain(err) {
|
|
615
|
+
if (!err || typeof err !== "object") return void 0;
|
|
616
|
+
const lines = [];
|
|
617
|
+
let cur = err.cause;
|
|
618
|
+
const seen = /* @__PURE__ */ new Set();
|
|
619
|
+
while (cur && lines.length < 5) {
|
|
620
|
+
if (seen.has(cur)) break;
|
|
621
|
+
seen.add(cur);
|
|
622
|
+
if (cur instanceof Error) {
|
|
623
|
+
lines.push(`${cur.name}: ${cur.message}`);
|
|
624
|
+
cur = cur.cause;
|
|
625
|
+
} else {
|
|
626
|
+
lines.push(String(cur));
|
|
627
|
+
cur = cur?.cause;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
return lines.length ? lines.join("\n") : void 0;
|
|
631
|
+
}
|
|
587
632
|
function wrapXHR(opts) {
|
|
588
633
|
if (typeof XMLHttpRequest === "undefined") return () => {
|
|
589
634
|
};
|
|
@@ -619,6 +664,7 @@ function wrapXHR(opts) {
|
|
|
619
664
|
}
|
|
620
665
|
s.start = Date.now();
|
|
621
666
|
s.reqBody = typeof body === "string" ? body : void 0;
|
|
667
|
+
const sendSiteStack = captureCallSiteStack();
|
|
622
668
|
opts.emit({
|
|
623
669
|
phase: "request",
|
|
624
670
|
reqId: s.reqId,
|
|
@@ -636,25 +682,32 @@ function wrapXHR(opts) {
|
|
|
636
682
|
method: s.method,
|
|
637
683
|
url: s.url,
|
|
638
684
|
status: this.status,
|
|
685
|
+
// XHR exposes statusText directly; same display purpose as fetch.
|
|
686
|
+
statusText: this.statusText || void 0,
|
|
639
687
|
durationMs: Date.now() - s.start,
|
|
640
688
|
resHeaders: headers,
|
|
641
689
|
resBody
|
|
642
690
|
});
|
|
643
691
|
};
|
|
644
|
-
const onError = () => {
|
|
692
|
+
const onError = (kind) => () => {
|
|
645
693
|
opts.emit({
|
|
646
694
|
phase: "error",
|
|
647
695
|
reqId: s.reqId,
|
|
648
696
|
method: s.method,
|
|
649
697
|
url: s.url,
|
|
650
698
|
durationMs: Date.now() - s.start,
|
|
651
|
-
|
|
699
|
+
// Distinguish error/timeout/abort in the message — the standard
|
|
700
|
+
// XHR `statusText` is empty for `error` and unhelpful for the
|
|
701
|
+
// others, so we synthesise a clear label.
|
|
702
|
+
errorMessage: this.statusText || `xhr ${kind}`,
|
|
703
|
+
errorName: `XHR${kind[0].toUpperCase()}${kind.slice(1)}`,
|
|
704
|
+
errorStack: sendSiteStack
|
|
652
705
|
});
|
|
653
706
|
};
|
|
654
707
|
this.addEventListener("load", onLoad);
|
|
655
|
-
this.addEventListener("error", onError);
|
|
656
|
-
this.addEventListener("timeout", onError);
|
|
657
|
-
this.addEventListener("abort", onError);
|
|
708
|
+
this.addEventListener("error", onError("error"));
|
|
709
|
+
this.addEventListener("timeout", onError("timeout"));
|
|
710
|
+
this.addEventListener("abort", onError("abort"));
|
|
658
711
|
return origSend.apply(this, [body]);
|
|
659
712
|
};
|
|
660
713
|
return () => {
|
|
@@ -663,6 +716,15 @@ function wrapXHR(opts) {
|
|
|
663
716
|
proto.setRequestHeader = origSetReqHeader;
|
|
664
717
|
};
|
|
665
718
|
}
|
|
719
|
+
function captureCallSiteStack() {
|
|
720
|
+
try {
|
|
721
|
+
throw new Error("xhr-callsite");
|
|
722
|
+
} catch (err) {
|
|
723
|
+
if (!(err instanceof Error) || !err.stack) return void 0;
|
|
724
|
+
const lines = err.stack.split("\n");
|
|
725
|
+
return lines.slice(2).join("\n") || void 0;
|
|
726
|
+
}
|
|
727
|
+
}
|
|
666
728
|
function parseXhrHeaders(raw) {
|
|
667
729
|
const out = {};
|
|
668
730
|
if (!raw) return out;
|
|
@@ -757,6 +819,10 @@ var CommandRegistry = class {
|
|
|
757
819
|
};
|
|
758
820
|
var MAX_DUMP_BYTES = 64 * 1024;
|
|
759
821
|
var MAX_SCREENSHOT_BYTES = 1024 * 1024;
|
|
822
|
+
var MAX_EXEC_CODE_BYTES = 8 * 1024;
|
|
823
|
+
var DEFAULT_EXEC_TIMEOUT_MS = 5e3;
|
|
824
|
+
var MIN_EXEC_TIMEOUT_MS = 250;
|
|
825
|
+
var MAX_EXEC_TIMEOUT_MS = 3e4;
|
|
760
826
|
async function defaultDomScreenshot() {
|
|
761
827
|
if (typeof document === "undefined" || typeof window === "undefined") {
|
|
762
828
|
throw new Error(
|
|
@@ -871,6 +937,9 @@ function registerBuiltins(registry, hooks = {}) {
|
|
|
871
937
|
registry.register("dump-state", makeDumpState(hooks.getState));
|
|
872
938
|
registry.register("set-feature-flag", makeSetFlag(hooks.setFeatureFlag));
|
|
873
939
|
registry.register("screenshot", makeScreenshot(hooks.screenshot));
|
|
940
|
+
if (hooks.exec !== false) {
|
|
941
|
+
registry.register("exec", makeExec(hooks.exec ?? {}));
|
|
942
|
+
}
|
|
874
943
|
}
|
|
875
944
|
var ping = () => ({ ok: true, ts: Date.now() });
|
|
876
945
|
function makeReload(reload) {
|
|
@@ -933,6 +1002,168 @@ function makeScreenshot(screenshot) {
|
|
|
933
1002
|
return { format, data, bytes: data.length };
|
|
934
1003
|
};
|
|
935
1004
|
}
|
|
1005
|
+
var cachedBOT = void 0;
|
|
1006
|
+
function getBOT() {
|
|
1007
|
+
if (cachedBOT !== void 0) return cachedBOT;
|
|
1008
|
+
if (typeof window === "undefined") {
|
|
1009
|
+
cachedBOT = null;
|
|
1010
|
+
return null;
|
|
1011
|
+
}
|
|
1012
|
+
try {
|
|
1013
|
+
const w = window;
|
|
1014
|
+
if (w.BOT) {
|
|
1015
|
+
cachedBOT = w.BOT;
|
|
1016
|
+
return cachedBOT;
|
|
1017
|
+
}
|
|
1018
|
+
} catch {
|
|
1019
|
+
}
|
|
1020
|
+
try {
|
|
1021
|
+
const w = window;
|
|
1022
|
+
const ng = w.angular;
|
|
1023
|
+
if (ng?.element && typeof document !== "undefined") {
|
|
1024
|
+
const injector = ng.element(document).injector?.();
|
|
1025
|
+
const bot = injector?.get?.("BOT");
|
|
1026
|
+
if (bot != null) {
|
|
1027
|
+
cachedBOT = bot;
|
|
1028
|
+
return cachedBOT;
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
} catch {
|
|
1032
|
+
}
|
|
1033
|
+
return null;
|
|
1034
|
+
}
|
|
1035
|
+
function makeExec(opts) {
|
|
1036
|
+
const extraGlobalNames = Object.keys(opts.globals ?? {});
|
|
1037
|
+
const extraGlobalValues = extraGlobalNames.map((k) => opts.globals[k]);
|
|
1038
|
+
return async (args, ctx) => {
|
|
1039
|
+
if (typeof args.code !== "string" || args.code.length === 0) {
|
|
1040
|
+
throw new Error("args.code (non-empty string) is required");
|
|
1041
|
+
}
|
|
1042
|
+
if (args.code.length > MAX_EXEC_CODE_BYTES) {
|
|
1043
|
+
throw new Error(`args.code ${args.code.length} bytes exceeds limit ${MAX_EXEC_CODE_BYTES}`);
|
|
1044
|
+
}
|
|
1045
|
+
const timeoutMs = clamp(
|
|
1046
|
+
typeof args.timeoutMs === "number" ? args.timeoutMs : DEFAULT_EXEC_TIMEOUT_MS,
|
|
1047
|
+
MIN_EXEC_TIMEOUT_MS,
|
|
1048
|
+
MAX_EXEC_TIMEOUT_MS
|
|
1049
|
+
);
|
|
1050
|
+
const start = Date.now();
|
|
1051
|
+
const logs = [];
|
|
1052
|
+
const consoleMethods = ["log", "info", "warn", "error", "debug"];
|
|
1053
|
+
const originals = {};
|
|
1054
|
+
if (typeof console !== "undefined") {
|
|
1055
|
+
for (const m of consoleMethods) {
|
|
1056
|
+
const orig = console[m];
|
|
1057
|
+
if (typeof orig !== "function") continue;
|
|
1058
|
+
originals[m] = orig;
|
|
1059
|
+
const captured = (...callArgs) => {
|
|
1060
|
+
logs.push({ method: m, args: callArgs.map(execSafeClone), ts: Date.now() });
|
|
1061
|
+
try {
|
|
1062
|
+
orig.call(console, ...callArgs);
|
|
1063
|
+
} catch {
|
|
1064
|
+
}
|
|
1065
|
+
};
|
|
1066
|
+
console[m] = captured;
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
let value;
|
|
1070
|
+
let threw = void 0;
|
|
1071
|
+
try {
|
|
1072
|
+
const AsyncFunction = Object.getPrototypeOf(async function() {
|
|
1073
|
+
}).constructor;
|
|
1074
|
+
const localNames = ["BOT", "window", "document", ...extraGlobalNames];
|
|
1075
|
+
const localValues = [
|
|
1076
|
+
getBOT(),
|
|
1077
|
+
typeof window !== "undefined" ? window : void 0,
|
|
1078
|
+
typeof document !== "undefined" ? document : void 0,
|
|
1079
|
+
...extraGlobalValues
|
|
1080
|
+
];
|
|
1081
|
+
let fnTry = null;
|
|
1082
|
+
try {
|
|
1083
|
+
fnTry = new AsyncFunction(...localNames, `return (${args.code});`);
|
|
1084
|
+
} catch (e) {
|
|
1085
|
+
if (!(e instanceof SyntaxError)) throw e;
|
|
1086
|
+
}
|
|
1087
|
+
const fn = fnTry ?? new AsyncFunction(...localNames, args.code);
|
|
1088
|
+
let timer = null;
|
|
1089
|
+
let abortReject = null;
|
|
1090
|
+
const onAbort = () => abortReject?.(new Error("exec cancelled"));
|
|
1091
|
+
ctx.signal?.addEventListener?.("abort", onAbort);
|
|
1092
|
+
try {
|
|
1093
|
+
value = await Promise.race([
|
|
1094
|
+
fn(...localValues),
|
|
1095
|
+
new Promise((_, reject) => {
|
|
1096
|
+
timer = setTimeout(
|
|
1097
|
+
() => reject(new Error(`exec exceeded ${timeoutMs}ms`)),
|
|
1098
|
+
timeoutMs
|
|
1099
|
+
);
|
|
1100
|
+
}),
|
|
1101
|
+
new Promise((_, reject) => {
|
|
1102
|
+
abortReject = reject;
|
|
1103
|
+
})
|
|
1104
|
+
]);
|
|
1105
|
+
} finally {
|
|
1106
|
+
if (timer) clearTimeout(timer);
|
|
1107
|
+
ctx.signal?.removeEventListener?.("abort", onAbort);
|
|
1108
|
+
}
|
|
1109
|
+
} catch (err) {
|
|
1110
|
+
threw = err;
|
|
1111
|
+
} finally {
|
|
1112
|
+
if (typeof console !== "undefined") {
|
|
1113
|
+
for (const m of consoleMethods) {
|
|
1114
|
+
if (originals[m]) {
|
|
1115
|
+
console[m] = originals[m];
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
if (threw !== void 0) {
|
|
1121
|
+
const e = threw instanceof Error ? threw : new Error(String(threw));
|
|
1122
|
+
const stackTruncated = typeof e.stack === "string" ? e.stack.split("\n").slice(0, 20).join("\n") : void 0;
|
|
1123
|
+
const detail = {
|
|
1124
|
+
name: e.name,
|
|
1125
|
+
message: e.message,
|
|
1126
|
+
stack: stackTruncated,
|
|
1127
|
+
logs,
|
|
1128
|
+
durationMs: Date.now() - start
|
|
1129
|
+
};
|
|
1130
|
+
const wrapped = new Error(JSON.stringify(detail));
|
|
1131
|
+
wrapped.detail = detail;
|
|
1132
|
+
throw wrapped;
|
|
1133
|
+
}
|
|
1134
|
+
return {
|
|
1135
|
+
ok: true,
|
|
1136
|
+
value: execSafeClone(value),
|
|
1137
|
+
logs,
|
|
1138
|
+
durationMs: Date.now() - start
|
|
1139
|
+
};
|
|
1140
|
+
};
|
|
1141
|
+
}
|
|
1142
|
+
function clamp(n, lo, hi) {
|
|
1143
|
+
if (!Number.isFinite(n)) return lo;
|
|
1144
|
+
return Math.min(Math.max(n, lo), hi);
|
|
1145
|
+
}
|
|
1146
|
+
function execSafeClone(v) {
|
|
1147
|
+
try {
|
|
1148
|
+
return JSON.parse(JSON.stringify(v, (_k, val) => {
|
|
1149
|
+
if (typeof val === "function") {
|
|
1150
|
+
return `[Function: ${val.name || "anonymous"}]`;
|
|
1151
|
+
}
|
|
1152
|
+
if (val instanceof Error) {
|
|
1153
|
+
return { name: val.name, message: val.message, stack: val.stack };
|
|
1154
|
+
}
|
|
1155
|
+
if (typeof val === "bigint") return val.toString() + "n";
|
|
1156
|
+
if (typeof val === "undefined") return null;
|
|
1157
|
+
if (val && typeof val === "object" && val.nodeType === 1) {
|
|
1158
|
+
const el = val;
|
|
1159
|
+
return `[${el.tagName ?? "Element"}${el.id ? "#" + el.id : ""}]`;
|
|
1160
|
+
}
|
|
1161
|
+
return val;
|
|
1162
|
+
}));
|
|
1163
|
+
} catch {
|
|
1164
|
+
return { __unserializable: typeof v };
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
936
1167
|
function safeStringify(v) {
|
|
937
1168
|
try {
|
|
938
1169
|
const seen = /* @__PURE__ */ new WeakSet();
|
|
@@ -1380,5 +1611,6 @@ exports.DEFAULT_MAX_BODY_BYTES = DEFAULT_MAX_BODY_BYTES;
|
|
|
1380
1611
|
exports.DEFAULT_REDACT_HEADERS = DEFAULT_REDACT_HEADERS;
|
|
1381
1612
|
exports.SCHEMA_VERSION = SCHEMA_VERSION;
|
|
1382
1613
|
exports.enableRemoteDebug = enableRemoteDebug;
|
|
1614
|
+
exports.getBOT = getBOT;
|
|
1383
1615
|
//# sourceMappingURL=index.cjs.map
|
|
1384
1616
|
//# sourceMappingURL=index.cjs.map
|