Skip to content

No boilerplate. No exports. No imports. Write your logic, return a value — Hoody Exec handles everything else. Every parameter you need is automatically injected into your script context.


A Hoody Exec script is a plain TypeScript/JavaScript file. Magic comments at the top configure behavior. The rest is your code. Return a value and it becomes the HTTP response.

scripts/1/api/users/[id].ts
// @mode worker
// @cors reflective
// @timeout 5000
// ALL parameters automatically available:
// req, res, metadata, shared, console, require, ws
// (mainResult is additionally available only in post.js middleware)
const { id } = metadata.parameters; // Dynamic route param
const user = await fetchUser(id);
// Return value auto-formatted as JSON
return { user };

Key points:

  • No export default — parameters are injected automatically
  • No module.exports — just write code at the top level
  • No imports needed for built-ins — crypto, fs, path, $, Database are pre-injected
  • Magic comments go at the very top, before any code
  • Return a value to send it as the response (or use res for full control)

Every script automatically receives these parameters — no imports, no configuration needed:

// req - Incoming HTTP request
req.url // '/api/users/123'
req.method // 'GET', 'POST', etc.
req.headers // { 'authorization': 'Bearer ...', ... }
req.body // Parsed JSON body (if Content-Type: application/json)
// res - HTTP response (for full control)
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(JSON.stringify({ success: true }))
res.statusCode = 404 // Set status code
res.setHeader('X-Custom', 'value')
// metadata - Request context and routing info
metadata.executionId // Unique execution ID
metadata.parameters // { id: '123' } from dynamic routes like [id].ts
metadata.clientIp // REAL client IP (see below)
metadata.path // '/api/users/123'
metadata.method // 'GET', 'POST', etc.
metadata.url // Full URL
metadata.query // Parsed query string { search: 'term' }
// shared - State object (persists in worker mode, resets in serverless)
shared.cache = new Map() // Worker: persists across requests
shared.requestCount = 0 // Serverless: reset every request
// console - Logger
console.log('message')
console.info('info')
console.debug('debug')
console.error('error')
// require - Module loader (auto-installs missing modules)
const axios = require('axios') // Auto-installed if not present
const lodash = require('lodash') // Auto-installed if not present

Available when // @websocket is enabled (worker mode only). Provides full control over WebSocket connections:

// Event handlers — Direct assignment pattern
ws.open = (socket, req) => { ... }
ws.message = (socket, data) => { ... }
ws.close = (socket, code, reason) => { ... }
ws.error = (socket, error) => { ... }
// OR Event emitter pattern
ws.on('open', (socket, req) => { ... })
ws.on('message', (socket, data) => { ... })
ws.on('close', (socket, code, reason) => { ... })
ws.on('error', (socket, error) => { ... })
// Connection management
ws.connections // Set of all active WebSocket connections for this hostname
ws.broadcast(data) // Send data to ALL connected clients
ws.broadcast(data, excludeSocket) // Send to all except one client
// Socket data — available on each socket instance
socket.data.ip // Client IP address
socket.data.url // Request URL
socket.data.headers // Request headers
socket.data.parameters // Dynamic route parameters
socket.data.executionId // Unique execution ID for this connection
// mainResult - ONLY available in post.js middleware
// Contains the return value of the main script that just executed
// Use it to wrap, transform, or log responses
// post.js example:
return {
data: mainResult,
timestamp: Date.now(),
requestId: metadata.executionId
};
// Available when // @ai true is set — no imports needed
ai // Helper namespace with three methods:
// ai.generate(opts) — generate a text completion
// ai.stream(opts) — stream text chunks
// ai.object(opts) — generate structured JSON against a schema
openai // Pre-configured OpenAI SDK client instance
model // Pre-configured Vercel AI SDK model (from @ai-model or default)
generateText // Vercel AI SDK — generate text completions
streamText // Vercel AI SDK — stream text responses
generateObject // Vercel AI SDK — generate structured JSON objects

See AI-Powered Scripts for full usage examples and model configuration.


The res object is enhanced with Express-like convenience methods:

res.json({ data: 'value' }) // Send JSON response
res.send('text') // Send text response
res.html('<h1>Hello</h1>') // Send HTML response
res.redirect('/new-path') // HTTP redirect
res.stream(readableStream) // Stream response
res.status(404) // Set status code (chainable)

Example with chaining:

// Return a 201 JSON response
res.status(201).json({ created: true, id: 'abc123' });

These are pre-injected into every script — no require or import needed:

// $ - Bun.$ for shell commands
const output = await $`ls -la /home/user`.text()
const result = await $`echo "Hello"`.text()
// Database - bun:sqlite Database constructor (require('bun:sqlite').Database)
const db = new Database('/hoody/databases/app.db')
const rows = db.query('SELECT * FROM users').all()
// Node.js built-ins (pre-injected)
crypto.randomUUID() // crypto module
fs.readFileSync('/path/to/file') // fs module
path.join('/home', 'user') // path module
// Also available: http, https, net, tls, child_process

Return whatever you want — Hoody Exec automatically handles content types, serialization, and status codes.

// Return Object → Auto-formatted JSON (Content-Type: application/json)
return { users: [...], count: 42 };
// Return Array → Auto-formatted JSON array
return [{ id: 1 }, { id: 2 }];
// Return String (HTML detected) → Content-Type: text/html
return "<!DOCTYPE html><html><body>Hello</body></html>";
// Return String (other) → Content-Type: text/plain
return "Plain text response";
// Return Number → JSON number
return 42;
// Return Boolean → JSON boolean
return true;
// Return Buffer → Auto-detected MIME type (images, PDFs, files)
return fs.readFileSync('/path/to/image.png'); // Automatic image/png
// Return Error → 500 status with error details
return new Error("Something went wrong");
// Return Nothing → Empty 204 response
// (no return statement or return undefined)

For full control, use res directly to bypass auto-handling:

res.writeHead(200, { 'Content-Type': 'application/xml' });
res.end('<?xml version="1.0"?><data>Custom</data>');

The abstraction: You focus on logic. Hoody Exec handles HTTP protocol details automatically.



These npm packages are bundled and always available — no installation delay on first use:

PackageDescription
@ai-sdk/openaiVercel AI OpenAI provider
aiVercel AI SDK
axiosHTTP client
cheerioHTML parser (jQuery-like)
cookieCookie parser
dayjsDate library
ejsTemplate engine
jsonwebtokenJWT creation/verification
lodashUtility library
markedMarkdown parser
mime-typesMIME type detection
openaiOfficial OpenAI SDK
papaparseCSV parser
playwright-coreBrowser automation (Chromium/Firefox/WebKit) — bring your own browser
puppeteer-coreHeadless Chrome/Firefox automation — bring your own browser
qrcodeQR code generator
rss-parserRSS/Atom feed parser
sanitize-htmlHTML sanitizer
uuidUUID generation
wsWebSocket client/server
xml2jsXML ↔ JS object parser
yamlYAML parser
zodSchema validation (also exposed as z)

Any other npm package is auto-installed on first require() — no package.json needed.


Scripts are stored in instance-specific directories:

/hoody/storage/hoody-exec/scripts/default/1/ # exec-1 (under default subdomain)
/hoody/storage/hoody-exec/scripts/default/2/ # exec-2
/hoody/storage/hoody-exec/scripts/default/test/ # exec-test
# Runtime routing also accepts the execId-only fallback /hoody/storage/hoody-exec/scripts/1/

The instance number (1, 2, test, etc.) maps to the hostname:

  • exec-1scripts/1/
  • exec-2scripts/2/
  • exec-testscripts/test/

To create scripts programmatically, use the scripts/write endpoint:

Terminal window
curl -s -X POST "https://PROJECT-CONTAINER-exec-1.SERVER.containers.hoody.icu/api/v1/exec/scripts/write" \
-H "Content-Type: application/json" \
-d '{
"path": "api/hello.ts",
"content": "// @mode serverless\nreturn { hello: \"world\" };",
"createDirs": true,
"validate": true
}'

When using AI-powered scripts (// @ai true), you can define system prompts in companion markdown files. These are loaded automatically and injected into the AI context.

Create a .system.md file matching your script name:

scripts/1/
├── chat.js # Your AI script
├── chat.system.md # System prompt for chat.js (loaded automatically)
├── summarize.js
└── summarize.system.md

The file chat.system.md is automatically loaded when chat.js executes. Write your system prompt as plain markdown:

You are a helpful coding assistant. Be concise and provide working code examples.
Always explain your reasoning step by step.

Create a _system.md file to set a default system prompt for all AI scripts in that directory:

scripts/1/api/
├── _system.md # Default system prompt for all scripts in api/
├── chat.js # Uses _system.md (no script-level override)
├── chat.system.md # Overrides _system.md for chat.js specifically
└── translate.js # Uses _system.md

Resolution order: Script-level (chat.system.md) takes precedence over directory-level (_system.md).

See AI-Powered Scripts for full details on AI configuration and model selection.


Hoody Exec runs on the Bun runtime:

  • 3x faster startup — Native speed optimizations
  • Modern JavaScript — Latest ECMAScript features built-in
  • Better module system — Improved dependency handling
  • Optimized for serverless — Perfect for fast script execution
  • Lower memory usage — More efficient runtime