@riddledc/openclaw-riddledc 0.3.4 → 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 CHANGED
@@ -1,4 +1,4 @@
1
- 3185b3314096550b3b62561d979cedbf819a24a4fea6d001ace2a95c9c6e7f18 dist/index.cjs
1
+ 008b101829a770aab04361a6432304b6fe7edf4173fed1185339d6f53969b418 dist/index.cjs
2
2
  94ce04f0e2d84bf64dd68f0500dfdd2f951287a3deccec87f197261961927f6f dist/index.d.cts
3
3
  94ce04f0e2d84bf64dd68f0500dfdd2f951287a3deccec87f197261961927f6f dist/index.d.ts
4
- 599dba9020d9825d7809b8f0ae6bceb6ccdfefc9a9be247299d13f1b73d84398 dist/index.js
4
+ 52e59ee4fd2c37fd503c9e52add79ef76e5959fdef1d0c5e191b132eba4027db dist/index.js
package/README.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # @riddledc/openclaw-riddledc
2
2
 
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
+
3
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
@@ -57,11 +61,21 @@ Example response:
57
61
 
58
62
  ## Security
59
63
 
60
- - Never hardcode API keys in config files
61
- - Use environment variables or a secret manager
62
- - The plugin only communicates with `api.riddledc.com` over HTTPS
63
- - Hardcoded domain allowlist prevents credential exfiltration
64
- - See [SECURITY.md](./SECURITY.md) for full threat model and data flow
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.
65
79
 
66
80
  ## Reproducible Builds
67
81
 
package/SECURITY.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # Security Model
2
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
+
3
20
  ## Data Flow
4
21
 
5
22
  ```
@@ -33,6 +50,29 @@
33
50
  - Make network requests to arbitrary URLs from your machine
34
51
  - Run code locally (all execution happens on Riddle's servers)
35
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
+
36
76
  ## Security Controls
37
77
 
38
78
  ### 1. Hardcoded Domain Allowlist
@@ -67,9 +107,24 @@ Only requires one secret: `RIDDLE_API_KEY`. No OAuth, no cookies, no session sta
67
107
  |--------|------------|
68
108
  | API key exfiltration to attacker server | Hardcoded domain check blocks all non-riddledc.com requests |
69
109
  | Malicious config injection | Domain check runs at request time, not config time |
70
- | Supply chain attack (npm) | Use npm provenance to verify package origin |
71
- | Build tampering | Checksums + reproducible builds |
110
+ | Supply chain attack (npm) | npm provenance + checksums + reproducible builds |
111
+ | Build tampering | CHECKSUMS.txt with SHA256 hashes |
72
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.
73
128
 
74
129
  ## Reporting Security Issues
75
130
 
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.4",
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.4",
3
+ "version": "0.4.0",
4
4
  "description": "OpenClaw integration package for RiddleDC (no secrets).",
5
5
  "license": "MIT",
6
6
  "author": "RiddleDC",
@@ -24,7 +24,8 @@
24
24
  "dist",
25
25
  "openclaw.plugin.json",
26
26
  "CHECKSUMS.txt",
27
- "SECURITY.md"
27
+ "SECURITY.md",
28
+ "LICENSE"
28
29
  ],
29
30
  "sideEffects": false,
30
31
  "engines": {