@rrwebcloud/openclaw-session-recording 2026.3.28-1 → 2026.3.28-3

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,12 @@ 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
+ definePluginEntry,
7
+ type AnyAgentTool,
8
+ type OpenClawConfig,
9
+ type OpenClawPluginApi,
10
+ } from "openclaw/plugin-sdk/plugin-entry";
8
11
 
9
12
  type RrwebReplayConfig = {
10
13
  enabled: boolean;
@@ -39,6 +42,7 @@ type ReplayStateStore = {
39
42
 
40
43
  const RRWEB_PLUGIN_ID = "rrweb-replay";
41
44
  const DEFAULT_RRWEB_API_BASE_URL = "https://api.rrwebcloud.com";
45
+ const DEFAULT_RRWEB_CDN_URL = "https://cdn.jsdelivr.net/npm/rrweb@latest/dist/rrweb.min.js";
42
46
  const LEGACY_BROWSER_RUNTIME_REASON =
43
47
  "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
48
 
@@ -57,6 +61,8 @@ type BrowserRuntimeCompat = {
57
61
  replaySessionId: string;
58
62
  replayServerUrl?: string;
59
63
  replayUrl?: string;
64
+ replayPublicKey?: string;
65
+ replayRrwebCdnUrl?: string;
60
66
  updatedAt: string;
61
67
  }) => void;
62
68
  unregisterManagedBrowserExtensions: (sourceId: string) => void;
@@ -187,15 +193,44 @@ function resolvePluginStateFile(api: OpenClawPluginApi): string {
187
193
  );
188
194
  }
189
195
 
196
+ async function readJsonFileWithFallbackLocal<T>(
197
+ filePath: string,
198
+ fallback: T,
199
+ ): Promise<{ value: T; exists: boolean }> {
200
+ try {
201
+ const raw = await fs.promises.readFile(filePath, "utf8");
202
+ return { value: JSON.parse(raw) as T, exists: true };
203
+ } catch (err) {
204
+ const code = (err as { code?: string }).code;
205
+ if (code === "ENOENT") {
206
+ return { value: fallback, exists: false };
207
+ }
208
+ return { value: fallback, exists: false };
209
+ }
210
+ }
211
+
212
+ async function writeJsonFileAtomicallyLocal(filePath: string, value: unknown): Promise<void> {
213
+ await fs.promises.mkdir(path.dirname(filePath), { recursive: true, mode: 0o700 }).catch(() => {
214
+ // Best-effort directory creation; write below will surface any real failure.
215
+ });
216
+ const tempPath = `${filePath}.${process.pid}.${Date.now()}.${crypto.randomUUID()}.tmp`;
217
+ await fs.promises.writeFile(tempPath, `${JSON.stringify(value, null, 2)}\n`, {
218
+ mode: 0o600,
219
+ });
220
+ await fs.promises.rename(tempPath, filePath);
221
+ }
222
+
190
223
  async function loadReplayState(api: OpenClawPluginApi): Promise<ReplayStateStore> {
191
224
  const filePath = resolvePluginStateFile(api);
192
- const { value } = await readJsonFileWithFallback<ReplayStateStore>(filePath, { sessions: {} });
225
+ const { value } = await readJsonFileWithFallbackLocal<ReplayStateStore>(filePath, {
226
+ sessions: {},
227
+ });
193
228
  return value;
194
229
  }
195
230
 
196
231
  async function saveReplayState(api: OpenClawPluginApi, state: ReplayStateStore): Promise<void> {
197
232
  const filePath = resolvePluginStateFile(api);
198
- await writeJsonFileAtomically(filePath, state);
233
+ await writeJsonFileAtomicallyLocal(filePath, state);
199
234
  }
200
235
 
201
236
  function buildReplaySessionState(params: {
@@ -225,7 +260,6 @@ function buildReplaySessionState(params: {
225
260
 
226
261
  function describeReplayAvailability(params: {
227
262
  config: RrwebReplayConfig;
228
- extensionDir: string | null;
229
263
  browserRuntimeAvailable: boolean;
230
264
  }): { enabled: boolean; reason?: string } {
231
265
  if (!params.config.enabled) {
@@ -237,12 +271,6 @@ function describeReplayAvailability(params: {
237
271
  reason: LEGACY_BROWSER_RUNTIME_REASON,
238
272
  };
239
273
  }
240
- if (!params.extensionDir) {
241
- return {
242
- enabled: false,
243
- reason: "Replay extension directory is missing. Check rrweb-replay.extensionMode/path.",
244
- };
245
- }
246
274
  const publicKey = resolveConfiguredSecret({
247
275
  value: params.config.publicKey,
248
276
  envVarName: params.config.publicKeyEnvVar,
@@ -313,6 +341,13 @@ function resolveBrowserProfileForReplay(params: {
313
341
  return { name: profileName, driver };
314
342
  }
315
343
 
344
+ function resolveConfiguredPublicKey(config: RrwebReplayConfig): string | undefined {
345
+ return resolveConfiguredSecret({
346
+ value: config.publicKey,
347
+ envVarName: config.publicKeyEnvVar,
348
+ });
349
+ }
350
+
316
351
  async function upsertReplaySession(params: {
317
352
  api: OpenClawPluginApi;
318
353
  config: RrwebReplayConfig;
@@ -425,6 +460,8 @@ async function armReplayContext(params: {
425
460
  replaySessionId: replayEntry.replaySessionId,
426
461
  replayServerUrl: replayEntry.replayServerUrl,
427
462
  replayUrl: replayEntry.replayUrl,
463
+ replayPublicKey: resolveConfiguredPublicKey(params.pluginConfig),
464
+ replayRrwebCdnUrl: DEFAULT_RRWEB_CDN_URL,
428
465
  updatedAt: new Date().toISOString(),
429
466
  });
430
467
  return await upsertReplaySession({
@@ -445,7 +482,6 @@ async function armReplayContext(params: {
445
482
  function createReplaySessionInfoTool(params: {
446
483
  api: OpenClawPluginApi;
447
484
  pluginConfig: RrwebReplayConfig;
448
- extensionDir: string | null;
449
485
  browserRuntimePromise: Promise<BrowserRuntimeCompat | null>;
450
486
  context: {
451
487
  sessionKey?: string;
@@ -476,7 +512,6 @@ function createReplaySessionInfoTool(params: {
476
512
  rawParams && typeof rawParams === "object" ? (rawParams as Record<string, unknown>) : null;
477
513
  const availability = describeReplayAvailability({
478
514
  config: params.pluginConfig,
479
- extensionDir: params.extensionDir,
480
515
  browserRuntimeAvailable: Boolean(browserRuntime),
481
516
  });
482
517
  const activate = toolParams?.activate === true;
@@ -530,7 +565,7 @@ function createReplaySessionInfoTool(params: {
530
565
  };
531
566
  }
532
567
 
533
- export default definePluginEntry({
568
+ const rrwebReplayPlugin = {
534
569
  id: RRWEB_PLUGIN_ID,
535
570
  name: "rrweb Replay",
536
571
  description: "Portable rrweb replay bootstrap for OpenClaw-managed browsers.",
@@ -554,8 +589,8 @@ export default definePluginEntry({
554
589
  return;
555
590
  }
556
591
  if (!extensionDir) {
557
- ctx.logger.warn(
558
- "[rrweb-replay] extension directory missing; replay browser extension was not registered",
592
+ ctx.logger.info(
593
+ "[rrweb-replay] extension directory missing; continuing with runtime-side rrwebcloud upload only",
559
594
  );
560
595
  return;
561
596
  }
@@ -578,7 +613,6 @@ export default definePluginEntry({
578
613
  const browserRuntime = await browserRuntimePromise;
579
614
  const availability = describeReplayAvailability({
580
615
  config: pluginConfig,
581
- extensionDir,
582
616
  browserRuntimeAvailable: Boolean(browserRuntime),
583
617
  });
584
618
  await upsertReplaySession({
@@ -621,7 +655,6 @@ export default definePluginEntry({
621
655
  const browserRuntime = await browserRuntimePromise;
622
656
  const availability = describeReplayAvailability({
623
657
  config: pluginConfig,
624
- extensionDir,
625
658
  browserRuntimeAvailable: Boolean(browserRuntime),
626
659
  });
627
660
  if (event.toolName !== "browser" || pluginConfig.recordingPolicy !== "browser-only") {
@@ -649,7 +682,6 @@ export default definePluginEntry({
649
682
  createReplaySessionInfoTool({
650
683
  api,
651
684
  pluginConfig,
652
- extensionDir,
653
685
  browserRuntimePromise,
654
686
  context: {
655
687
  sessionKey: context.sessionKey,
@@ -659,4 +691,6 @@ export default definePluginEntry({
659
691
  { name: "replay_session_info", optional: true },
660
692
  );
661
693
  },
662
- });
694
+ };
695
+
696
+ 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-3",
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