@botim/mp-debug-sdk
v0.7.2
Published
Remote-debug SDK for BOTIM mini-programs — streams console, network, and error events to a BOTIM debug-relay for live inspection, with an AI-observable command channel.
Readme
@botim/mp-debug-sdk
Remote-debug your BOTIM mini-program. Stream console, network, and error events to a debug-relay you control; let humans tail live and let AI agents query, subscribe, and issue safe commands back to the device.
Why
Mini-programs run on user devices in environments you can't easily attach a debugger to. This SDK gives you:
- Live logs of
console.*,fetch,XMLHttpRequest, and uncaught errors. - 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. - 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. - Built-in redaction before the event ever enters the in-memory buffer.
Install
If your project's
.npmrcpins the@botimscope to a private registry, override it for this package. This SDK is published to public npm. If your.npmrccontains a line like@botim:registry=<private-mirror-url>, plainnpm install @botim/mp-debug-sdkwill route to the private mirror, not find the package, and fail. Use:
npm install @botim/mp-debug-sdk --registry=https://registry.npmjs.org/If your repo has no @botim scope override, the flag is harmless — npm uses registry.npmjs.org by default. Don't permanently re-pin the @botim scope to public npm in your .npmrc: it would shadow whatever private registry your other @botim/* packages come from.
1. Add the env config files
Each environment of your mini-program ships with one config file at the project root, in the standard BOTIM mini-program schema:
// botim.dev.json (real schema — abridged)
{
"mp_id": "mbrx_p2p_dev",
"app_id": "me.botim.rd.p2p",
"version": "0.0.127",
"app": {
"version": "0.0.127",
"md5": "fbd6b256d4961abe1cc7703984c31857"
},
"framework": { "version": "57.0.1" },
"category": "Finance"
// ...all other BOTIM platform fields are passed through untouched
}Repeat for botim.uat.json, botim.beta.json, botim.prod.json. The plugin reads mp_id (the canonical mini-program id; the legacy miniProgramId field is also accepted) and derives a deterministic buildSignature from version + app.md5 so it correlates with each release rather than churning on platform-side metadata changes.
The plugin also auto-fills app.name (from app_id) and app.version (from version) so your enableRemoteDebug call doesn't have to repeat them.
2. Wire the Vite plugin
// vite.config.ts
import { botimDebug } from '@botim/mp-debug-sdk/vite';
// Pick the relay endpoint at build time. You control where logs go —
// the SDK never phones home to anything but this URL.
const RELAY_URL = process.env.RELAY_URL ?? 'http://localhost:8090';
export default (({ mode }: { mode: string }) => ({
plugins: [botimDebug({ mode, relayUrl: RELAY_URL })],
}));Switch envs by changing the build flag — no runtime branches:
vite build --mode dev # picks botim.dev.json
vite build --mode uat # picks botim.uat.json
vite build --mode beta # picks botim.beta.json
vite build --mode prod # picks botim.prod.jsonIf a matching botim.{mode}.json is missing or malformed, the build fails — wrong-env bundles can't ship.
Plugin options
| option | type | purpose |
|---|---|---|
| mode | string (optional) | Force a specific mode. When omitted, the plugin auto-reads mode from Vite's resolved config. |
| mapMode | (mode) => env (optional) | Map a custom Vite mode name to a BOTIM env (defaults: development → dev, production → prod, others pass through). |
| relayUrl | string (optional) | The relay endpoint the SDK will POST to. Baked into virtual:botim/config as botimConfig.relayUrl. Trailing slash stripped automatically. |
| root | string (optional) | Project root for resolving botim.{mode}.json. Defaults to Vite's resolved root. |
3. (TypeScript) reference the virtual-module shim
In any .d.ts your tsconfig picks up (or in tsconfig.json#compilerOptions.types):
/// <reference types="@botim/mp-debug-sdk/vite/virtual-shim" />Now import { botimConfig } from 'virtual:botim/config' type-checks (botimConfig.relayUrl included).
4. Enable remote debug at the entry point
import { enableRemoteDebug } from '@botim/mp-debug-sdk';
import { botimConfig } from 'virtual:botim/config';
const handle = await enableRemoteDebug({
// The relay URL the plugin baked in. Falls back to same-origin if you
// didn't pass `relayUrl` to the plugin (e.g. you're using a dev proxy).
endpoint: botimConfig.relayUrl ?? location.origin,
config: botimConfig,
// app is optional — plugin auto-fills from app_id + version in
// botim.{env}.json. Override only when you want a different name/version
// reported in events.
// app: { name: 'checkout-mp', version: '1.4.0' },
// dev/uat/beta builds: consent is implicit
// prod builds: provide a hostToken or set userOptIn after a privacy dialog
consent: { userOptIn: true },
// Optional host hooks the AI command channel can invoke
builtins: {
reload: () => location.reload(),
getState: () => app.snapshot(),
setFeatureFlag: (key, value) => app.flags.set(key, value),
screenshot: async () => await canvas.toBase64PNG(),
},
onError: (err) => console.warn('[debug-sdk]', err),
});That's it — console.*, network calls, and uncaught errors now stream to the relay.
Cross-origin setup
The SDK posts directly to endpoint — there's no proxy required. Your relay must allow the page's origin via CORS. A reference debug-relay implementation typically defaults to CORS_ORIGINS=* for development; tighten via env when going to production:
CORS_ORIGINS=https://my-mp.example.com,https://staging.example.com5. Remote REPL (exec) — default-on
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.
# From any terminal that can reach your relay:
curl -sX POST "<RELAY_URL>/v1/mp/<MP_ID>/devices/<DEVICE_ID>/commands" \
-H 'content-type: application/json' \
-d '{"name":"exec","args":{"code":"console.log(\"hi\"); return window.location.href"}}'Result event:
{
"type": "command-ack",
"payload": {
"command": "exec",
"ok": true,
"result": {
"value": "https://my-mp.example.com/page",
"logs": [{"method":"log","args":["hi"],"ts": 1735324800123}],
"durationMs": 2
}
}
}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.
To opt out (e.g. you ship a non-debug release that still uses the SDK for telemetry only):
await enableRemoteDebug({
// ...,
builtins: { exec: false },
});To inject extra locals for your app:
await enableRemoteDebug({
// ...,
builtins: { exec: { globals: { app: myApp, store: myStore } } },
});
// agent can then call: { code: "return store.getState().user" }Pre-buffer redaction (headers, JWT/long-token regex) is applied to
result.valueandresult.logsbefore they leave the device buffer. The existing prod consent gate (hostTokenoruserOptIn) gates whether attach happens at all. Seedocs/recipes/exec.mdfor deeper notes.
6. Custom commands (optional)
Any AI agent with the right scope can request a command. The SDK only runs commands registered via registerCommand:
handle.registerCommand('clear-cart', async () => {
await store.clearCart();
return { cleared: true };
});
handle.registerCommand('set-locale', async (args) => {
if (typeof args.locale !== 'string') throw new Error('args.locale required');
i18n.setLocale(args.locale);
return { locale: args.locale };
});Anything not registered gets a command-rejected event with reason unknown-command.
7. Lifecycle
await handle.flush(); // force-send buffered events
await handle.stop(); // uninstall interceptors + drain queue
console.log(handle.sid); // server-issued session idProduction safety
- No-op on disable:
enableRemoteDebug({ enabled: false, ... })returns an inert handle. No I/O, no globals wrapped. - Synchronous validation: bad config or missing consent throws immediately, before any interceptor is installed.
- No-throw runtime: once attached, transport/network/redaction errors only surface via
onError. Your app never observes a thrown error from the SDK. - Defense-in-depth redaction: header denylist + JWT/long-token regex + body byte cap, applied before events enter the buffer.
- Single in-flight long-poll: at most one outbound request per device for the AI command channel.
- Self-event suppression: the SDK captures
fetchreference at import time and routes its own ingest/poll calls through that pre-interception fetch — no risk of the SDK observing itself and creating a feedback loop.
Debugging a live mini-program
Once your build is wired and shipped, see docs/live-debugging.md for the end-to-end runbook against your debug-relay deployment. It covers:
- pointing the Vite plugin at the live relay URL,
- pulling errors and tailing sessions from the admin UI,
- a dedicated For AI agents section with
curlrecipes for/v1/mp/{mpId}/events, the SSE live-tail, and command POSTs.
API reference
| Symbol | Purpose |
|---|---|
| enableRemoteDebug(options) | Boot the SDK; returns a RemoteDebugHandle. |
| botimDebug({ mode?, relayUrl?, mapMode? }) | Vite plugin; wire in vite.config.ts. |
| resolveBotimConfig(mode, root) | Pure resolver, for non-Vite bundlers. |
| BotimConfig | Type of botim.{env}.json after resolution. |
| BotimConfigError | Synchronous throw on bad/missing config. |
| BotimConsentError | Synchronous throw on missing consent in prod. |
| RemoteDebugHandle.registerCommand(name, handler) | Allow an AI command. |
| RemoteDebugHandle.flush() | Force-flush the in-memory buffer. |
| RemoteDebugHandle.stop() | Uninstall and drain the queue. |
AI debug skill (Claude Code)
A Claude Code skill that teaches AI agents how to wire and consume this SDK lives at .claude/skills/botim-debug-relay/SKILL.md. Open Claude Code from this repo and the skill auto-loads — no setup needed. To use it from anywhere on your machine:
cp -r .claude/skills/botim-debug-relay ~/.claude/skills/The file in this repo is a mirror. The canonical copy lives in
botim-debug-relay/.claude/skills/botim-debug-relay/SKILL.md. Do not edit the SDK-side copy directly — your changes will be overwritten by the next sync. Edit in the relay repo, then runbash bin/sync-skill.shfrom that repo to propagate here.
License
ISC © BOTIM
