@riddledc/openclaw-riddledc 0.3.3 → 0.4.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/CHECKSUMS.txt ADDED
@@ -0,0 +1,4 @@
1
+ 008b101829a770aab04361a6432304b6fe7edf4173fed1185339d6f53969b418 dist/index.cjs
2
+ 94ce04f0e2d84bf64dd68f0500dfdd2f951287a3deccec87f197261961927f6f dist/index.d.cts
3
+ 94ce04f0e2d84bf64dd68f0500dfdd2f951287a3deccec87f197261961927f6f dist/index.d.ts
4
+ 52e59ee4fd2c37fd503c9e52add79ef76e5959fdef1d0c5e191b132eba4027db dist/index.js
package/README.md CHANGED
@@ -1,34 +1,92 @@
1
1
  # @riddledc/openclaw-riddledc
2
2
 
3
- OpenClaw integration package for RiddleDC. No secrets. No assumption about MCP.
3
+ [![npm version](https://img.shields.io/npm/v/@riddledc/openclaw-riddledc.svg)](https://www.npmjs.com/package/@riddledc/openclaw-riddledc)
4
+ [![Build Status](https://github.com/riddledc/integrations/actions/workflows/release.yml/badge.svg)](https://github.com/riddledc/integrations/actions)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+
7
+ OpenClaw plugin for [Riddle](https://riddledc.com) - hosted browser automation API. Take screenshots, run Playwright scripts, and automate web interactions from your OpenClaw agent.
4
8
 
5
9
  ## Install
6
10
 
7
- ```
11
+ ```bash
12
+ # 1. Install the plugin
8
13
  openclaw plugins install @riddledc/openclaw-riddledc
9
- openclaw plugins enable openclaw-riddledc
14
+
15
+ # 2. Add to allowlist and enable
16
+ openclaw config set plugins.allow --json '["discord","telegram","memory-core","openclaw-riddledc"]'
17
+ openclaw config set tools.alsoAllow --json '["openclaw-riddledc"]'
18
+
19
+ # 3. Set your API key
20
+ openclaw config set plugins.entries.openclaw-riddledc.config.apiKey "YOUR_RIDDLE_API_KEY"
21
+
22
+ # 4. Restart gateway
23
+ openclaw gateway restart
24
+ # Or if using systemd: systemctl restart openclaw-gateway
10
25
  ```
11
26
 
12
- ## OpenClaw plugin metadata
27
+ Get your API key at [riddledc.com](https://riddledc.com).
13
28
 
14
- This package ships `openclaw.plugin.json` for OpenClaw registration.
29
+ ## Tools
15
30
 
16
- The plugin id is `openclaw-riddledc`.
31
+ | Tool | Description |
32
+ |------|-------------|
33
+ | `riddle_screenshot` | Take a screenshot of a single URL |
34
+ | `riddle_screenshots` | Take screenshots of multiple URLs in one job |
35
+ | `riddle_steps` | Run a workflow using steps (goto/click/fill/etc.) |
36
+ | `riddle_script` | Run full Playwright code |
37
+ | `riddle_run` | Low-level pass-through to the Riddle API |
17
38
 
18
- ## Configuration
39
+ All tools return screenshots + console logs by default. Pass `include: ["har"]` to also capture network traffic.
19
40
 
20
- The plugin accepts:
21
- - `apiKey` (or `RIDDLE_API_KEY` env var)
22
- - `baseUrl` (defaults to `https://api.riddledc.com`)
41
+ ## How It Works
23
42
 
24
- ## Tools
43
+ Screenshots are automatically saved to `~/.openclaw/workspace/riddle/screenshots/` and the tool returns a file reference instead of inline base64. This keeps agent context small and prevents token overflow.
44
+
45
+ Example response:
46
+ ```json
47
+ {
48
+ "ok": true,
49
+ "job_id": "job_abc123",
50
+ "screenshot": { "saved": "riddle/screenshots/job_abc123.png", "sizeBytes": 45000 },
51
+ "console": []
52
+ }
53
+ ```
54
+
55
+ ## Configuration
25
56
 
26
- - `riddle_run`
27
- - `riddle_screenshot`
28
- - `riddle_screenshots`
29
- - `riddle_steps`
30
- - `riddle_script`
57
+ | Option | Description |
58
+ |--------|-------------|
59
+ | `apiKey` | Your Riddle API key (or set `RIDDLE_API_KEY` env var) |
60
+ | `baseUrl` | API endpoint (defaults to `https://api.riddledc.com`) |
31
61
 
32
62
  ## Security
33
63
 
34
- Do not hardcode keys. Provide credentials via env vars or your secret manager.
64
+ - **Capability manifest**: See `openclaw.plugin.json` for declared permissions
65
+ - **Network**: Only communicates with `api.riddledc.com` (hardcoded allowlist)
66
+ - **Context**: No access to conversation history, other tools, or user profile
67
+ - **Filesystem**: Only writes to `~/.openclaw/workspace/riddle/`
68
+ - **Secrets**: Only requires `RIDDLE_API_KEY` (use env var, not config file)
69
+
70
+ For defense in depth, run your agent with sandboxing:
71
+
72
+ ```yaml
73
+ agents:
74
+ defaults:
75
+ sandbox: true
76
+ ```
77
+
78
+ See [SECURITY.md](./SECURITY.md) for full threat model, data flow diagram, and capability details.
79
+
80
+ ## Reproducible Builds
81
+
82
+ To verify a build matches the published package:
83
+
84
+ 1. Clone the repo at the tagged version
85
+ 2. Run: `pnpm install && pnpm build`
86
+ 3. Compare checksums: `shasum -a 256 dist/*`
87
+
88
+ Expected checksums are in `CHECKSUMS.txt`.
89
+
90
+ ## License
91
+
92
+ MIT
package/SECURITY.md ADDED
@@ -0,0 +1,131 @@
1
+ # Security Model
2
+
3
+ ## Plugin vs Skill: What This Is
4
+
5
+ This is a **plugin** (code), not a **skill** (prompt). Understanding the difference matters for trust:
6
+
7
+ | Type | What it is | Trust implications |
8
+ |------|------------|-------------------|
9
+ | **Plugin** | Node.js code that runs in-process with OpenClaw | Has full process privileges; can access env vars, filesystem, network |
10
+ | **Skill** | Markdown/prompt instructions that guide the LLM | No direct system access; influences agent via tool invocation |
11
+
12
+ **This package is a plugin.** It runs as code inside the OpenClaw process. That means:
13
+
14
+ - It *could* read any env var, file, or make any network call (plugins are trusted code)
15
+ - We *choose* to constrain ourselves via hardcoded allowlists and explicit capability limits
16
+ - You should audit the source or trust the npm provenance
17
+
18
+ **This plugin does NOT bundle any skills.** It only provides tools. No prompt instructions are injected into your agent's context.
19
+
20
+ ## Data Flow
21
+
22
+ ```
23
+ ┌─────────────────┐ HTTPS only ┌──────────────────┐
24
+ │ OpenClaw Agent │ ──────────────────► │ api.riddledc.com │
25
+ │ │ POST /v1/run │ │
26
+ │ - RIDDLE_API_KEY │ - Runs browser │
27
+ │ - User prompts │ ◄────────────────── │ - Returns data │
28
+ └─────────────────┘ JSON response └──────────────────┘
29
+
30
+
31
+ ┌─────────────────┐
32
+ │ Local Workspace │
33
+ │ ~/.openclaw/ │
34
+ │ workspace/riddle│ Screenshots saved
35
+ │ /screenshots/ │ as files (not inline)
36
+ └─────────────────┘
37
+ ```
38
+
39
+ ## What This Plugin CAN Do
40
+
41
+ - Send requests to api.riddledc.com (hardcoded, cannot be changed)
42
+ - Read your RIDDLE_API_KEY from config or environment
43
+ - Write screenshot/HAR files to your workspace directory
44
+ - Execute Playwright scripts on Riddle's remote browser
45
+
46
+ ## What This Plugin CANNOT Do
47
+
48
+ - Send your API key to any other domain (blocked by `assertAllowedBaseUrl`)
49
+ - Access your filesystem outside the workspace directory
50
+ - Make network requests to arbitrary URLs from your machine
51
+ - Run code locally (all execution happens on Riddle's servers)
52
+
53
+ ## Agent Context Access
54
+
55
+ This plugin has **no access** to:
56
+
57
+ | Context | Access |
58
+ |---------|--------|
59
+ | Conversation history | ❌ None |
60
+ | Other tools' outputs | ❌ None |
61
+ | User profile / preferences | ❌ None |
62
+ | Other plugins' data | ❌ None |
63
+ | System environment (except RIDDLE_API_KEY) | ❌ None |
64
+
65
+ The plugin only sees data explicitly passed to its tools by the agent. It does not hook into message events, read logs, or access the agent's memory/context.
66
+
67
+ ## Capability Manifest
68
+
69
+ This plugin declares its capabilities in `openclaw.plugin.json`. Key constraints:
70
+
71
+ - **Network egress**: Only `api.riddledc.com` (hardcoded, enforced at runtime)
72
+ - **Filesystem**: Write only to `~/.openclaw/workspace/riddle/`
73
+ - **Tools**: Provides 5 tools; invokes no other agent tools
74
+ - **Secrets**: Only `RIDDLE_API_KEY` required
75
+
76
+ ## Security Controls
77
+
78
+ ### 1. Hardcoded Domain Allowlist
79
+
80
+ ```typescript
81
+ function assertAllowedBaseUrl(baseUrl: string) {
82
+ const url = new URL(baseUrl);
83
+ if (url.protocol !== "https:")
84
+ throw new Error(`Riddle baseUrl must be https`);
85
+ if (url.hostname !== "api.riddledc.com")
86
+ throw new Error(`Refusing to use non-official Riddle host`);
87
+ }
88
+ ```
89
+
90
+ This runs on EVERY request. Even if config is manipulated, keys never leave riddledc.com.
91
+
92
+ ### 2. No Inline Base64
93
+
94
+ Screenshots are saved to disk, not returned inline. This prevents:
95
+
96
+ - Context overflow attacks
97
+ - Memory exhaustion
98
+ - Accidental key leakage in logs
99
+
100
+ ### 3. Minimal Permissions
101
+
102
+ Only requires one secret: `RIDDLE_API_KEY`. No OAuth, no cookies, no session state.
103
+
104
+ ## Threat Model
105
+
106
+ | Threat | Mitigation |
107
+ |--------|------------|
108
+ | API key exfiltration to attacker server | Hardcoded domain check blocks all non-riddledc.com requests |
109
+ | Malicious config injection | Domain check runs at request time, not config time |
110
+ | Supply chain attack (npm) | npm provenance + checksums + reproducible builds |
111
+ | Build tampering | CHECKSUMS.txt with SHA256 hashes |
112
+ | Local file access | Plugin only writes to designated workspace subdirectory |
113
+ | Context/conversation leakage | Plugin has no access to agent context (see above) |
114
+ | Prompt injection via tool output | Screenshots saved as file refs, not inline content |
115
+
116
+ ## Recommended: Run in Sandbox
117
+
118
+ For defense in depth, consider running your agent with sandboxing enabled:
119
+
120
+ ```yaml
121
+ # In your OpenClaw config
122
+ agents:
123
+ defaults:
124
+ sandbox: true
125
+ ```
126
+
127
+ This runs tools like `exec` in a Docker container, limiting blast radius if any plugin or skill misbehaves. While this plugin doesn't require sandboxing (it only calls a remote API), sandboxing protects against other plugins or prompt injection attacks.
128
+
129
+ ## Reporting Security Issues
130
+
131
+ Email: security@riddledc.com
package/dist/index.cjs CHANGED
@@ -221,10 +221,20 @@ function register(api) {
221
221
  api.registerTool(
222
222
  {
223
223
  name: "riddle_screenshot",
224
- description: 'Riddle: take a screenshot of a single URL. Returns screenshot + console by default; pass include:["har"] to opt in to HAR capture.',
224
+ description: 'Riddle: take a screenshot of a single URL. Supports authenticated screenshots via cookies/localStorage. Returns screenshot + console by default; pass include:["har"] to opt in to HAR capture.',
225
225
  parameters: import_typebox.Type.Object({
226
226
  url: import_typebox.Type.String(),
227
227
  timeout_sec: import_typebox.Type.Optional(import_typebox.Type.Number()),
228
+ cookies: import_typebox.Type.Optional(import_typebox.Type.Array(import_typebox.Type.Object({
229
+ name: import_typebox.Type.String(),
230
+ value: import_typebox.Type.String(),
231
+ domain: import_typebox.Type.String(),
232
+ path: import_typebox.Type.Optional(import_typebox.Type.String()),
233
+ secure: import_typebox.Type.Optional(import_typebox.Type.Boolean()),
234
+ httpOnly: import_typebox.Type.Optional(import_typebox.Type.Boolean())
235
+ }), { description: "Cookies to inject for authenticated sessions" })),
236
+ localStorage: import_typebox.Type.Optional(import_typebox.Type.Record(import_typebox.Type.String(), import_typebox.Type.String(), { description: "localStorage key-value pairs to inject (e.g., JWT tokens)" })),
237
+ headers: import_typebox.Type.Optional(import_typebox.Type.Record(import_typebox.Type.String(), import_typebox.Type.String(), { description: "HTTP headers to send with requests" })),
228
238
  options: import_typebox.Type.Optional(import_typebox.Type.Record(import_typebox.Type.String(), import_typebox.Type.Any())),
229
239
  include: import_typebox.Type.Optional(import_typebox.Type.Array(import_typebox.Type.String())),
230
240
  harInline: import_typebox.Type.Optional(import_typebox.Type.Boolean())
@@ -233,7 +243,11 @@ function register(api) {
233
243
  if (!params.url || typeof params.url !== "string") throw new Error("url must be a string");
234
244
  const payload = { url: params.url };
235
245
  if (params.timeout_sec) payload.timeout_sec = params.timeout_sec;
236
- if (params.options) payload.options = params.options;
246
+ const opts = { ...params.options || {} };
247
+ if (params.cookies) opts.cookies = params.cookies;
248
+ if (params.localStorage) opts.localStorage = params.localStorage;
249
+ if (params.headers) opts.headers = params.headers;
250
+ if (Object.keys(opts).length > 0) payload.options = opts;
237
251
  if (params.include) payload.include = params.include;
238
252
  if (params.harInline) payload.harInline = params.harInline;
239
253
  const result = await runWithDefaults(api, payload, { include: ["screenshot", "console"] });
@@ -245,10 +259,20 @@ function register(api) {
245
259
  api.registerTool(
246
260
  {
247
261
  name: "riddle_screenshots",
248
- description: 'Riddle: take screenshots for multiple URLs in one job. Returns screenshots + console by default; pass include:["har"] to opt in to HAR capture.',
262
+ description: 'Riddle: take screenshots for multiple URLs in one job. Supports authenticated sessions via cookies/localStorage (shared across all URLs). Returns screenshots + console by default; pass include:["har"] to opt in to HAR capture.',
249
263
  parameters: import_typebox.Type.Object({
250
264
  urls: import_typebox.Type.Array(import_typebox.Type.String()),
251
265
  timeout_sec: import_typebox.Type.Optional(import_typebox.Type.Number()),
266
+ cookies: import_typebox.Type.Optional(import_typebox.Type.Array(import_typebox.Type.Object({
267
+ name: import_typebox.Type.String(),
268
+ value: import_typebox.Type.String(),
269
+ domain: import_typebox.Type.String(),
270
+ path: import_typebox.Type.Optional(import_typebox.Type.String()),
271
+ secure: import_typebox.Type.Optional(import_typebox.Type.Boolean()),
272
+ httpOnly: import_typebox.Type.Optional(import_typebox.Type.Boolean())
273
+ }), { description: "Cookies to inject for authenticated sessions" })),
274
+ localStorage: import_typebox.Type.Optional(import_typebox.Type.Record(import_typebox.Type.String(), import_typebox.Type.String(), { description: "localStorage key-value pairs to inject (e.g., JWT tokens)" })),
275
+ headers: import_typebox.Type.Optional(import_typebox.Type.Record(import_typebox.Type.String(), import_typebox.Type.String(), { description: "HTTP headers to send with requests" })),
252
276
  options: import_typebox.Type.Optional(import_typebox.Type.Record(import_typebox.Type.String(), import_typebox.Type.Any())),
253
277
  include: import_typebox.Type.Optional(import_typebox.Type.Array(import_typebox.Type.String())),
254
278
  harInline: import_typebox.Type.Optional(import_typebox.Type.Boolean())
@@ -259,7 +283,11 @@ function register(api) {
259
283
  }
260
284
  const payload = { urls: params.urls };
261
285
  if (params.timeout_sec) payload.timeout_sec = params.timeout_sec;
262
- if (params.options) payload.options = params.options;
286
+ const opts = { ...params.options || {} };
287
+ if (params.cookies) opts.cookies = params.cookies;
288
+ if (params.localStorage) opts.localStorage = params.localStorage;
289
+ if (params.headers) opts.headers = params.headers;
290
+ if (Object.keys(opts).length > 0) payload.options = opts;
263
291
  if (params.include) payload.include = params.include;
264
292
  if (params.harInline) payload.harInline = params.harInline;
265
293
  const result = await runWithDefaults(api, payload, { include: ["screenshot", "console"] });
@@ -271,10 +299,20 @@ function register(api) {
271
299
  api.registerTool(
272
300
  {
273
301
  name: "riddle_steps",
274
- description: 'Riddle: run a workflow in steps mode (goto/click/fill/etc.). Returns screenshot + console by default; pass include:["har"] to opt in to HAR capture.',
302
+ description: 'Riddle: run a workflow in steps mode (goto/click/fill/etc.). Supports authenticated sessions via cookies/localStorage. Returns screenshot + console by default; pass include:["har"] to opt in to HAR capture.',
275
303
  parameters: import_typebox.Type.Object({
276
304
  steps: import_typebox.Type.Array(import_typebox.Type.Record(import_typebox.Type.String(), import_typebox.Type.Any())),
277
305
  timeout_sec: import_typebox.Type.Optional(import_typebox.Type.Number()),
306
+ cookies: import_typebox.Type.Optional(import_typebox.Type.Array(import_typebox.Type.Object({
307
+ name: import_typebox.Type.String(),
308
+ value: import_typebox.Type.String(),
309
+ domain: import_typebox.Type.String(),
310
+ path: import_typebox.Type.Optional(import_typebox.Type.String()),
311
+ secure: import_typebox.Type.Optional(import_typebox.Type.Boolean()),
312
+ httpOnly: import_typebox.Type.Optional(import_typebox.Type.Boolean())
313
+ }), { description: "Cookies to inject for authenticated sessions" })),
314
+ localStorage: import_typebox.Type.Optional(import_typebox.Type.Record(import_typebox.Type.String(), import_typebox.Type.String(), { description: "localStorage key-value pairs to inject (e.g., JWT tokens)" })),
315
+ headers: import_typebox.Type.Optional(import_typebox.Type.Record(import_typebox.Type.String(), import_typebox.Type.String(), { description: "HTTP headers to send with requests" })),
278
316
  options: import_typebox.Type.Optional(import_typebox.Type.Record(import_typebox.Type.String(), import_typebox.Type.Any())),
279
317
  include: import_typebox.Type.Optional(import_typebox.Type.Array(import_typebox.Type.String())),
280
318
  harInline: import_typebox.Type.Optional(import_typebox.Type.Boolean()),
@@ -285,7 +323,11 @@ function register(api) {
285
323
  const payload = { steps: params.steps };
286
324
  if (typeof params.sync === "boolean") payload.sync = params.sync;
287
325
  if (params.timeout_sec) payload.timeout_sec = params.timeout_sec;
288
- if (params.options) payload.options = params.options;
326
+ const opts = { ...params.options || {} };
327
+ if (params.cookies) opts.cookies = params.cookies;
328
+ if (params.localStorage) opts.localStorage = params.localStorage;
329
+ if (params.headers) opts.headers = params.headers;
330
+ if (Object.keys(opts).length > 0) payload.options = opts;
289
331
  if (params.include) payload.include = params.include;
290
332
  if (params.harInline) payload.harInline = params.harInline;
291
333
  const result = await runWithDefaults(api, payload, { include: ["screenshot", "console", "result"] });
@@ -297,10 +339,20 @@ function register(api) {
297
339
  api.registerTool(
298
340
  {
299
341
  name: "riddle_script",
300
- description: 'Riddle: run full Playwright code (script mode). Returns screenshot + console by default; pass include:["har"] to opt in to HAR capture.',
342
+ description: 'Riddle: run full Playwright code (script mode). Supports authenticated sessions via cookies/localStorage. In scripts, use `await injectLocalStorage()` after navigating to the origin to apply localStorage values. Returns screenshot + console by default; pass include:["har"] to opt in to HAR capture.',
301
343
  parameters: import_typebox.Type.Object({
302
344
  script: import_typebox.Type.String(),
303
345
  timeout_sec: import_typebox.Type.Optional(import_typebox.Type.Number()),
346
+ cookies: import_typebox.Type.Optional(import_typebox.Type.Array(import_typebox.Type.Object({
347
+ name: import_typebox.Type.String(),
348
+ value: import_typebox.Type.String(),
349
+ domain: import_typebox.Type.String(),
350
+ path: import_typebox.Type.Optional(import_typebox.Type.String()),
351
+ secure: import_typebox.Type.Optional(import_typebox.Type.Boolean()),
352
+ httpOnly: import_typebox.Type.Optional(import_typebox.Type.Boolean())
353
+ }), { description: "Cookies to inject for authenticated sessions" })),
354
+ localStorage: import_typebox.Type.Optional(import_typebox.Type.Record(import_typebox.Type.String(), import_typebox.Type.String(), { description: "localStorage key-value pairs; use injectLocalStorage() in script after goto to apply" })),
355
+ headers: import_typebox.Type.Optional(import_typebox.Type.Record(import_typebox.Type.String(), import_typebox.Type.String(), { description: "HTTP headers to send with requests" })),
304
356
  options: import_typebox.Type.Optional(import_typebox.Type.Record(import_typebox.Type.String(), import_typebox.Type.Any())),
305
357
  include: import_typebox.Type.Optional(import_typebox.Type.Array(import_typebox.Type.String())),
306
358
  harInline: import_typebox.Type.Optional(import_typebox.Type.Boolean()),
@@ -311,7 +363,11 @@ function register(api) {
311
363
  const payload = { script: params.script };
312
364
  if (typeof params.sync === "boolean") payload.sync = params.sync;
313
365
  if (params.timeout_sec) payload.timeout_sec = params.timeout_sec;
314
- if (params.options) payload.options = params.options;
366
+ const opts = { ...params.options || {} };
367
+ if (params.cookies) opts.cookies = params.cookies;
368
+ if (params.localStorage) opts.localStorage = params.localStorage;
369
+ if (params.headers) opts.headers = params.headers;
370
+ if (Object.keys(opts).length > 0) payload.options = opts;
315
371
  if (params.include) payload.include = params.include;
316
372
  if (params.harInline) payload.harInline = params.harInline;
317
373
  const result = await runWithDefaults(api, payload, { include: ["screenshot", "console", "result"] });
package/dist/index.js CHANGED
@@ -197,10 +197,20 @@ function register(api) {
197
197
  api.registerTool(
198
198
  {
199
199
  name: "riddle_screenshot",
200
- description: 'Riddle: take a screenshot of a single URL. Returns screenshot + console by default; pass include:["har"] to opt in to HAR capture.',
200
+ description: 'Riddle: take a screenshot of a single URL. Supports authenticated screenshots via cookies/localStorage. Returns screenshot + console by default; pass include:["har"] to opt in to HAR capture.',
201
201
  parameters: Type.Object({
202
202
  url: Type.String(),
203
203
  timeout_sec: Type.Optional(Type.Number()),
204
+ cookies: Type.Optional(Type.Array(Type.Object({
205
+ name: Type.String(),
206
+ value: Type.String(),
207
+ domain: Type.String(),
208
+ path: Type.Optional(Type.String()),
209
+ secure: Type.Optional(Type.Boolean()),
210
+ httpOnly: Type.Optional(Type.Boolean())
211
+ }), { description: "Cookies to inject for authenticated sessions" })),
212
+ localStorage: Type.Optional(Type.Record(Type.String(), Type.String(), { description: "localStorage key-value pairs to inject (e.g., JWT tokens)" })),
213
+ headers: Type.Optional(Type.Record(Type.String(), Type.String(), { description: "HTTP headers to send with requests" })),
204
214
  options: Type.Optional(Type.Record(Type.String(), Type.Any())),
205
215
  include: Type.Optional(Type.Array(Type.String())),
206
216
  harInline: Type.Optional(Type.Boolean())
@@ -209,7 +219,11 @@ function register(api) {
209
219
  if (!params.url || typeof params.url !== "string") throw new Error("url must be a string");
210
220
  const payload = { url: params.url };
211
221
  if (params.timeout_sec) payload.timeout_sec = params.timeout_sec;
212
- if (params.options) payload.options = params.options;
222
+ const opts = { ...params.options || {} };
223
+ if (params.cookies) opts.cookies = params.cookies;
224
+ if (params.localStorage) opts.localStorage = params.localStorage;
225
+ if (params.headers) opts.headers = params.headers;
226
+ if (Object.keys(opts).length > 0) payload.options = opts;
213
227
  if (params.include) payload.include = params.include;
214
228
  if (params.harInline) payload.harInline = params.harInline;
215
229
  const result = await runWithDefaults(api, payload, { include: ["screenshot", "console"] });
@@ -221,10 +235,20 @@ function register(api) {
221
235
  api.registerTool(
222
236
  {
223
237
  name: "riddle_screenshots",
224
- description: 'Riddle: take screenshots for multiple URLs in one job. Returns screenshots + console by default; pass include:["har"] to opt in to HAR capture.',
238
+ description: 'Riddle: take screenshots for multiple URLs in one job. Supports authenticated sessions via cookies/localStorage (shared across all URLs). Returns screenshots + console by default; pass include:["har"] to opt in to HAR capture.',
225
239
  parameters: Type.Object({
226
240
  urls: Type.Array(Type.String()),
227
241
  timeout_sec: Type.Optional(Type.Number()),
242
+ cookies: Type.Optional(Type.Array(Type.Object({
243
+ name: Type.String(),
244
+ value: Type.String(),
245
+ domain: Type.String(),
246
+ path: Type.Optional(Type.String()),
247
+ secure: Type.Optional(Type.Boolean()),
248
+ httpOnly: Type.Optional(Type.Boolean())
249
+ }), { description: "Cookies to inject for authenticated sessions" })),
250
+ localStorage: Type.Optional(Type.Record(Type.String(), Type.String(), { description: "localStorage key-value pairs to inject (e.g., JWT tokens)" })),
251
+ headers: Type.Optional(Type.Record(Type.String(), Type.String(), { description: "HTTP headers to send with requests" })),
228
252
  options: Type.Optional(Type.Record(Type.String(), Type.Any())),
229
253
  include: Type.Optional(Type.Array(Type.String())),
230
254
  harInline: Type.Optional(Type.Boolean())
@@ -235,7 +259,11 @@ function register(api) {
235
259
  }
236
260
  const payload = { urls: params.urls };
237
261
  if (params.timeout_sec) payload.timeout_sec = params.timeout_sec;
238
- if (params.options) payload.options = params.options;
262
+ const opts = { ...params.options || {} };
263
+ if (params.cookies) opts.cookies = params.cookies;
264
+ if (params.localStorage) opts.localStorage = params.localStorage;
265
+ if (params.headers) opts.headers = params.headers;
266
+ if (Object.keys(opts).length > 0) payload.options = opts;
239
267
  if (params.include) payload.include = params.include;
240
268
  if (params.harInline) payload.harInline = params.harInline;
241
269
  const result = await runWithDefaults(api, payload, { include: ["screenshot", "console"] });
@@ -247,10 +275,20 @@ function register(api) {
247
275
  api.registerTool(
248
276
  {
249
277
  name: "riddle_steps",
250
- description: 'Riddle: run a workflow in steps mode (goto/click/fill/etc.). Returns screenshot + console by default; pass include:["har"] to opt in to HAR capture.',
278
+ description: 'Riddle: run a workflow in steps mode (goto/click/fill/etc.). Supports authenticated sessions via cookies/localStorage. Returns screenshot + console by default; pass include:["har"] to opt in to HAR capture.',
251
279
  parameters: Type.Object({
252
280
  steps: Type.Array(Type.Record(Type.String(), Type.Any())),
253
281
  timeout_sec: Type.Optional(Type.Number()),
282
+ cookies: Type.Optional(Type.Array(Type.Object({
283
+ name: Type.String(),
284
+ value: Type.String(),
285
+ domain: Type.String(),
286
+ path: Type.Optional(Type.String()),
287
+ secure: Type.Optional(Type.Boolean()),
288
+ httpOnly: Type.Optional(Type.Boolean())
289
+ }), { description: "Cookies to inject for authenticated sessions" })),
290
+ localStorage: Type.Optional(Type.Record(Type.String(), Type.String(), { description: "localStorage key-value pairs to inject (e.g., JWT tokens)" })),
291
+ headers: Type.Optional(Type.Record(Type.String(), Type.String(), { description: "HTTP headers to send with requests" })),
254
292
  options: Type.Optional(Type.Record(Type.String(), Type.Any())),
255
293
  include: Type.Optional(Type.Array(Type.String())),
256
294
  harInline: Type.Optional(Type.Boolean()),
@@ -261,7 +299,11 @@ function register(api) {
261
299
  const payload = { steps: params.steps };
262
300
  if (typeof params.sync === "boolean") payload.sync = params.sync;
263
301
  if (params.timeout_sec) payload.timeout_sec = params.timeout_sec;
264
- if (params.options) payload.options = params.options;
302
+ const opts = { ...params.options || {} };
303
+ if (params.cookies) opts.cookies = params.cookies;
304
+ if (params.localStorage) opts.localStorage = params.localStorage;
305
+ if (params.headers) opts.headers = params.headers;
306
+ if (Object.keys(opts).length > 0) payload.options = opts;
265
307
  if (params.include) payload.include = params.include;
266
308
  if (params.harInline) payload.harInline = params.harInline;
267
309
  const result = await runWithDefaults(api, payload, { include: ["screenshot", "console", "result"] });
@@ -273,10 +315,20 @@ function register(api) {
273
315
  api.registerTool(
274
316
  {
275
317
  name: "riddle_script",
276
- description: 'Riddle: run full Playwright code (script mode). Returns screenshot + console by default; pass include:["har"] to opt in to HAR capture.',
318
+ description: 'Riddle: run full Playwright code (script mode). Supports authenticated sessions via cookies/localStorage. In scripts, use `await injectLocalStorage()` after navigating to the origin to apply localStorage values. Returns screenshot + console by default; pass include:["har"] to opt in to HAR capture.',
277
319
  parameters: Type.Object({
278
320
  script: Type.String(),
279
321
  timeout_sec: Type.Optional(Type.Number()),
322
+ cookies: Type.Optional(Type.Array(Type.Object({
323
+ name: Type.String(),
324
+ value: Type.String(),
325
+ domain: Type.String(),
326
+ path: Type.Optional(Type.String()),
327
+ secure: Type.Optional(Type.Boolean()),
328
+ httpOnly: Type.Optional(Type.Boolean())
329
+ }), { description: "Cookies to inject for authenticated sessions" })),
330
+ localStorage: Type.Optional(Type.Record(Type.String(), Type.String(), { description: "localStorage key-value pairs; use injectLocalStorage() in script after goto to apply" })),
331
+ headers: Type.Optional(Type.Record(Type.String(), Type.String(), { description: "HTTP headers to send with requests" })),
280
332
  options: Type.Optional(Type.Record(Type.String(), Type.Any())),
281
333
  include: Type.Optional(Type.Array(Type.String())),
282
334
  harInline: Type.Optional(Type.Boolean()),
@@ -287,7 +339,11 @@ function register(api) {
287
339
  const payload = { script: params.script };
288
340
  if (typeof params.sync === "boolean") payload.sync = params.sync;
289
341
  if (params.timeout_sec) payload.timeout_sec = params.timeout_sec;
290
- if (params.options) payload.options = params.options;
342
+ const opts = { ...params.options || {} };
343
+ if (params.cookies) opts.cookies = params.cookies;
344
+ if (params.localStorage) opts.localStorage = params.localStorage;
345
+ if (params.headers) opts.headers = params.headers;
346
+ if (Object.keys(opts).length > 0) payload.options = opts;
291
347
  if (params.include) payload.include = params.include;
292
348
  if (params.harInline) payload.harInline = params.harInline;
293
349
  const result = await runWithDefaults(api, payload, { include: ["screenshot", "console", "result"] });
@@ -2,8 +2,49 @@
2
2
  "id": "openclaw-riddledc",
3
3
  "name": "Riddle",
4
4
  "description": "Riddle (riddledc.com) hosted browser API tools for OpenClaw agents.",
5
- "version": "0.3.3",
6
- "notes": "0.3.1: Screenshots now saved to workspace files instead of inline base64 to prevent context bloat.",
5
+ "version": "0.4.0",
6
+ "notes": "0.3.4: Added capability manifest, npm provenance, checksums, SECURITY.md.",
7
+ "type": "plugin",
8
+ "bundledSkills": [],
9
+ "capabilities": {
10
+ "network": {
11
+ "egress": [
12
+ "api.riddledc.com"
13
+ ],
14
+ "enforced": true,
15
+ "note": "Hardcoded allowlist in assertAllowedBaseUrl() - cannot be overridden by config"
16
+ },
17
+ "filesystem": {
18
+ "write": [
19
+ "~/.openclaw/workspace/riddle/"
20
+ ],
21
+ "read": []
22
+ },
23
+ "agentContext": {
24
+ "conversationHistory": false,
25
+ "otherToolOutputs": false,
26
+ "userProfile": false,
27
+ "note": "Plugin only sees data explicitly passed to its tools"
28
+ },
29
+ "tools": {
30
+ "provides": [
31
+ "riddle_screenshot",
32
+ "riddle_screenshots",
33
+ "riddle_steps",
34
+ "riddle_script",
35
+ "riddle_run"
36
+ ],
37
+ "invokes": [],
38
+ "note": "Provides tools for agent use; does not invoke other agent tools"
39
+ },
40
+ "secrets": {
41
+ "required": [
42
+ "RIDDLE_API_KEY"
43
+ ],
44
+ "optional": [],
45
+ "note": "Key only sent to api.riddledc.com (enforced)"
46
+ }
47
+ },
7
48
  "configSchema": {
8
49
  "type": "object",
9
50
  "additionalProperties": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riddledc/openclaw-riddledc",
3
- "version": "0.3.3",
3
+ "version": "0.4.0",
4
4
  "description": "OpenClaw integration package for RiddleDC (no secrets).",
5
5
  "license": "MIT",
6
6
  "author": "RiddleDC",
@@ -22,7 +22,10 @@
22
22
  },
23
23
  "files": [
24
24
  "dist",
25
- "openclaw.plugin.json"
25
+ "openclaw.plugin.json",
26
+ "CHECKSUMS.txt",
27
+ "SECURITY.md",
28
+ "LICENSE"
26
29
  ],
27
30
  "sideEffects": false,
28
31
  "engines": {
@@ -42,10 +45,11 @@
42
45
  "typescript": "^5.4.5"
43
46
  },
44
47
  "scripts": {
45
- "build": "tsup src/index.ts --format cjs,esm --dts --out-dir dist",
48
+ "build": "npm run sync:openclaw-plugin-version && tsup src/index.ts --format cjs,esm --dts --out-dir dist",
46
49
  "clean": "rm -rf dist",
47
50
  "lint": "echo 'lint: (not configured)'",
48
51
  "test": "echo 'test: (not configured)'",
49
- "sync:openclaw-plugin-version": "node scripts/sync-openclaw-plugin-version.mjs"
52
+ "sync:openclaw-plugin-version": "node scripts/sync-openclaw-plugin-version.mjs",
53
+ "checksums": "node scripts/generate-checksums.mjs"
50
54
  }
51
55
  }