Skip to main content

JavaScript API Reference

Egern scripts use export default to export an async function. The ctx object is injected at runtime.

export default async function(ctx) {
// ...
}

Five script types are supported: Request, Response, Schedule, Generic, and Network.


ctx

ctx.script

Script information.

PropertyTypeDescription
ctx.script.namestringScript name

ctx.env

Object<string, string> — Environment variable key-value pairs. See Environment Variables for details.

export default async function(ctx) {
const apiKey = ctx.env.API_KEY;
const apiUrl = ctx.env.API_URL;
}

ctx.app

Application information.

PropertyTypeDescription
ctx.app.versionstringApp version
ctx.app.languagestringSystem language

ctx.device

Device network environment information.

PropertyTypeDescription
ctx.device.cellular.carrierstring | nullCellular carrier
ctx.device.cellular.radiostring | nullCellular radio technology
ctx.device.wifi.ssidstring | nullWi-Fi name
ctx.device.wifi.bssidstring | nullWi-Fi BSSID
ctx.device.ipv4.addressstring | nullIPv4 address
ctx.device.ipv4.gatewaystring | nullIPv4 gateway
ctx.device.ipv4.interfacestring | nullNetwork interface
ctx.device.ipv6.addressstring | nullIPv6 address
ctx.device.ipv6.interfacestring | nullNetwork interface
ctx.device.dnsServersstring[]DNS server list

ctx.cron

string | undefined — Only available in schedule scripts. The cron expression.

ctx.widgetFamily

string | undefined — Only available in generic scripts. The widget size family.

Possible values: systemSmall, systemMedium, systemLarge, systemExtraLarge, accessoryCircular, accessoryRectangular, accessoryInline.

ctx.request

Object | undefined — Only available in request/response scripts.

Property/MethodTypeDescription
methodstringHTTP method
urlstringRequest URL
headersHeadersRequest headers
bodyReadableStream | nullRequest body stream
json()Promise<any>Parse as JSON
text()Promise<string>Parse as text
arrayBuffer()Promise<ArrayBuffer>Parse as ArrayBuffer
blob()Promise<Blob>Parse as Blob
formData()Promise<FormData>Parse as FormData

Note: The body can only be consumed once (consistent with Fetch API behavior).

ctx.response

Object | undefined — Only available in response scripts.

Property/MethodTypeDescription
statusnumberStatus code
headersHeadersResponse headers
bodyReadableStream | nullResponse body stream
json()Promise<any>Parse as JSON
text()Promise<string>Parse as text
arrayBuffer()Promise<ArrayBuffer>Parse as ArrayBuffer
blob()Promise<Blob>Parse as Blob
formData()Promise<FormData>Parse as FormData

Note: The body can only be consumed once (consistent with Fetch API behavior).

Headers Object

ctx.request.headers, ctx.response.headers, and ctx.http response headers are all Headers objects. They support case-insensitive property access and the following methods:

MethodReturnDescription
headers.get(name)string | nullGet value (multiple values joined with , )
headers.getAll(name)string[]Get all values (always returns array)
headers.has(name)booleanCheck existence
headers.set(name, value)voidSet (replaces existing)
headers.append(name, value)voidAppend value
headers.delete(name)voidDelete

All method name parameters are case-insensitive. Direct property access is also case-insensitive, returning string for single values and string[] for multiple values:

// These are equivalent
headers['Content-Type'] // 'application/json'
headers['content-type'] // 'application/json'

// Multi-value header
headers['set-cookie'] // ['session=abc', 'token=xyz']
headers.get('set-cookie') // 'session=abc, token=xyz'
headers.getAll('set-cookie') // ['session=abc', 'token=xyz']

// Modification
headers.set('X-Custom', 'value');
headers.append('X-Custom', 'value2');
headers.delete('X-Custom');
headers['X-New'] = 'value';
delete headers['X-New'];

ctx.http

Send HTTP requests. All methods return Promise<Response>.

Methods

ctx.http.get(url, options?)
ctx.http.post(url, options?)
ctx.http.put(url, options?)
ctx.http.delete(url, options?)
ctx.http.head(url, options?)
ctx.http.options(url, options?)
ctx.http.patch(url, options?)

Parameters

  • urlstring, the request URL.
  • options — Optional object:
FieldTypeDescription
headersHeaders | Object<string, string | string[]>Request headers (multi-value headers as arrays)
bodystring | Uint8Array | Object | ReadableStreamRequest body (Object is auto-serialized to JSON)
timeoutnumberTimeout in milliseconds
policystringProxy policy
policyDescriptorstringPolicy descriptor
redirect'follow' | 'manual' | 'error'Redirect policy (default 'follow'); manual returns 3xx response, error throws on redirect
credentials'omit' | 'include'Whether to include cookies (default 'include')
insecureTlsbooleanAllow insecure TLS (default false)

Response Object

Property/MethodTypeDescription
statusnumberStatus code
headersHeadersResponse headers
bodyReadableStreamResponse body stream
json()Promise<any>Parse as JSON
text()Promise<string>Parse as text
blob()Promise<Blob>Parse as Blob
arrayBuffer()Promise<ArrayBuffer>Parse as ArrayBuffer
formData()Promise<FormData>Parse as FormData

Note: The body can only be consumed once (consistent with Fetch API behavior).

Example

const resp = await ctx.http.get('https://api.example.com/data');
const data = await resp.json();

ctx.storage

Persistent key-value storage.

MethodReturnDescription
ctx.storage.get(key)string | nullRead value
ctx.storage.set(key, value)voidWrite value (value is string)
ctx.storage.getJSON(key)any | nullRead and JSON parse
ctx.storage.setJSON(key, value)voidJSON serialize and write
ctx.storage.delete(key)voidDelete key

Example

ctx.storage.set('token', 'abc123');
const token = ctx.storage.get('token'); // 'abc123'
ctx.storage.delete('token');

// JSON convenience methods
ctx.storage.setJSON('config', { theme: 'dark', lang: 'zh' });
const config = ctx.storage.getJSON('config'); // { theme: 'dark', lang: 'zh' }

ctx.notify(options)

Send a notification.

FieldTypeDescription
titlestringTitle
subtitlestringSubtitle (optional)
bodystringContent (optional)
soundbooleanPlay alert sound (default true)
durationnumberDisplay duration in seconds (optional)
attachmentObjectNotification attachment (optional)
attachment.urlstringMedia URL, auto-downloaded as attachment (mutually exclusive with base64)
attachment.base64stringBase64-encoded media data (mutually exclusive with url)
attachment.mimeTypestringMIME type (optional, gif/png/jpg/pdf auto-detected)
actionObjectTap action (optional)
action.typestring"openUrl" or "clipboard"
action.urlstringURL to open when type is "openUrl"
action.textstringText to copy when type is "clipboard"

Example

// Basic notification
ctx.notify({ title: 'Done', body: 'Task completed' });

// With attachment and tap action
ctx.notify({
title: 'Screenshot saved',
body: 'Tap for details',
sound: true,
duration: 5,
attachment: {
url: 'https://example.com/image.png',
mimeType: 'image/png',
},
action: {
type: 'openUrl',
url: 'https://example.com/details',
},
});

// Base64 attachment + copy to clipboard
ctx.notify({
title: 'Verification code',
body: '1234',
attachment: {
base64: 'iVBORw0KGgo...',
mimeType: 'image/png',
},
action: {
type: 'clipboard',
text: '1234',
},
});

ctx.lookupIP(ip)

Look up IP address information.

  • ipstring, the IP address.
  • Returns Object | null:
PropertyTypeDescription
countrystringCountry/region code
asnnumberAS number
organizationstringOrganization name

Example

const info = ctx.lookupIP('8.8.8.8');
// { country: 'US', asn: 15169, organization: 'GOOGLE' }

ctx.compress

Compression/decompression. Input and output are Uint8Array. All methods return Promise<Uint8Array | null>.

MethodDescription
ctx.compress.gzip(data)Gzip compress
ctx.compress.gunzip(data)Gzip decompress
ctx.compress.deflate(data)Deflate compress
ctx.compress.inflate(data)Deflate decompress
ctx.compress.brotli(data)Brotli compress
ctx.compress.unbrotli(data)Brotli decompress

ctx.ssh

SSH client with remote command execution and SFTP file operations.

ctx.ssh.connect(config)

Connect to an SSH server. Returns Promise<Session>.

FieldTypeDescription
hoststringServer address
portnumberPort (default 22)
usernamestringUsername
passwordstringPassword (mutually exclusive with privateKey)
privateKeystringPrivate key content (mutually exclusive with password)
passphrasestringPrivate key passphrase (optional)
timeoutnumberConnection timeout in milliseconds (default 10000)

Session

session.exec(command, options?)

Execute a remote command. Returns Promise<ExecResult>.

options:

FieldTypeDescription
stdinstringStandard input (optional)
encoding'binary'When set to 'binary', stdout/stderr return Uint8Array (optional)
envObject<string, string>Environment variables (optional, requires server AcceptEnv configuration)

ExecResult:

PropertyTypeDescription
codenumberExit code
stdoutstring | Uint8ArrayStandard output
stderrstring | Uint8ArrayStandard error

session.sftp()

Initialize the SFTP subsystem. Returns Promise<Sftp>. Only needs to be called once per Session.

session.close()

Disconnect the SSH connection. Returns Promise<void>.

Sftp

SFTP file operations. API style aligned with Node.js fs/promises.

File Read/Write

MethodReturnDescription
sftp.readFile(path, encoding?)Promise<string | Uint8Array>Read file; returns string when encoding is 'utf8', otherwise Uint8Array
sftp.writeFile(path, data, options?)Promise<void>Write file (overwrite); data can be string, Uint8Array, or Object (auto JSON serialized)
sftp.appendFile(path, data, options?)Promise<void>Append to file; same data types as above

The encoding parameter for readFile can be the string 'utf8' or an object { encoding: 'utf8' }.

writeFile/appendFile options:

FieldTypeDescription
modenumberFile permissions (default 0o644)

Directory Operations

MethodReturnDescription
sftp.readdir(path)Promise<DirEntry[]>List directory contents
sftp.mkdir(path, options?)Promise<void>Create directory
sftp.rm(path, options?)Promise<void>Remove file or directory

mkdir options:

FieldTypeDescription
modenumberDirectory permissions (default 0o755)
recursivebooleanRecursively create parent directories (default false)

rm options:

FieldTypeDescription
recursivebooleanRecursively remove directory and contents (default false)
forcebooleanIgnore nonexistent paths (default false)

File Information

MethodReturnDescription
sftp.stat(path)Promise<FileAttr>Get file attributes (follows symlinks)
sftp.lstat(path)Promise<FileAttr>Get file attributes (does not follow symlinks)
sftp.exists(path)Promise<boolean>Check if path exists
sftp.statfs(path)Promise<StatFs>Get filesystem information

FileAttr:

PropertyTypeDescription
sizenumberFile size in bytes
uidnumberOwner UID
gidnumberOwner GID
modenumberPermission bits
atimeDateLast access time
mtimeDateLast modification time
isFile()booleanWhether it is a regular file
isDirectory()booleanWhether it is a directory
isSymbolicLink()booleanWhether it is a symbolic link

DirEntry inherits all FileAttr properties, plus:

PropertyTypeDescription
namestringFile name

StatFs:

PropertyTypeDescription
bsizenumberBlock size
frsizenumberFragment size
blocksnumberTotal blocks
bfreenumberFree blocks
bavailnumberFree blocks for unprivileged users
filesnumberTotal inodes
ffreenumberFree inodes
favailnumberFree inodes for unprivileged users
fsidnumberFilesystem ID
flagnumberMount flags
namemaxnumberMaximum filename length

Permissions and Ownership

MethodReturnDescription
sftp.chmod(path, mode)Promise<void>Change permissions (mode is octal, e.g. 0o755)
sftp.chown(path, uid, gid)Promise<void>Change owner
MethodReturnDescription
sftp.rename(oldPath, newPath)Promise<void>Rename/move
sftp.symlink(target, path)Promise<void>Create symbolic link
sftp.readlink(path)Promise<string>Read symbolic link target
sftp.realpath(path)Promise<string>Resolve to absolute path

Examples

// Execute remote command
const session = await ctx.ssh.connect({
host: '192.168.1.100',
username: 'root',
privateKey: ctx.env.sshKey,
});
const result = await session.exec('uname -a');
ctx.notify({ title: 'Server', body: result.stdout.trim() });
await session.close();
// SFTP file operations
const session = await ctx.ssh.connect({
host: '192.168.1.100',
username: 'deploy',
password: ctx.env.sshPassword,
});
const sftp = await session.sftp();

// Upload config
await sftp.writeFile('/etc/app/config.json', { port: 8080, debug: false });

// Read log
const log = await sftp.readFile('/var/log/app.log', 'utf8');

// Backup directory
const files = await sftp.readdir('/etc/app');
for (const f of files) {
if (f.isFile()) {
const data = await sftp.readFile(`/etc/app/${f.name}`);
await sftp.writeFile(`/backup/app/${f.name}`, data);
}
}

await session.close();

ctx.respond(response)

In request scripts, return a response directly without sending the request to the upstream server.

  • response — Object:
FieldTypeDescription
statusnumberStatus code
headersHeaders | Object<string, string | string[]>Response headers (optional, multi-value as arrays)
bodystring | Uint8Array | Object | ReadableStreamResponse body (optional, Object is auto-serialized to JSON)
return ctx.respond({ status: 200, headers: { 'Content-Type': 'text/plain' }, body: 'blocked' });

ctx.abort()

Abort the current request or response. Used in request/response scripts.

return ctx.abort();

Return Values

The function's return value determines script behavior. Different script types return different structures.

Request Script

Executes before an HTTP request is sent. Can modify the request, return a response directly, or abort.

Return an object to modify the request. All fields are optional; omitted fields remain unchanged.

FieldTypeDescription
methodstringHTTP method
urlstringRequest URL
headersHeaders | Object<string, string | string[]>Request headers (multi-value as arrays)
bodystring | Uint8Array | Object | ReadableStreamRequest body (Object is auto-serialized to JSON)

You can also use ctx.respond() to return a response directly, ctx.abort() to abort, or return nothing to pass through.

// Modify request
return { url: 'https://...', headers: { ... }, body: '...' };

// Pass through body stream
return { url: 'https://...', body: ctx.request.body };

// Return response directly
return ctx.respond({ status: 200, headers: {}, body: 'blocked' });

// Abort request
return ctx.abort();

Response Script

Executes before an HTTP response is returned to the client. Can modify the response or abort.

Return an object to modify the response. All fields are optional; omitted fields remain unchanged.

FieldTypeDescription
statusnumberStatus code
headersHeaders | Object<string, string | string[]>Response headers (multi-value as arrays)
bodystring | Uint8Array | Object | ReadableStreamResponse body (Object is auto-serialized to JSON)

You can also use ctx.abort() to abort, or return nothing to pass through.

return { status: 200, headers: { ... }, body: '...' };
return ctx.abort();

Generic Script

Return a Widget DSL JSON object. Egern renders it as an iOS widget. The root node must have type: "widget".

return {
type: 'widget',
children: [
{ type: 'text', text: 'Status: OK', font: { size: 'headline', weight: 'semibold' }, textColor: '#FFFFFF' }
],
backgroundColor: '#2D6A4F',
padding: 16,
};

Schedule / Network Script

No return value needed.


Full Examples

Request — URL Rewrite

export default async function(ctx) {
return { url: ctx.request.url.replace('http://', 'https://') };
}

Response — Modify JSON Body

export default async function(ctx) {
const data = await ctx.response.json();
data.ads = [];
return { body: data };
}

Request — Block Ads

export default async function(ctx) {
if (ctx.request.url.includes('/ads')) return ctx.abort();
}

Schedule — Scheduled Task

export default async function(ctx) {
const resp = await ctx.http.get('https://api.example.com/data');
const data = await resp.json();
ctx.storage.set('latest', JSON.stringify(data));
ctx.notify({ title: 'Updated', body: `${data.length} items` });
}

Generic — Widget

export default async function(ctx) {
const resp = await ctx.http.get('https://api.example.com/status');
const status = await resp.text();
return {
type: 'widget',
children: [
{ type: 'text', text: 'Server Status', font: { size: 'headline', weight: 'bold' }, textColor: '#FFFFFF' },
{ type: 'text', text: status, font: { size: 'body' }, textColor: '#FFFFFFCC' },
],
backgroundColor: '#1A1A2E',
padding: 16,
gap: 8,
};
}

Schedule — SSH Server Monitoring

export default async function(ctx) {
const session = await ctx.ssh.connect({
host: ctx.env.host,
username: ctx.env.user,
privateKey: ctx.env.key,
});

const { stdout: uptime } = await session.exec('uptime -p');
const { stdout: disk } = await session.exec('df -h / | tail -1');
await session.close();

ctx.notify({
title: 'Server Status',
body: `${uptime.trim()}\nDisk: ${disk.trim()}`,
});
}