This page is automatically synced from
docs-en/scripts.md. Language: English | 中文
Scripts Management and Development Guide
The Admin UI Scripts module manages request, response, streaming response, decode, and parser scripts. Scripts are stored on disk and executed by the QuickJS sandbox. For rules generated or shared through a CLI or AI, prefer inline script blocks in the rule itself; the Scripts page is intended primarily for interactive tests, manual editing, and reusable local named scripts.
Script Types
- Request Script: runs before forwarding to upstream and can modify method, headers, or body.
- Response Script: runs after receiving upstream response and can modify status, headers, or body.
- Streaming Response Script: runs through
resStreamScript://and incrementally transforms or mocks true SSE output without collecting the response. - Decode Script: decodes, redacts, or formats body content before display and persistence.
- Parser Script: used with
bp://...anddecode://bpfor binary protocol parsing. It affects stored and displayed traffic, not the actual client/upstream stream.
Naming
Script names map to {data_dir}/scripts/{type}/{name}.js. Names may include / for directory hierarchy. They must be non-empty, at most 128 characters, must not start or end with /, must not contain .. or //, and may only use letters, digits, -, _, and /.
Runtime Objects
ctx: request id, script name, script type, Values, matched rules, and phase.log/console: logs visible in the UI.file: sandboxed file API.net: optional network API with limits.request/response: phase-specific mutable traffic objects.
QuickJS execution is synchronous; async/await is not supported.
Streaming Responses with resStreamScript
Use resStreamScript://{script_name} or an inline rule block when an SSE response must remain incremental. Each HTTP response stream gets its own persistent QuickJS context, so top-level variables and closures survive across events in the same stream while separate requests remain isolated.
Two true streaming modes are available:
- Transform requires an upstream
Content-Type: text/event-streamresponse. Bifrost callsstream.onEvent(event)for each complete SSE event and sends the returned output immediately, without waiting for upstream EOF or[DONE]. - Mock calls
stream.next()repeatedly. Each result is sent before the optionaldelayMsis applied, so the response is not pre-generated or buffered as a complete body.
Transform example:
stream.mode = "transform";
let index = 0;
stream.onEvent = (event) => {
index += 1;
return {
event: "mapped.delta",
data: JSON.stringify({ index, upstream: event.data }),
};
};
stream.onEnd = () => "data: [DONE]\n\n";Mock example:
stream.mode = "mock";
let index = 0;
stream.next = () => {
index += 1;
return {
output: { event: "mock.delta", data: String(index) },
delayMs: 100,
done: index === 3,
};
};stream.next(), stream.onEvent(event), and stream.onEnd() may return a raw SSE string, an SSE event object, an array, a step object with output / outputs, or null / undefined for no output. A complete input event is limited to 16 MiB; oversize events and callback failures after streaming starts produce an explicit SSE error instead of silent truncation.
Streaming callbacks remain synchronous and do not support async/await. The sandbox timeout_ms restarts for each JavaScript callback and limits only that callback's CPU time; time spent waiting for the next upstream event does not accumulate. A matched result may contain only one resStreamScript, and it cannot be combined with resScript, which collects the complete body.
See the Script Rules reference for inline blocks, event fields, return shapes, response headers, and composition limits.