113 lines
4.4 KiB
Markdown
113 lines
4.4 KiB
Markdown
# RunUserScript
|
|
|
|
Executes a user-authored Playwright browser-macro from the caller's
|
|
`browser-macros/` folder.
|
|
|
|
> **Retired (2026-06):** plain-Node `scripts/` (the old `kind: 'script'`) and
|
|
> `templates/` were removed. Keep reusable procedures/boilerplate in **Skills**,
|
|
> and run ad-hoc code (Node, Python, …) with the **Bash** tool. Passing
|
|
> `kind: 'script'` now returns an error pointing at those replacements.
|
|
|
|
| directory | runtime | signature | use case |
|
|
|-----------|---------|-----------|----------|
|
|
| `browser-macros/` | Playwright — Chromium | `main({ context, params })` | Web automation with a live browser session |
|
|
|
|
## Input
|
|
|
|
```ts
|
|
{
|
|
name: string, // filename — '.js' is appended if absent
|
|
params?: Record<string, unknown>, // runtime values matching the macro's param spec
|
|
}
|
|
```
|
|
|
|
## Param validation
|
|
|
|
Params are validated against the `params:` block in the macro's YAML frontmatter:
|
|
- Extra params not listed in the spec → error containing "param"
|
|
- Wrong type for a declared param → error containing "param"
|
|
- Missing required param (no default) → error containing "param"
|
|
- Params with defaults are filled in automatically when not supplied
|
|
|
|
On any param error the tool returns `isError: true` immediately — no subprocess is spawned.
|
|
|
|
## Session integration
|
|
|
|
If the macro's frontmatter declares `session_profile_id: <N>`, the tool:
|
|
|
|
1. Loads the profile from the DB (owner-gated — must belong to `ctx.userId`).
|
|
2. Decrypts the user's envelope-encrypted DEK using the master key.
|
|
3. Decrypts the AES-GCM storageState blob using the DEK.
|
|
4. Passes the decrypted Playwright `storageState` object to the child process.
|
|
|
|
If any step fails the tool returns `isError: true` with a descriptive message.
|
|
|
|
## Self-healing recorder
|
|
|
|
When a macro fails at runtime, the tool automatically enables the BrowseWeb
|
|
recorder for the current task (if not already enabled). On task completion,
|
|
`recording-flush` stages a candidate patch as `browser-macros/{name}.next.js`
|
|
for diff review.
|
|
|
|
## Output format
|
|
|
|
On success:
|
|
```
|
|
<result stringified>
|
|
|
|
[script logs]
|
|
<console.log lines from the child process>
|
|
```
|
|
|
|
The result is JSON-stringified if it is an object or array; `String(result)` otherwise. The `[script logs]` section is only appended when the macro produced logs.
|
|
|
|
On failure:
|
|
```
|
|
RunUserScript "{name}" failed: <error message>
|
|
|
|
The recorder is now enabled for this task; subsequent BrowseWeb actions will be captured.
|
|
On task complete, a candidate patch will be saved as browser-macros/{name}.next.js for review.
|
|
```
|
|
|
|
## Error cases
|
|
|
|
| Situation | `isError` | message contains |
|
|
|-----------|-----------|-----------------|
|
|
| No authenticated user | true | "authenticated" |
|
|
| Macro file not found | true | "not found" |
|
|
| Retired `kind: 'script'` passed | true | "retired" |
|
|
| Frontmatter parse error | true | "frontmatter" |
|
|
| Param type / missing error | true | "param" |
|
|
| Session profile not found / not owned | true | "not found or does not belong" |
|
|
| Profile not active | true | "not active" |
|
|
| DEK / blob decryption failure | true | "decrypt" |
|
|
| Macro timeout (60 s) | true | "timeout" |
|
|
| Macro exits non-zero | true | "exited code" |
|
|
|
|
## Notes
|
|
|
|
- The tool is a META_TOOL — it is available in every movement without listing it in `allowed_tools`.
|
|
- Use `ListUserAssets` first to discover available macros and their param specs.
|
|
- On macro failure, use `BrowseWeb` as a manual fallback.
|
|
- To run Python or other ad-hoc code, use the **Bash** tool (pip packages pre-baked).
|
|
|
|
## Security and trust model
|
|
|
|
`RunUserScript` is **disabled by default**. To enable it, add to `config.yaml`:
|
|
|
|
```yaml
|
|
tools:
|
|
user_scripts_enabled: true
|
|
```
|
|
|
|
**Only enable for trusted users.** Macros run in a restricted child process:
|
|
- Env is scrubbed — only `PATH`, `HOME`, `TMPDIR/TMP`, `LANG`, `NODE_ENV`, and `PLAYWRIGHT_BROWSERS_PATH` are forwarded. API keys, database passwords, and other secrets in the orchestrator's environment are not visible to the macro.
|
|
- CWD is set to the system tmpdir, not the orchestrator workspace.
|
|
- Stdout is capped at 1 MB and stderr at 200 KB; exceeding either limit kills the child.
|
|
- On timeout, the entire process group (including Playwright's Chromium) is killed.
|
|
|
|
Browser-macros cannot use Node's `--permission` model — Chromium launch, native
|
|
bindings, and outbound HTTPS all need unrestricted `child_process`/addons/network.
|
|
They run with full Node.js capability (env-scrubbed only) and rely on
|
|
container-level isolation. Treat them as trusted code.
|