@botim/mp-debug-sdk 0.8.0 → 1.2.0
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/auto.cjs +1874 -0
- package/dist/auto.d.cts +2 -0
- package/dist/auto.d.ts +2 -0
- package/dist/auto.js +1857 -0
- package/dist/index.cjs +28 -10
- package/dist/index.d.cts +15 -17
- package/dist/index.d.ts +15 -17
- package/dist/index.js +28 -11
- package/dist/vite/plugin.cjs +98 -29
- package/dist/vite/plugin.d.cts +16 -6
- package/dist/vite/plugin.d.ts +16 -6
- package/dist/vite/plugin.js +99 -30
- package/dist/vite/virtual-shim.d.ts +3 -3
- package/package.json +20 -7
- package/README.md +0 -265
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/types.cjs.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/vite/plugin.cjs.map +0 -1
- package/dist/vite/plugin.js.map +0 -1
package/dist/vite/plugin.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/vite/resolve-config.ts
|
|
2
2
|
import { readFileSync, existsSync } from "fs";
|
|
3
|
-
import { resolve, isAbsolute } from "path";
|
|
3
|
+
import { resolve, isAbsolute, basename } from "path";
|
|
4
4
|
import { createHash } from "crypto";
|
|
5
5
|
|
|
6
6
|
// src/errors.ts
|
|
@@ -16,7 +16,7 @@ var BotimConfigError = class extends Error {
|
|
|
16
16
|
|
|
17
17
|
// src/vite/resolve-config.ts
|
|
18
18
|
var VALID_ENVS = ["dev", "uat", "beta", "prod"];
|
|
19
|
-
var ID_PATTERN = /^[a-z][a-z0-
|
|
19
|
+
var ID_PATTERN = /^[a-z][a-z0-9._-]{1,127}$/i;
|
|
20
20
|
function defaultMapMode(mode) {
|
|
21
21
|
if (mode === "development") return "dev";
|
|
22
22
|
if (mode === "production") return "prod";
|
|
@@ -38,30 +38,44 @@ function resolveBotimConfig(mode, root, opts = {}) {
|
|
|
38
38
|
}
|
|
39
39
|
const env = mapped;
|
|
40
40
|
const projectRoot = isAbsolute(root) ? root : resolve(process.cwd(), root);
|
|
41
|
-
const
|
|
42
|
-
const
|
|
41
|
+
const explicit = opts.configFileName !== void 0;
|
|
42
|
+
const candidates = explicit ? [
|
|
43
|
+
{
|
|
44
|
+
env,
|
|
45
|
+
path: (() => {
|
|
46
|
+
const fileName = resolveFileName(opts.configFileName, env, mode);
|
|
47
|
+
return isAbsolute(fileName) ? fileName : resolve(projectRoot, fileName);
|
|
48
|
+
})()
|
|
49
|
+
}
|
|
50
|
+
] : buildCandidatePaths(projectRoot, env);
|
|
43
51
|
const inline = opts.inlineConfig;
|
|
44
|
-
const inlineHasIdentity = typeof inline?.
|
|
52
|
+
const inlineHasIdentity = typeof inline?.app_id === "string" && inline.app_id.length > 0 || typeof inline?.miniProgramId === "string" && inline.miniProgramId.length > 0;
|
|
53
|
+
const source = candidates.find((c) => existsSync(c.path));
|
|
45
54
|
let raw;
|
|
46
55
|
let parsedFromFile = {};
|
|
47
|
-
if (
|
|
48
|
-
raw = readFileSync(
|
|
56
|
+
if (source) {
|
|
57
|
+
raw = readFileSync(source.path, "utf8");
|
|
49
58
|
try {
|
|
50
59
|
parsedFromFile = JSON.parse(raw);
|
|
51
60
|
} catch (err) {
|
|
52
61
|
const detail = err instanceof Error ? err.message : String(err);
|
|
53
|
-
throw new BotimConfigError(`failed to parse JSON in ${
|
|
62
|
+
throw new BotimConfigError(`failed to parse JSON in ${source.path}: ${detail}`, {
|
|
54
63
|
code: "config-invalid-json",
|
|
55
|
-
path:
|
|
64
|
+
path: source.path
|
|
56
65
|
});
|
|
57
66
|
}
|
|
58
|
-
|
|
67
|
+
if (source.env !== env) {
|
|
68
|
+
console.warn(
|
|
69
|
+
`[@botim/debug-sdk] no config for env "${env}"; borrowed app_id from ${basename(source.path)} (env stays ${env})`
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
} else if (explicit && !inlineHasIdentity) {
|
|
73
|
+
const wanted = candidates[0].path;
|
|
59
74
|
throw new BotimConfigError(
|
|
60
|
-
`expected config file not found: ${
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
{ code: "config-missing", path: filePath }
|
|
75
|
+
`expected config file not found: ${wanted}
|
|
76
|
+
Create it with at least { "app_id": "..." }, pass { configFileName },
|
|
77
|
+
or supply inlineConfig with app_id.`,
|
|
78
|
+
{ code: "config-missing", path: wanted }
|
|
65
79
|
);
|
|
66
80
|
}
|
|
67
81
|
const parsed = {
|
|
@@ -75,22 +89,28 @@ function resolveBotimConfig(mode, root, opts = {}) {
|
|
|
75
89
|
parsed.package_url = { ...parsedFromFile.package_url, ...inline?.package_url };
|
|
76
90
|
}
|
|
77
91
|
if (parsed.deleted === 1) {
|
|
92
|
+
const label = source?.path ?? "inlineConfig";
|
|
78
93
|
throw new BotimConfigError(
|
|
79
|
-
`${
|
|
80
|
-
{ code: "config-deleted", path:
|
|
94
|
+
`${label} reports the mini-program as deleted (deleted: 1). Cannot ship debug SDK against a retired mini-program.`,
|
|
95
|
+
{ code: "config-deleted", path: source?.path }
|
|
81
96
|
);
|
|
82
97
|
}
|
|
83
|
-
const idValue = parsed.
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
98
|
+
const idValue = parsed.app_id ?? parsed.miniProgramId;
|
|
99
|
+
let miniProgramId;
|
|
100
|
+
if (typeof idValue === "string" && idValue.length > 0) {
|
|
101
|
+
if (!ID_PATTERN.test(idValue)) {
|
|
102
|
+
const label = source?.path ?? "inlineConfig";
|
|
103
|
+
throw new BotimConfigError(
|
|
104
|
+
`${label} field "app_id" must match ${ID_PATTERN.toString()} (got: ${JSON.stringify(idValue)})`,
|
|
105
|
+
{ code: "config-invalid-id", path: source?.path }
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
miniProgramId = idValue;
|
|
109
|
+
} else {
|
|
110
|
+
miniProgramId = synthesizeMiniProgramId(projectRoot, env);
|
|
111
|
+
const reason = source ? `config ${basename(source.path)} has no app_id` : "no botim_config.*.json found for any env";
|
|
112
|
+
console.warn(
|
|
113
|
+
`[@botim/debug-sdk] ${reason}; synthesized id ${miniProgramId} (env=${env})`
|
|
94
114
|
);
|
|
95
115
|
}
|
|
96
116
|
const md5 = typeof parsed.app?.md5 === "string" ? parsed.app.md5 : typeof parsed.package_url?.md5 === "string" ? parsed.package_url.md5 : void 0;
|
|
@@ -101,13 +121,13 @@ function resolveBotimConfig(mode, root, opts = {}) {
|
|
|
101
121
|
} else if (versionForSig && md5) {
|
|
102
122
|
buildSignature = `v${versionForSig}+md5:${md5.slice(0, 12)}`;
|
|
103
123
|
} else {
|
|
104
|
-
const sigSource = raw ?? JSON.stringify({
|
|
124
|
+
const sigSource = raw ?? JSON.stringify({ app_id: miniProgramId, env, version: versionForSig, app: parsed.app });
|
|
105
125
|
buildSignature = "sha256:" + createHash("sha256").update(sigSource).digest("hex").slice(0, 12);
|
|
106
126
|
}
|
|
107
127
|
const cfg = {
|
|
108
128
|
// Spread first so the canonical fields below override anything inherited.
|
|
109
129
|
...parsed,
|
|
110
|
-
miniProgramId
|
|
130
|
+
miniProgramId,
|
|
111
131
|
env,
|
|
112
132
|
buildSignature,
|
|
113
133
|
appName: typeof parsed.app_id === "string" ? parsed.app_id : parsed.appName,
|
|
@@ -140,6 +160,20 @@ function resolveFileName(override, env, mode) {
|
|
|
140
160
|
{ code: "config-invalid-filename" }
|
|
141
161
|
);
|
|
142
162
|
}
|
|
163
|
+
function synthesizeMiniProgramId(projectRoot, env) {
|
|
164
|
+
const dir = basename(projectRoot);
|
|
165
|
+
const hash = createHash("sha256").update(`${dir}:${env}`).digest("hex").slice(0, 8);
|
|
166
|
+
return `mp-${hash}`;
|
|
167
|
+
}
|
|
168
|
+
function buildCandidatePaths(projectRoot, selected) {
|
|
169
|
+
const order = [selected, ...VALID_ENVS.filter((e) => e !== selected)];
|
|
170
|
+
const out = [];
|
|
171
|
+
for (const e of order) {
|
|
172
|
+
out.push({ env: e, path: resolve(projectRoot, `botim_config.${e}.json`) });
|
|
173
|
+
out.push({ env: e, path: resolve(projectRoot, `botim.${e}.json`) });
|
|
174
|
+
}
|
|
175
|
+
return out;
|
|
176
|
+
}
|
|
143
177
|
|
|
144
178
|
// src/vite/plugin.ts
|
|
145
179
|
var VIRTUAL_ID = "virtual:botim/config";
|
|
@@ -185,6 +219,41 @@ export const botimConfig = Object.freeze(${JSON.stringify(resolved)});
|
|
|
185
219
|
export default botimConfig;
|
|
186
220
|
`;
|
|
187
221
|
return { code: body, map: null };
|
|
222
|
+
},
|
|
223
|
+
// Enable relay debugging from vite.config alone: when `relayToken` is set,
|
|
224
|
+
// inject an `enableRemoteDebug(...)` bootstrap into index.html. This hook
|
|
225
|
+
// runs in BOTH dev (`serve`) and `build`, so the same call ships to the
|
|
226
|
+
// built/deployed bundle. App source is never touched; removing the option
|
|
227
|
+
// (or the plugin) removes the injection.
|
|
228
|
+
transformIndexHtml: {
|
|
229
|
+
order: "pre",
|
|
230
|
+
handler() {
|
|
231
|
+
const token = options.relayToken;
|
|
232
|
+
if (typeof token !== "string" || token.length === 0) return void 0;
|
|
233
|
+
if (!resolved) return void 0;
|
|
234
|
+
const endpoint = (options.relayUrl ?? resolved.relayUrl ?? "").replace(/\/+$/, "");
|
|
235
|
+
if (!endpoint) {
|
|
236
|
+
console.warn(
|
|
237
|
+
"[@botim/debug-sdk:vite] relayToken set but no relayUrl \u2014 debug bootstrap NOT injected (pass relayUrl)."
|
|
238
|
+
);
|
|
239
|
+
return void 0;
|
|
240
|
+
}
|
|
241
|
+
const debugOptions = {
|
|
242
|
+
endpoint,
|
|
243
|
+
token,
|
|
244
|
+
config: { miniProgramId: resolved.miniProgramId, env: resolved.env },
|
|
245
|
+
consent: { userOptIn: true }
|
|
246
|
+
};
|
|
247
|
+
return [
|
|
248
|
+
{
|
|
249
|
+
tag: "script",
|
|
250
|
+
attrs: { type: "module" },
|
|
251
|
+
children: `import { enableRemoteDebug } from '@botim/mp-debug-sdk';
|
|
252
|
+
enableRemoteDebug(${JSON.stringify(debugOptions)}).catch(() => {});`,
|
|
253
|
+
injectTo: "head-prepend"
|
|
254
|
+
}
|
|
255
|
+
];
|
|
256
|
+
}
|
|
188
257
|
}
|
|
189
258
|
};
|
|
190
259
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Ambient module declaration for the virtual module emitted by
|
|
3
|
-
* `@botim/debug-sdk/vite`. Add this file to your `tsconfig.json#include`
|
|
4
|
-
* (or reference `@botim/debug-sdk/vite` from `compilerOptions.types`) and
|
|
3
|
+
* `@botim/mp-debug-sdk/vite`. Add this file to your `tsconfig.json#include`
|
|
4
|
+
* (or reference `@botim/mp-debug-sdk/vite` from `compilerOptions.types`) and
|
|
5
5
|
* `import { botimConfig } from 'virtual:botim/config'` will type-check.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
declare module 'virtual:botim/config' {
|
|
9
|
-
import type { BotimConfig } from '@botim/debug-sdk';
|
|
9
|
+
import type { BotimConfig } from '@botim/mp-debug-sdk';
|
|
10
10
|
/**
|
|
11
11
|
* Resolved BotimConfig augmented with the optional `relayUrl` carried
|
|
12
12
|
* over from the Vite plugin's `relayUrl` option (when provided). Pass
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@botim/mp-debug-sdk",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "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.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -17,6 +17,11 @@
|
|
|
17
17
|
"import": "./dist/types.js",
|
|
18
18
|
"require": "./dist/types.cjs"
|
|
19
19
|
},
|
|
20
|
+
"./auto": {
|
|
21
|
+
"types": "./dist/auto.d.ts",
|
|
22
|
+
"import": "./dist/auto.js",
|
|
23
|
+
"require": "./dist/auto.cjs"
|
|
24
|
+
},
|
|
20
25
|
"./vite": {
|
|
21
26
|
"types": "./dist/vite/plugin.d.ts",
|
|
22
27
|
"import": "./dist/vite/plugin.js",
|
|
@@ -25,9 +30,11 @@
|
|
|
25
30
|
"./vite/virtual-shim": "./dist/vite/virtual-shim.d.ts"
|
|
26
31
|
},
|
|
27
32
|
"files": [
|
|
28
|
-
"dist",
|
|
29
|
-
"
|
|
30
|
-
"
|
|
33
|
+
"dist/**/*.js",
|
|
34
|
+
"dist/**/*.cjs",
|
|
35
|
+
"dist/**/*.d.ts",
|
|
36
|
+
"dist/**/*.d.cts",
|
|
37
|
+
"LICENSE"
|
|
31
38
|
],
|
|
32
39
|
"scripts": {
|
|
33
40
|
"build": "tsup && npm run build:copy-shim",
|
|
@@ -37,7 +44,9 @@
|
|
|
37
44
|
"test": "vitest run",
|
|
38
45
|
"test:watch": "vitest",
|
|
39
46
|
"check-no-leaks": "node scripts/check-no-leaks.mjs",
|
|
40
|
-
"prepublishOnly": "npm run type-check && npm test && npm run build && npm run check-no-leaks"
|
|
47
|
+
"prepublishOnly": "npm run type-check && npm test && npm run build && npm run check-no-leaks",
|
|
48
|
+
"prepack": "node -e \"const fs=require('node:fs');if(fs.existsSync('README.md'))fs.renameSync('README.md','.README.repo.md')\"",
|
|
49
|
+
"postpack": "node -e \"const fs=require('node:fs');if(fs.existsSync('.README.repo.md'))fs.renameSync('.README.repo.md','README.md')\""
|
|
41
50
|
},
|
|
42
51
|
"keywords": [
|
|
43
52
|
"botim",
|
|
@@ -55,9 +64,13 @@
|
|
|
55
64
|
"node": ">=16.0.0"
|
|
56
65
|
},
|
|
57
66
|
"publishConfig": {
|
|
58
|
-
"access": "public"
|
|
67
|
+
"access": "public",
|
|
68
|
+
"registry": "https://registry.npmjs.org/"
|
|
59
69
|
},
|
|
60
|
-
"sideEffects":
|
|
70
|
+
"sideEffects": [
|
|
71
|
+
"./dist/auto.js",
|
|
72
|
+
"./dist/auto.cjs"
|
|
73
|
+
],
|
|
61
74
|
"devDependencies": {
|
|
62
75
|
"@types/node": "24.9.2",
|
|
63
76
|
"happy-dom": "^15.11.7",
|
package/README.md
DELETED
|
@@ -1,265 +0,0 @@
|
|
|
1
|
-
# @botim/mp-debug-sdk
|
|
2
|
-
|
|
3
|
-
> 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.
|
|
4
|
-
|
|
5
|
-
## Why
|
|
6
|
-
|
|
7
|
-
Mini-programs run on user devices in environments you can't easily attach a debugger to. This SDK gives you:
|
|
8
|
-
|
|
9
|
-
- **Live logs** of `console.*`, `fetch`, `XMLHttpRequest`, and uncaught errors.
|
|
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`, `exec` (default-on remote REPL), or any custom command you allow. Anything not registered is rejected.
|
|
12
|
-
- **Built-in redaction** before the event ever enters the in-memory buffer.
|
|
13
|
-
|
|
14
|
-
## Install
|
|
15
|
-
|
|
16
|
-
> **If your project's `.npmrc` pins the `@botim` scope to a private registry, override it for this package.**
|
|
17
|
-
> This SDK is published to public npm. If your `.npmrc` contains a line like `@botim:registry=<private-mirror-url>`, plain `npm install @botim/mp-debug-sdk` will route to the private mirror, not find the package, and fail. Use:
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
npm install @botim/mp-debug-sdk --registry=https://registry.npmjs.org/
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
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.
|
|
24
|
-
|
|
25
|
-
## 1. Add the env config files
|
|
26
|
-
|
|
27
|
-
Each environment of your mini-program ships with one config file at the project root, in the standard BOTIM mini-program schema:
|
|
28
|
-
|
|
29
|
-
```jsonc
|
|
30
|
-
// botim.dev.json (real schema — abridged)
|
|
31
|
-
{
|
|
32
|
-
"mp_id": "mbrx_p2p_dev",
|
|
33
|
-
"app_id": "me.botim.rd.p2p",
|
|
34
|
-
"version": "0.0.127",
|
|
35
|
-
"app": {
|
|
36
|
-
"version": "0.0.127",
|
|
37
|
-
"md5": "fbd6b256d4961abe1cc7703984c31857"
|
|
38
|
-
},
|
|
39
|
-
"framework": { "version": "57.0.1" },
|
|
40
|
-
"category": "Finance"
|
|
41
|
-
// ...all other BOTIM platform fields are passed through untouched
|
|
42
|
-
}
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
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.
|
|
46
|
-
|
|
47
|
-
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.
|
|
48
|
-
|
|
49
|
-
## 2. Wire the Vite plugin
|
|
50
|
-
|
|
51
|
-
```ts
|
|
52
|
-
// vite.config.ts
|
|
53
|
-
import { botimDebug } from '@botim/mp-debug-sdk/vite';
|
|
54
|
-
|
|
55
|
-
// Pick the relay endpoint at build time. You control where logs go —
|
|
56
|
-
// the SDK never phones home to anything but this URL.
|
|
57
|
-
const RELAY_URL = process.env.RELAY_URL ?? 'http://localhost:8090';
|
|
58
|
-
|
|
59
|
-
export default (({ mode }: { mode: string }) => ({
|
|
60
|
-
plugins: [botimDebug({ mode, relayUrl: RELAY_URL })],
|
|
61
|
-
}));
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
Switch envs by changing the build flag — no runtime branches:
|
|
65
|
-
|
|
66
|
-
```bash
|
|
67
|
-
vite build --mode dev # picks botim.dev.json
|
|
68
|
-
vite build --mode uat # picks botim.uat.json
|
|
69
|
-
vite build --mode beta # picks botim.beta.json
|
|
70
|
-
vite build --mode prod # picks botim.prod.json
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
If a matching `botim.{mode}.json` is missing or malformed, **the build fails** — wrong-env bundles can't ship.
|
|
74
|
-
|
|
75
|
-
### Plugin options
|
|
76
|
-
|
|
77
|
-
| option | type | purpose |
|
|
78
|
-
|---|---|---|
|
|
79
|
-
| `mode` | `string` (optional) | Force a specific mode. When omitted, the plugin auto-reads `mode` from Vite's resolved config. |
|
|
80
|
-
| `mapMode` | `(mode) => env` (optional) | Map a custom Vite mode name to a BOTIM env (defaults: `development → dev`, `production → prod`, others pass through). |
|
|
81
|
-
| `relayUrl` | `string` (optional) | The relay endpoint the SDK will POST to. Baked into `virtual:botim/config` as `botimConfig.relayUrl`. Trailing slash stripped automatically. |
|
|
82
|
-
| `root` | `string` (optional) | Project root for resolving `botim.{mode}.json`. Defaults to Vite's resolved root. |
|
|
83
|
-
|
|
84
|
-
## 3. (TypeScript) reference the virtual-module shim
|
|
85
|
-
|
|
86
|
-
In any `.d.ts` your tsconfig picks up (or in `tsconfig.json#compilerOptions.types`):
|
|
87
|
-
|
|
88
|
-
```ts
|
|
89
|
-
/// <reference types="@botim/mp-debug-sdk/vite/virtual-shim" />
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
Now `import { botimConfig } from 'virtual:botim/config'` type-checks (`botimConfig.relayUrl` included).
|
|
93
|
-
|
|
94
|
-
## 4. Enable remote debug at the entry point
|
|
95
|
-
|
|
96
|
-
```ts
|
|
97
|
-
import { enableRemoteDebug } from '@botim/mp-debug-sdk';
|
|
98
|
-
import { botimConfig } from 'virtual:botim/config';
|
|
99
|
-
|
|
100
|
-
const handle = await enableRemoteDebug({
|
|
101
|
-
// The relay URL the plugin baked in. Falls back to same-origin if you
|
|
102
|
-
// didn't pass `relayUrl` to the plugin (e.g. you're using a dev proxy).
|
|
103
|
-
// For a gateway-hosted relay this is the gateway origin (optionally with a
|
|
104
|
-
// path prefix), e.g. 'https://proxy-uat-ae.botim.me/botim-cli-gateway'.
|
|
105
|
-
endpoint: botimConfig.relayUrl ?? location.origin,
|
|
106
|
-
// Relay connect token (rdt_…). REQUIRED for gateway-hosted relays, which
|
|
107
|
-
// gate attach on `Authorization: Bearer rdt_…`. Mint one bound to this
|
|
108
|
-
// (mpId, env) with `botim-cli relay token --mp <id> --env uat` (24 h TTL).
|
|
109
|
-
// Omit for the legacy standalone relay, which ignores it.
|
|
110
|
-
token: import.meta.env.VITE_RELAY_TOKEN,
|
|
111
|
-
config: botimConfig,
|
|
112
|
-
// app is optional — plugin auto-fills from app_id + version in
|
|
113
|
-
// botim.{env}.json. Override only when you want a different name/version
|
|
114
|
-
// reported in events.
|
|
115
|
-
// app: { name: 'checkout-mp', version: '1.4.0' },
|
|
116
|
-
|
|
117
|
-
// dev/uat/beta builds: consent is implicit
|
|
118
|
-
// prod builds: provide a hostToken or set userOptIn after a privacy dialog
|
|
119
|
-
consent: { userOptIn: true },
|
|
120
|
-
|
|
121
|
-
// Optional host hooks the AI command channel can invoke
|
|
122
|
-
builtins: {
|
|
123
|
-
reload: () => location.reload(),
|
|
124
|
-
getState: () => app.snapshot(),
|
|
125
|
-
setFeatureFlag: (key, value) => app.flags.set(key, value),
|
|
126
|
-
screenshot: async () => await canvas.toBase64PNG(),
|
|
127
|
-
},
|
|
128
|
-
|
|
129
|
-
onError: (err) => console.warn('[debug-sdk]', err),
|
|
130
|
-
});
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
That's it — `console.*`, network calls, and uncaught errors now stream to the relay.
|
|
134
|
-
|
|
135
|
-
### Cross-origin setup
|
|
136
|
-
|
|
137
|
-
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:
|
|
138
|
-
|
|
139
|
-
```bash
|
|
140
|
-
CORS_ORIGINS=https://my-mp.example.com,https://staging.example.com
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
## 5. Remote REPL (`exec`) — default-on
|
|
144
|
-
|
|
145
|
-
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.
|
|
146
|
-
|
|
147
|
-
```bash
|
|
148
|
-
# From any terminal that can reach your relay:
|
|
149
|
-
curl -sX POST "<RELAY_URL>/v1/mp/<MP_ID>/devices/<DEVICE_ID>/commands" \
|
|
150
|
-
-H 'content-type: application/json' \
|
|
151
|
-
-d '{"name":"exec","args":{"code":"console.log(\"hi\"); return window.location.href"}}'
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
Result event:
|
|
155
|
-
|
|
156
|
-
```jsonc
|
|
157
|
-
{
|
|
158
|
-
"type": "command-ack",
|
|
159
|
-
"payload": {
|
|
160
|
-
"command": "exec",
|
|
161
|
-
"ok": true,
|
|
162
|
-
"result": {
|
|
163
|
-
"value": "https://my-mp.example.com/page",
|
|
164
|
-
"logs": [{"method":"log","args":["hi"],"ts": 1735324800123}],
|
|
165
|
-
"durationMs": 2
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
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.
|
|
172
|
-
|
|
173
|
-
**To opt out** (e.g. you ship a non-debug release that still uses the SDK for telemetry only):
|
|
174
|
-
|
|
175
|
-
```ts
|
|
176
|
-
await enableRemoteDebug({
|
|
177
|
-
// ...,
|
|
178
|
-
builtins: { exec: false },
|
|
179
|
-
});
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
**To inject extra locals** for your app:
|
|
183
|
-
|
|
184
|
-
```ts
|
|
185
|
-
await enableRemoteDebug({
|
|
186
|
-
// ...,
|
|
187
|
-
builtins: { exec: { globals: { app: myApp, store: myStore } } },
|
|
188
|
-
});
|
|
189
|
-
// agent can then call: { code: "return store.getState().user" }
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
> 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.
|
|
193
|
-
|
|
194
|
-
## 6. Custom commands (optional)
|
|
195
|
-
|
|
196
|
-
Any AI agent with the right scope can request a command. The SDK only runs commands registered via `registerCommand`:
|
|
197
|
-
|
|
198
|
-
```ts
|
|
199
|
-
handle.registerCommand('clear-cart', async () => {
|
|
200
|
-
await store.clearCart();
|
|
201
|
-
return { cleared: true };
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
handle.registerCommand('set-locale', async (args) => {
|
|
205
|
-
if (typeof args.locale !== 'string') throw new Error('args.locale required');
|
|
206
|
-
i18n.setLocale(args.locale);
|
|
207
|
-
return { locale: args.locale };
|
|
208
|
-
});
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
Anything not registered gets a `command-rejected` event with reason `unknown-command`.
|
|
212
|
-
|
|
213
|
-
## 7. Lifecycle
|
|
214
|
-
|
|
215
|
-
```ts
|
|
216
|
-
await handle.flush(); // force-send buffered events
|
|
217
|
-
await handle.stop(); // uninstall interceptors + drain queue
|
|
218
|
-
console.log(handle.sid); // server-issued session id
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
## Production safety
|
|
222
|
-
|
|
223
|
-
- **No-op on disable**: `enableRemoteDebug({ enabled: false, ... })` returns an inert handle. No I/O, no globals wrapped.
|
|
224
|
-
- **Synchronous validation**: bad config or missing consent throws immediately, *before* any interceptor is installed.
|
|
225
|
-
- **No-throw runtime**: once attached, transport/network/redaction errors only surface via `onError`. Your app never observes a thrown error from the SDK.
|
|
226
|
-
- **Defense-in-depth redaction**: header denylist + JWT/long-token regex + body byte cap, applied **before** events enter the buffer.
|
|
227
|
-
- **Single in-flight long-poll**: at most one outbound request per device for the AI command channel.
|
|
228
|
-
- **Self-event suppression**: the SDK captures `fetch` reference 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.
|
|
229
|
-
|
|
230
|
-
## Debugging a live mini-program
|
|
231
|
-
|
|
232
|
-
Once your build is wired and shipped, see **[`docs/live-debugging.md`](./docs/live-debugging.md)** for the end-to-end runbook against your debug-relay deployment. It covers:
|
|
233
|
-
|
|
234
|
-
- pointing the Vite plugin at the live relay URL,
|
|
235
|
-
- pulling errors and tailing sessions from the admin UI,
|
|
236
|
-
- a dedicated **For AI agents** section with `curl` recipes for `/v1/mp/{mpId}/events`, the SSE live-tail, and command POSTs.
|
|
237
|
-
|
|
238
|
-
## API reference
|
|
239
|
-
|
|
240
|
-
| Symbol | Purpose |
|
|
241
|
-
|---|---|
|
|
242
|
-
| `enableRemoteDebug(options)` | Boot the SDK; returns a `RemoteDebugHandle`. |
|
|
243
|
-
| `botimDebug({ mode?, relayUrl?, mapMode? })` | Vite plugin; wire in `vite.config.ts`. |
|
|
244
|
-
| `resolveBotimConfig(mode, root)` | Pure resolver, for non-Vite bundlers. |
|
|
245
|
-
| `BotimConfig` | Type of `botim.{env}.json` after resolution. |
|
|
246
|
-
| `BotimConfigError` | Synchronous throw on bad/missing config. |
|
|
247
|
-
| `BotimConsentError` | Synchronous throw on missing consent in prod. |
|
|
248
|
-
| `RemoteDebugHandle.registerCommand(name, handler)` | Allow an AI command. |
|
|
249
|
-
| `RemoteDebugHandle.flush()` | Force-flush the in-memory buffer. |
|
|
250
|
-
| `RemoteDebugHandle.stop()` | Uninstall and drain the queue. |
|
|
251
|
-
|
|
252
|
-
## AI debug skill (Claude Code)
|
|
253
|
-
|
|
254
|
-
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:
|
|
255
|
-
|
|
256
|
-
```bash
|
|
257
|
-
cp -r .claude/skills/botim-debug-relay ~/.claude/skills/
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
> **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 run `bash bin/sync-skill.sh` from that repo to propagate here.
|
|
261
|
-
|
|
262
|
-
## License
|
|
263
|
-
|
|
264
|
-
[ISC](./LICENSE) © BOTIM
|
|
265
|
-
|