@rrwebcloud/openclaw-session-recording 2026.3.28-1 → 2026.3.28-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 CHANGED
@@ -15,15 +15,17 @@ plugins:
15
15
  entries:
16
16
  rrweb-replay:
17
17
  enabled: true
18
- publicKey: pk_live_your_public_key
19
- extensionMode: bundled
18
+ publicKey: public_key_rr_your_public_key
20
19
  ```
21
20
 
22
21
  ## Notes
23
22
 
24
- - OpenClaw `2026.3.24+` uses the full automatic browser-runtime integration path
23
+ - OpenClaw `2026.3.24+` uses the automatic browser-runtime integration path
25
24
  - OpenClaw `2026.3.13` loads in legacy compatibility mode and reports clear next steps instead of failing on install
26
25
  - `serverUrl` defaults to `https://api.rrwebcloud.com`
27
- - `secretKey` is not required for the bundled browser flow
26
+ - `publicKey` is required for rrwebcloud ingest
27
+ - `secretKey` is not required for recording; it is only needed if you want to query rrwebcloud APIs yourself
28
+ - OpenClaw now records rrweb events locally and uploads authenticated NDJSON from runtime code; it does not rely on browser-side `record.js` ingest
29
+ - the browser extension is optional for the runtime uploader path and is only needed when you specifically want extension-side behavior
28
30
  - the plugin targets managed Chromium browser profiles such as `openclaw`
29
31
  - `replay_session_info` exposes the active replay metadata
package/index.ts CHANGED
@@ -2,9 +2,11 @@ import crypto from "node:crypto";
2
2
  import fs from "node:fs";
3
3
  import path from "node:path";
4
4
  import { Type } from "@sinclair/typebox";
5
- import { readJsonFileWithFallback, writeJsonFileAtomically } from "openclaw/plugin-sdk/json-store";
6
- import { definePluginEntry, type OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-entry";
7
- import { type AnyAgentTool, type OpenClawConfig } from "openclaw/plugin-sdk/plugin-entry";
5
+ import {
6
+ type AnyAgentTool,
7
+ type OpenClawConfig,
8
+ type OpenClawPluginApi,
9
+ } from "openclaw/plugin-sdk";
8
10
 
9
11
  type RrwebReplayConfig = {
10
12
  enabled: boolean;
@@ -39,6 +41,7 @@ type ReplayStateStore = {
39
41
 
40
42
  const RRWEB_PLUGIN_ID = "rrweb-replay";
41
43
  const DEFAULT_RRWEB_API_BASE_URL = "https://api.rrwebcloud.com";
44
+ const DEFAULT_RRWEB_CDN_URL = "https://cdn.jsdelivr.net/npm/rrweb@latest/dist/rrweb.min.js";
42
45
  const LEGACY_BROWSER_RUNTIME_REASON =
43
46
  "This OpenClaw host does not export plugin-sdk/browser-runtime. Automatic rrweb extension wiring requires OpenClaw >=2026.3.24. On legacy hosts, install/configure the rrweb browser addon separately or upgrade OpenClaw.";
44
47
 
@@ -57,6 +60,8 @@ type BrowserRuntimeCompat = {
57
60
  replaySessionId: string;
58
61
  replayServerUrl?: string;
59
62
  replayUrl?: string;
63
+ replayPublicKey?: string;
64
+ replayRrwebCdnUrl?: string;
60
65
  updatedAt: string;
61
66
  }) => void;
62
67
  unregisterManagedBrowserExtensions: (sourceId: string) => void;
@@ -187,15 +192,44 @@ function resolvePluginStateFile(api: OpenClawPluginApi): string {
187
192
  );
188
193
  }
189
194
 
195
+ async function readJsonFileWithFallbackLocal<T>(
196
+ filePath: string,
197
+ fallback: T,
198
+ ): Promise<{ value: T; exists: boolean }> {
199
+ try {
200
+ const raw = await fs.promises.readFile(filePath, "utf8");
201
+ return { value: JSON.parse(raw) as T, exists: true };
202
+ } catch (err) {
203
+ const code = (err as { code?: string }).code;
204
+ if (code === "ENOENT") {
205
+ return { value: fallback, exists: false };
206
+ }
207
+ return { value: fallback, exists: false };
208
+ }
209
+ }
210
+
211
+ async function writeJsonFileAtomicallyLocal(filePath: string, value: unknown): Promise<void> {
212
+ await fs.promises.mkdir(path.dirname(filePath), { recursive: true, mode: 0o700 }).catch(() => {
213
+ // Best-effort directory creation; write below will surface any real failure.
214
+ });
215
+ const tempPath = `${filePath}.${process.pid}.${Date.now()}.${crypto.randomUUID()}.tmp`;
216
+ await fs.promises.writeFile(tempPath, `${JSON.stringify(value, null, 2)}\n`, {
217
+ mode: 0o600,
218
+ });
219
+ await fs.promises.rename(tempPath, filePath);
220
+ }
221
+
190
222
  async function loadReplayState(api: OpenClawPluginApi): Promise<ReplayStateStore> {
191
223
  const filePath = resolvePluginStateFile(api);
192
- const { value } = await readJsonFileWithFallback<ReplayStateStore>(filePath, { sessions: {} });
224
+ const { value } = await readJsonFileWithFallbackLocal<ReplayStateStore>(filePath, {
225
+ sessions: {},
226
+ });
193
227
  return value;
194
228
  }
195
229
 
196
230
  async function saveReplayState(api: OpenClawPluginApi, state: ReplayStateStore): Promise<void> {
197
231
  const filePath = resolvePluginStateFile(api);
198
- await writeJsonFileAtomically(filePath, state);
232
+ await writeJsonFileAtomicallyLocal(filePath, state);
199
233
  }
200
234
 
201
235
  function buildReplaySessionState(params: {
@@ -225,7 +259,6 @@ function buildReplaySessionState(params: {
225
259
 
226
260
  function describeReplayAvailability(params: {
227
261
  config: RrwebReplayConfig;
228
- extensionDir: string | null;
229
262
  browserRuntimeAvailable: boolean;
230
263
  }): { enabled: boolean; reason?: string } {
231
264
  if (!params.config.enabled) {
@@ -237,12 +270,6 @@ function describeReplayAvailability(params: {
237
270
  reason: LEGACY_BROWSER_RUNTIME_REASON,
238
271
  };
239
272
  }
240
- if (!params.extensionDir) {
241
- return {
242
- enabled: false,
243
- reason: "Replay extension directory is missing. Check rrweb-replay.extensionMode/path.",
244
- };
245
- }
246
273
  const publicKey = resolveConfiguredSecret({
247
274
  value: params.config.publicKey,
248
275
  envVarName: params.config.publicKeyEnvVar,
@@ -313,6 +340,13 @@ function resolveBrowserProfileForReplay(params: {
313
340
  return { name: profileName, driver };
314
341
  }
315
342
 
343
+ function resolveConfiguredPublicKey(config: RrwebReplayConfig): string | undefined {
344
+ return resolveConfiguredSecret({
345
+ value: config.publicKey,
346
+ envVarName: config.publicKeyEnvVar,
347
+ });
348
+ }
349
+
316
350
  async function upsertReplaySession(params: {
317
351
  api: OpenClawPluginApi;
318
352
  config: RrwebReplayConfig;
@@ -425,6 +459,8 @@ async function armReplayContext(params: {
425
459
  replaySessionId: replayEntry.replaySessionId,
426
460
  replayServerUrl: replayEntry.replayServerUrl,
427
461
  replayUrl: replayEntry.replayUrl,
462
+ replayPublicKey: resolveConfiguredPublicKey(params.pluginConfig),
463
+ replayRrwebCdnUrl: DEFAULT_RRWEB_CDN_URL,
428
464
  updatedAt: new Date().toISOString(),
429
465
  });
430
466
  return await upsertReplaySession({
@@ -445,7 +481,6 @@ async function armReplayContext(params: {
445
481
  function createReplaySessionInfoTool(params: {
446
482
  api: OpenClawPluginApi;
447
483
  pluginConfig: RrwebReplayConfig;
448
- extensionDir: string | null;
449
484
  browserRuntimePromise: Promise<BrowserRuntimeCompat | null>;
450
485
  context: {
451
486
  sessionKey?: string;
@@ -476,7 +511,6 @@ function createReplaySessionInfoTool(params: {
476
511
  rawParams && typeof rawParams === "object" ? (rawParams as Record<string, unknown>) : null;
477
512
  const availability = describeReplayAvailability({
478
513
  config: params.pluginConfig,
479
- extensionDir: params.extensionDir,
480
514
  browserRuntimeAvailable: Boolean(browserRuntime),
481
515
  });
482
516
  const activate = toolParams?.activate === true;
@@ -530,7 +564,7 @@ function createReplaySessionInfoTool(params: {
530
564
  };
531
565
  }
532
566
 
533
- export default definePluginEntry({
567
+ const rrwebReplayPlugin = {
534
568
  id: RRWEB_PLUGIN_ID,
535
569
  name: "rrweb Replay",
536
570
  description: "Portable rrweb replay bootstrap for OpenClaw-managed browsers.",
@@ -554,8 +588,8 @@ export default definePluginEntry({
554
588
  return;
555
589
  }
556
590
  if (!extensionDir) {
557
- ctx.logger.warn(
558
- "[rrweb-replay] extension directory missing; replay browser extension was not registered",
591
+ ctx.logger.info(
592
+ "[rrweb-replay] extension directory missing; continuing with runtime-side rrwebcloud upload only",
559
593
  );
560
594
  return;
561
595
  }
@@ -578,7 +612,6 @@ export default definePluginEntry({
578
612
  const browserRuntime = await browserRuntimePromise;
579
613
  const availability = describeReplayAvailability({
580
614
  config: pluginConfig,
581
- extensionDir,
582
615
  browserRuntimeAvailable: Boolean(browserRuntime),
583
616
  });
584
617
  await upsertReplaySession({
@@ -621,7 +654,6 @@ export default definePluginEntry({
621
654
  const browserRuntime = await browserRuntimePromise;
622
655
  const availability = describeReplayAvailability({
623
656
  config: pluginConfig,
624
- extensionDir,
625
657
  browserRuntimeAvailable: Boolean(browserRuntime),
626
658
  });
627
659
  if (event.toolName !== "browser" || pluginConfig.recordingPolicy !== "browser-only") {
@@ -649,7 +681,6 @@ export default definePluginEntry({
649
681
  createReplaySessionInfoTool({
650
682
  api,
651
683
  pluginConfig,
652
- extensionDir,
653
684
  browserRuntimePromise,
654
685
  context: {
655
686
  sessionKey: context.sessionKey,
@@ -659,4 +690,6 @@ export default definePluginEntry({
659
690
  { name: "replay_session_info", optional: true },
660
691
  );
661
692
  },
662
- });
693
+ };
694
+
695
+ export default rrwebReplayPlugin;
package/package.json CHANGED
@@ -1,9 +1,8 @@
1
1
  {
2
2
  "name": "@rrwebcloud/openclaw-session-recording",
3
- "version": "2026.3.28-1",
3
+ "version": "2026.3.28-2",
4
4
  "description": "OpenClaw rrweb replay plugin",
5
5
  "license": "MIT",
6
- "type": "module",
7
6
  "files": [
8
7
  ".codex-plugin/",
9
8
  "README.md",
@@ -12,6 +11,10 @@
12
11
  "openclaw.plugin.json",
13
12
  "skills/"
14
13
  ],
14
+ "type": "module",
15
+ "publishConfig": {
16
+ "access": "public"
17
+ },
15
18
  "dependencies": {
16
19
  "@sinclair/typebox": "0.34.48"
17
20
  },
@@ -26,11 +29,10 @@
26
29
  "optional": true
27
30
  }
28
31
  },
29
- "publishConfig": {
30
- "access": "public"
31
- },
32
32
  "openclaw": {
33
- "extensions": ["./index.ts"],
33
+ "extensions": [
34
+ "./index.ts"
35
+ ],
34
36
  "install": {
35
37
  "npmSpec": "@rrwebcloud/openclaw-session-recording",
36
38
  "localPath": "extensions/rrweb-replay",
@@ -7,14 +7,15 @@ What it does:
7
7
  - Explains whether the `rrweb-replay` plugin is configured and active
8
8
  - Shows the current replay session id via `replay_session_info`
9
9
  - Helps activate replay context when `recordingPolicy` is `opt-in-tool`
10
- - Points you to browser profile and extension troubleshooting
10
+ - Points you to browser profile and rrwebcloud upload troubleshooting
11
11
 
12
12
  Operational notes:
13
13
 
14
14
  - Runtime setup is handled by the native `rrweb-replay` plugin, not by this skill
15
15
  - The core browser runtime can also expose replay through `browser.replay.*` when the rrwebcloud extension artifact is wired directly into managed browser startup
16
16
  - The plugin targets OpenClaw-managed Chromium profiles such as `openclaw`
17
- - The streamlined OpenClaw path only needs a public key; the bundled browser bootstrap defaults to the rrweb Cloud API endpoint and does not require a secret key
17
+ - The streamlined OpenClaw path records rrweb events in the managed browser and uploads authenticated NDJSON from runtime code
18
+ - The streamlined OpenClaw path only needs a public key; the rrweb Cloud API endpoint defaults to `https://api.rrwebcloud.com` and recording does not require a secret key
18
19
  - Minimal config:
19
20
 
20
21
  ```yaml
@@ -22,17 +23,15 @@ plugins:
22
23
  entries:
23
24
  rrweb-replay:
24
25
  enabled: true
25
- publicKey: pk_live_your_public_key
26
- extensionMode: bundled
26
+ publicKey: public_key_rr_your_public_key
27
27
  ```
28
28
 
29
29
  - If replay reports disabled, check:
30
- - `browser.replay.enabled`
31
- - `browser.replay.extensionPath`
32
30
  - `plugins.entries.rrweb-replay`
33
- - `rrweb-replay.extensionMode` / `rrweb-replay.extensionPath`
34
31
  - `rrweb-replay.publicKey` or `rrweb-replay.publicKeyEnvVar`
35
- - `browser.profiles.<name>.extensions` only if you are bypassing the plugin runtime seam
32
+ - `rrweb-replay.serverUrl` only if you intentionally override the default API host
33
+ - `browser.profiles.<name>.driver` is a managed profile such as `openclaw`
34
+ - `browser.profiles.<name>.extensions` only if you are intentionally layering extension-side behavior on top of runtime upload
36
35
 
37
36
  Useful command/tool flow:
38
37
 
@@ -42,7 +41,7 @@ Useful command/tool flow:
42
41
 
43
42
  Troubleshooting:
44
43
 
45
- - Missing extension directory: the bundled bootstrap extension was not staged; verify plugin install layout
44
+ - Missing extension directory: runtime upload can still work; only extension-side behavior is unavailable
46
45
  - No replay session id: start a new OpenClaw session or use the browser tool so the plugin can bind session metadata
47
46
  - Wrong profile: pass `profile` to `replay_session_info activate=true` or use the browser tool with an explicit `profile`
48
47
  - Advanced setup: only override `rrweb-replay.serverUrl` if you are targeting a non-default replay API endpoint