@botim/mp-debug-sdk 0.5.2 → 0.6.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/README.md +70 -4
- package/dist/index.cjs +170 -0
- 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 +170 -1
- package/dist/index.js.map +1 -1
- package/package.json +7 -3
package/dist/index.d.cts
CHANGED
|
@@ -52,6 +52,36 @@ interface BuiltinHostHooks {
|
|
|
52
52
|
* The SDK enforces a 1 MB base64/JSON cap regardless.
|
|
53
53
|
*/
|
|
54
54
|
screenshot?: () => ScreenshotResult | Promise<ScreenshotResult>;
|
|
55
|
+
/**
|
|
56
|
+
* Default-enabled remote-eval command (`exec`). Pass `false` to opt out
|
|
57
|
+
* of registration entirely; pass an object to inject extra locals into
|
|
58
|
+
* every snippet's scope.
|
|
59
|
+
*
|
|
60
|
+
* builtins: { exec: false }
|
|
61
|
+
* → exec NOT registered; agents calling exec see `unknown-command`.
|
|
62
|
+
*
|
|
63
|
+
* builtins: { exec: { globals: { app, store } } }
|
|
64
|
+
* → exec registered; snippets can reference `app` and `store` as
|
|
65
|
+
* bare identifiers. Default locals (`BOT`, `window`, `document`)
|
|
66
|
+
* are always present.
|
|
67
|
+
*
|
|
68
|
+
* builtins.exec is undefined / not set
|
|
69
|
+
* → default behaviour: exec registered with default locals only.
|
|
70
|
+
*
|
|
71
|
+
* See `openspec/changes/add-default-exec-builtin/design.md` for the
|
|
72
|
+
* security rationale (no prod-specific gating in MVP; relies on the
|
|
73
|
+
* existing `enableRemoteDebug` consent gate).
|
|
74
|
+
*/
|
|
75
|
+
exec?: false | ExecOptions;
|
|
76
|
+
}
|
|
77
|
+
interface ExecOptions {
|
|
78
|
+
/**
|
|
79
|
+
* Additional locals bound into every snippet's scope. Keys MUST be
|
|
80
|
+
* valid identifier names (the SDK does not validate this — passing a
|
|
81
|
+
* key like "1foo" or "a-b" will throw at AsyncFunction construction
|
|
82
|
+
* time and surface as a `command-rejected`).
|
|
83
|
+
*/
|
|
84
|
+
globals?: Record<string, unknown>;
|
|
55
85
|
}
|
|
56
86
|
type ScreenshotResult = string | {
|
|
57
87
|
data: string;
|
|
@@ -71,6 +101,7 @@ type ScreenshotResult = string | {
|
|
|
71
101
|
*/
|
|
72
102
|
| 'html-snapshot';
|
|
73
103
|
};
|
|
104
|
+
declare function getBOT(): unknown;
|
|
74
105
|
|
|
75
106
|
/**
|
|
76
107
|
* Per-signature sliding-window event deduper.
|
|
@@ -211,4 +242,4 @@ declare const DEFAULT_BUFFER_SIZE = 1000;
|
|
|
211
242
|
declare const DEFAULT_MAX_BATCH_SIZE = 50;
|
|
212
243
|
declare function enableRemoteDebug(options: RemoteDebugOptions): Promise<RemoteDebugHandle>;
|
|
213
244
|
|
|
214
|
-
export { type AppInfo, BotimConfig, BotimConfigError, BotimConsentError, type BuiltinHostHooks, CommandHandler, ConsentInput, ConsolePayload, DEFAULT_BODY_PATTERNS, DEFAULT_BUFFER_SIZE, DEFAULT_FLUSH_INTERVAL_MS, DEFAULT_MAX_BATCH_SIZE, DEFAULT_MAX_BODY_BYTES, DEFAULT_REDACT_HEADERS, type DedupOptions, DeviceInfo, ErrorPayload, EventLevel, EventType, NetworkPayload, type RedactionConfig, type RemoteDebugHandle, type RemoteDebugOptions, type SamplingConfig, type SuppressionSummary, enableRemoteDebug };
|
|
245
|
+
export { type AppInfo, BotimConfig, BotimConfigError, BotimConsentError, type BuiltinHostHooks, CommandHandler, ConsentInput, ConsolePayload, DEFAULT_BODY_PATTERNS, DEFAULT_BUFFER_SIZE, DEFAULT_FLUSH_INTERVAL_MS, DEFAULT_MAX_BATCH_SIZE, DEFAULT_MAX_BODY_BYTES, DEFAULT_REDACT_HEADERS, type DedupOptions, DeviceInfo, ErrorPayload, EventLevel, EventType, type ExecOptions, NetworkPayload, type RedactionConfig, type RemoteDebugHandle, type RemoteDebugOptions, type SamplingConfig, type SuppressionSummary, enableRemoteDebug, getBOT };
|
package/dist/index.d.ts
CHANGED
|
@@ -52,6 +52,36 @@ interface BuiltinHostHooks {
|
|
|
52
52
|
* The SDK enforces a 1 MB base64/JSON cap regardless.
|
|
53
53
|
*/
|
|
54
54
|
screenshot?: () => ScreenshotResult | Promise<ScreenshotResult>;
|
|
55
|
+
/**
|
|
56
|
+
* Default-enabled remote-eval command (`exec`). Pass `false` to opt out
|
|
57
|
+
* of registration entirely; pass an object to inject extra locals into
|
|
58
|
+
* every snippet's scope.
|
|
59
|
+
*
|
|
60
|
+
* builtins: { exec: false }
|
|
61
|
+
* → exec NOT registered; agents calling exec see `unknown-command`.
|
|
62
|
+
*
|
|
63
|
+
* builtins: { exec: { globals: { app, store } } }
|
|
64
|
+
* → exec registered; snippets can reference `app` and `store` as
|
|
65
|
+
* bare identifiers. Default locals (`BOT`, `window`, `document`)
|
|
66
|
+
* are always present.
|
|
67
|
+
*
|
|
68
|
+
* builtins.exec is undefined / not set
|
|
69
|
+
* → default behaviour: exec registered with default locals only.
|
|
70
|
+
*
|
|
71
|
+
* See `openspec/changes/add-default-exec-builtin/design.md` for the
|
|
72
|
+
* security rationale (no prod-specific gating in MVP; relies on the
|
|
73
|
+
* existing `enableRemoteDebug` consent gate).
|
|
74
|
+
*/
|
|
75
|
+
exec?: false | ExecOptions;
|
|
76
|
+
}
|
|
77
|
+
interface ExecOptions {
|
|
78
|
+
/**
|
|
79
|
+
* Additional locals bound into every snippet's scope. Keys MUST be
|
|
80
|
+
* valid identifier names (the SDK does not validate this — passing a
|
|
81
|
+
* key like "1foo" or "a-b" will throw at AsyncFunction construction
|
|
82
|
+
* time and surface as a `command-rejected`).
|
|
83
|
+
*/
|
|
84
|
+
globals?: Record<string, unknown>;
|
|
55
85
|
}
|
|
56
86
|
type ScreenshotResult = string | {
|
|
57
87
|
data: string;
|
|
@@ -71,6 +101,7 @@ type ScreenshotResult = string | {
|
|
|
71
101
|
*/
|
|
72
102
|
| 'html-snapshot';
|
|
73
103
|
};
|
|
104
|
+
declare function getBOT(): unknown;
|
|
74
105
|
|
|
75
106
|
/**
|
|
76
107
|
* Per-signature sliding-window event deduper.
|
|
@@ -211,4 +242,4 @@ declare const DEFAULT_BUFFER_SIZE = 1000;
|
|
|
211
242
|
declare const DEFAULT_MAX_BATCH_SIZE = 50;
|
|
212
243
|
declare function enableRemoteDebug(options: RemoteDebugOptions): Promise<RemoteDebugHandle>;
|
|
213
244
|
|
|
214
|
-
export { type AppInfo, BotimConfig, BotimConfigError, BotimConsentError, type BuiltinHostHooks, CommandHandler, ConsentInput, ConsolePayload, DEFAULT_BODY_PATTERNS, DEFAULT_BUFFER_SIZE, DEFAULT_FLUSH_INTERVAL_MS, DEFAULT_MAX_BATCH_SIZE, DEFAULT_MAX_BODY_BYTES, DEFAULT_REDACT_HEADERS, type DedupOptions, DeviceInfo, ErrorPayload, EventLevel, EventType, NetworkPayload, type RedactionConfig, type RemoteDebugHandle, type RemoteDebugOptions, type SamplingConfig, type SuppressionSummary, enableRemoteDebug };
|
|
245
|
+
export { type AppInfo, BotimConfig, BotimConfigError, BotimConsentError, type BuiltinHostHooks, CommandHandler, ConsentInput, ConsolePayload, DEFAULT_BODY_PATTERNS, DEFAULT_BUFFER_SIZE, DEFAULT_FLUSH_INTERVAL_MS, DEFAULT_MAX_BATCH_SIZE, DEFAULT_MAX_BODY_BYTES, DEFAULT_REDACT_HEADERS, type DedupOptions, DeviceInfo, ErrorPayload, EventLevel, EventType, type ExecOptions, NetworkPayload, type RedactionConfig, type RemoteDebugHandle, type RemoteDebugOptions, type SamplingConfig, type SuppressionSummary, enableRemoteDebug, getBOT };
|
package/dist/index.js
CHANGED
|
@@ -817,6 +817,10 @@ var CommandRegistry = class {
|
|
|
817
817
|
};
|
|
818
818
|
var MAX_DUMP_BYTES = 64 * 1024;
|
|
819
819
|
var MAX_SCREENSHOT_BYTES = 1024 * 1024;
|
|
820
|
+
var MAX_EXEC_CODE_BYTES = 8 * 1024;
|
|
821
|
+
var DEFAULT_EXEC_TIMEOUT_MS = 5e3;
|
|
822
|
+
var MIN_EXEC_TIMEOUT_MS = 250;
|
|
823
|
+
var MAX_EXEC_TIMEOUT_MS = 3e4;
|
|
820
824
|
async function defaultDomScreenshot() {
|
|
821
825
|
if (typeof document === "undefined" || typeof window === "undefined") {
|
|
822
826
|
throw new Error(
|
|
@@ -931,6 +935,9 @@ function registerBuiltins(registry, hooks = {}) {
|
|
|
931
935
|
registry.register("dump-state", makeDumpState(hooks.getState));
|
|
932
936
|
registry.register("set-feature-flag", makeSetFlag(hooks.setFeatureFlag));
|
|
933
937
|
registry.register("screenshot", makeScreenshot(hooks.screenshot));
|
|
938
|
+
if (hooks.exec !== false) {
|
|
939
|
+
registry.register("exec", makeExec(hooks.exec ?? {}));
|
|
940
|
+
}
|
|
934
941
|
}
|
|
935
942
|
var ping = () => ({ ok: true, ts: Date.now() });
|
|
936
943
|
function makeReload(reload) {
|
|
@@ -993,6 +1000,168 @@ function makeScreenshot(screenshot) {
|
|
|
993
1000
|
return { format, data, bytes: data.length };
|
|
994
1001
|
};
|
|
995
1002
|
}
|
|
1003
|
+
var cachedBOT = void 0;
|
|
1004
|
+
function getBOT() {
|
|
1005
|
+
if (cachedBOT !== void 0) return cachedBOT;
|
|
1006
|
+
if (typeof window === "undefined") {
|
|
1007
|
+
cachedBOT = null;
|
|
1008
|
+
return null;
|
|
1009
|
+
}
|
|
1010
|
+
try {
|
|
1011
|
+
const w = window;
|
|
1012
|
+
if (w.BOT) {
|
|
1013
|
+
cachedBOT = w.BOT;
|
|
1014
|
+
return cachedBOT;
|
|
1015
|
+
}
|
|
1016
|
+
} catch {
|
|
1017
|
+
}
|
|
1018
|
+
try {
|
|
1019
|
+
const w = window;
|
|
1020
|
+
const ng = w.angular;
|
|
1021
|
+
if (ng?.element && typeof document !== "undefined") {
|
|
1022
|
+
const injector = ng.element(document).injector?.();
|
|
1023
|
+
const bot = injector?.get?.("BOT");
|
|
1024
|
+
if (bot != null) {
|
|
1025
|
+
cachedBOT = bot;
|
|
1026
|
+
return cachedBOT;
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
} catch {
|
|
1030
|
+
}
|
|
1031
|
+
return null;
|
|
1032
|
+
}
|
|
1033
|
+
function makeExec(opts) {
|
|
1034
|
+
const extraGlobalNames = Object.keys(opts.globals ?? {});
|
|
1035
|
+
const extraGlobalValues = extraGlobalNames.map((k) => opts.globals[k]);
|
|
1036
|
+
return async (args, ctx) => {
|
|
1037
|
+
if (typeof args.code !== "string" || args.code.length === 0) {
|
|
1038
|
+
throw new Error("args.code (non-empty string) is required");
|
|
1039
|
+
}
|
|
1040
|
+
if (args.code.length > MAX_EXEC_CODE_BYTES) {
|
|
1041
|
+
throw new Error(`args.code ${args.code.length} bytes exceeds limit ${MAX_EXEC_CODE_BYTES}`);
|
|
1042
|
+
}
|
|
1043
|
+
const timeoutMs = clamp(
|
|
1044
|
+
typeof args.timeoutMs === "number" ? args.timeoutMs : DEFAULT_EXEC_TIMEOUT_MS,
|
|
1045
|
+
MIN_EXEC_TIMEOUT_MS,
|
|
1046
|
+
MAX_EXEC_TIMEOUT_MS
|
|
1047
|
+
);
|
|
1048
|
+
const start = Date.now();
|
|
1049
|
+
const logs = [];
|
|
1050
|
+
const consoleMethods = ["log", "info", "warn", "error", "debug"];
|
|
1051
|
+
const originals = {};
|
|
1052
|
+
if (typeof console !== "undefined") {
|
|
1053
|
+
for (const m of consoleMethods) {
|
|
1054
|
+
const orig = console[m];
|
|
1055
|
+
if (typeof orig !== "function") continue;
|
|
1056
|
+
originals[m] = orig;
|
|
1057
|
+
const captured = (...callArgs) => {
|
|
1058
|
+
logs.push({ method: m, args: callArgs.map(execSafeClone), ts: Date.now() });
|
|
1059
|
+
try {
|
|
1060
|
+
orig.call(console, ...callArgs);
|
|
1061
|
+
} catch {
|
|
1062
|
+
}
|
|
1063
|
+
};
|
|
1064
|
+
console[m] = captured;
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
let value;
|
|
1068
|
+
let threw = void 0;
|
|
1069
|
+
try {
|
|
1070
|
+
const AsyncFunction = Object.getPrototypeOf(async function() {
|
|
1071
|
+
}).constructor;
|
|
1072
|
+
const localNames = ["BOT", "window", "document", ...extraGlobalNames];
|
|
1073
|
+
const localValues = [
|
|
1074
|
+
getBOT(),
|
|
1075
|
+
typeof window !== "undefined" ? window : void 0,
|
|
1076
|
+
typeof document !== "undefined" ? document : void 0,
|
|
1077
|
+
...extraGlobalValues
|
|
1078
|
+
];
|
|
1079
|
+
let fnTry = null;
|
|
1080
|
+
try {
|
|
1081
|
+
fnTry = new AsyncFunction(...localNames, `return (${args.code});`);
|
|
1082
|
+
} catch (e) {
|
|
1083
|
+
if (!(e instanceof SyntaxError)) throw e;
|
|
1084
|
+
}
|
|
1085
|
+
const fn = fnTry ?? new AsyncFunction(...localNames, args.code);
|
|
1086
|
+
let timer = null;
|
|
1087
|
+
let abortReject = null;
|
|
1088
|
+
const onAbort = () => abortReject?.(new Error("exec cancelled"));
|
|
1089
|
+
ctx.signal?.addEventListener?.("abort", onAbort);
|
|
1090
|
+
try {
|
|
1091
|
+
value = await Promise.race([
|
|
1092
|
+
fn(...localValues),
|
|
1093
|
+
new Promise((_, reject) => {
|
|
1094
|
+
timer = setTimeout(
|
|
1095
|
+
() => reject(new Error(`exec exceeded ${timeoutMs}ms`)),
|
|
1096
|
+
timeoutMs
|
|
1097
|
+
);
|
|
1098
|
+
}),
|
|
1099
|
+
new Promise((_, reject) => {
|
|
1100
|
+
abortReject = reject;
|
|
1101
|
+
})
|
|
1102
|
+
]);
|
|
1103
|
+
} finally {
|
|
1104
|
+
if (timer) clearTimeout(timer);
|
|
1105
|
+
ctx.signal?.removeEventListener?.("abort", onAbort);
|
|
1106
|
+
}
|
|
1107
|
+
} catch (err) {
|
|
1108
|
+
threw = err;
|
|
1109
|
+
} finally {
|
|
1110
|
+
if (typeof console !== "undefined") {
|
|
1111
|
+
for (const m of consoleMethods) {
|
|
1112
|
+
if (originals[m]) {
|
|
1113
|
+
console[m] = originals[m];
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
if (threw !== void 0) {
|
|
1119
|
+
const e = threw instanceof Error ? threw : new Error(String(threw));
|
|
1120
|
+
const stackTruncated = typeof e.stack === "string" ? e.stack.split("\n").slice(0, 20).join("\n") : void 0;
|
|
1121
|
+
const detail = {
|
|
1122
|
+
name: e.name,
|
|
1123
|
+
message: e.message,
|
|
1124
|
+
stack: stackTruncated,
|
|
1125
|
+
logs,
|
|
1126
|
+
durationMs: Date.now() - start
|
|
1127
|
+
};
|
|
1128
|
+
const wrapped = new Error(JSON.stringify(detail));
|
|
1129
|
+
wrapped.detail = detail;
|
|
1130
|
+
throw wrapped;
|
|
1131
|
+
}
|
|
1132
|
+
return {
|
|
1133
|
+
ok: true,
|
|
1134
|
+
value: execSafeClone(value),
|
|
1135
|
+
logs,
|
|
1136
|
+
durationMs: Date.now() - start
|
|
1137
|
+
};
|
|
1138
|
+
};
|
|
1139
|
+
}
|
|
1140
|
+
function clamp(n, lo, hi) {
|
|
1141
|
+
if (!Number.isFinite(n)) return lo;
|
|
1142
|
+
return Math.min(Math.max(n, lo), hi);
|
|
1143
|
+
}
|
|
1144
|
+
function execSafeClone(v) {
|
|
1145
|
+
try {
|
|
1146
|
+
return JSON.parse(JSON.stringify(v, (_k, val) => {
|
|
1147
|
+
if (typeof val === "function") {
|
|
1148
|
+
return `[Function: ${val.name || "anonymous"}]`;
|
|
1149
|
+
}
|
|
1150
|
+
if (val instanceof Error) {
|
|
1151
|
+
return { name: val.name, message: val.message, stack: val.stack };
|
|
1152
|
+
}
|
|
1153
|
+
if (typeof val === "bigint") return val.toString() + "n";
|
|
1154
|
+
if (typeof val === "undefined") return null;
|
|
1155
|
+
if (val && typeof val === "object" && val.nodeType === 1) {
|
|
1156
|
+
const el = val;
|
|
1157
|
+
return `[${el.tagName ?? "Element"}${el.id ? "#" + el.id : ""}]`;
|
|
1158
|
+
}
|
|
1159
|
+
return val;
|
|
1160
|
+
}));
|
|
1161
|
+
} catch {
|
|
1162
|
+
return { __unserializable: typeof v };
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
996
1165
|
function safeStringify(v) {
|
|
997
1166
|
try {
|
|
998
1167
|
const seen = /* @__PURE__ */ new WeakSet();
|
|
@@ -1430,6 +1599,6 @@ async function enableRemoteDebug(options) {
|
|
|
1430
1599
|
};
|
|
1431
1600
|
}
|
|
1432
1601
|
|
|
1433
|
-
export { BotimConfigError, BotimConsentError, DEFAULT_BODY_PATTERNS, DEFAULT_BUFFER_SIZE, DEFAULT_FLUSH_INTERVAL_MS, DEFAULT_MAX_BATCH_SIZE, DEFAULT_MAX_BODY_BYTES, DEFAULT_REDACT_HEADERS, SCHEMA_VERSION, enableRemoteDebug };
|
|
1602
|
+
export { BotimConfigError, BotimConsentError, DEFAULT_BODY_PATTERNS, DEFAULT_BUFFER_SIZE, DEFAULT_FLUSH_INTERVAL_MS, DEFAULT_MAX_BATCH_SIZE, DEFAULT_MAX_BODY_BYTES, DEFAULT_REDACT_HEADERS, SCHEMA_VERSION, enableRemoteDebug, getBOT };
|
|
1434
1603
|
//# sourceMappingURL=index.js.map
|
|
1435
1604
|
//# sourceMappingURL=index.js.map
|