JavaScript 脚本

Chute 支持 JavaScript 脚本,用于高级请求/响应修改、自定义规则匹配、DNS 解析和定时任务。脚本使用 Apple 的 JavaScriptCore 引擎,遵循 Surge 兼容的脚本 API。

脚本在配置文件的 [Script] 部分中定义。

配置

[Script]
MyScript = type=http-request, script-path=/path/to/script.js, pattern=^https?://example\.com, requires-body=true, max-size=262144, timeout=10, argument=myArg, debug=true
CronJob = type=cron, script-path=/path/to/cron.js, cron-expression=* * * * *, wake-system=true

脚本参数

参数 必填 默认值 描述
type http-request 脚本触发类型(见下文)
script-path JS 脚本的本地文件路径或 HTTP(S) URL
pattern (匹配所有) 过滤脚本触发时机的 URL 正则表达式
requires-body 自动检测 强制脚本接收完整的请求/响应 Body
max-size 131072 (128KB) 需要访问 Body 的脚本的最大 Body 大小(字节)
timeout 5.0 秒 每个脚本的执行超时时间
argument 自定义字符串参数,在 JS 中以 $argument 访问
debug false 跳过编译缓存以方便调试
cron-expression Cron 调度表达式(仅 cron 类型)
wake-system false 唤醒系统以执行 cron 脚本(仅 iOS)
enable true 启用或禁用此脚本
script-update-interval 0 远程脚本更新的间隔秒数(-1 = 从不更新,0 = 仅启动时更新)

脚本类型

类型字符串 枚举 描述
http-request HTTP Request 在请求发送到上游之前拦截并修改 HTTP 请求
http-response HTTP Response 在响应返回给客户端之前拦截并修改 HTTP 响应
http-request-before-send HTTP Request Before Send 在完整 Body 收集后、发送前修改请求
rule Rule 自定义规则匹配逻辑
dns DNS 自定义 DNS 解析
cron Cron 定时/计划脚本
event Event 系统事件处理(例如 network-changed)

Body 自动检测:如果脚本源码中包含 $request.body$response.body,Body 将自动提供,最大不超过 max-size。使用 requires-body=true 可强制此行为。


JavaScript API 参考

脚本在沙盒化的 JavaScriptCore 环境中运行,以下全局对象可用。

$request(只读)

可用范围:http-requesthttp-responsehttp-request-before-sendrule

属性 类型 描述
.url String 完整请求 URL
.method String HTTP 方法(GETPOST 等),DNS 查询时为 QUERY
.headers Object 请求头键值对
.body String 或 null 请求 Body(UTF-8 解码)
.hostname String 目标主机名
.destPort Number 目标端口
.processPath String 发起请求的进程路径(仅 macOS)
.userAgent String User-Agent 请求头值
.sourceIP String 来源 IP 地址
.listenPort Number 代理监听端口
.requestId String 唯一请求 ID
.dnsResult String 解析后的 IP 地址
.srcPort Number 来源端口
.protocol String 检测到的协议:httphttpstcpdns

$response(只读)

可用范围:http-response

属性 类型 描述
.status Number HTTP 状态码
.headers Object 响应头键值对
.body String 或 null 响应 Body(UTF-8 解码)

$done(value) — 完成处理函数

必须在脚本结束时恰好调用一次,以表示脚本完成。脚本执行将阻塞,直到 $done() 被调用或超时到期。

$done({})                        // 直通 — 不做任何修改
$done()                          // 中止连接
$done({matched: true})           // 规则匹配结果(仅 rule 脚本)
$done({address: "1.2.3.4"})      // DNS 结果(仅 dns 脚本)

HTTP Request 脚本返回值:

$done({
    url: "https://new.example.com/path",     // 重写 URL
    headers: {"X-Custom": "value"},           // 修改请求头
    body: "new request body",                 // 修改 Body
    response: {                               // 返回合成响应(跳过上游)
        status: 200,
        headers: {"Content-Type": "text/html"},
        body: "<html>Blocked</html>"
    }
})

当提供 response 时,请求被短路:Chute 直接向客户端返回合成响应,而不联系上游服务器。这对于阻止请求、Mock API 或返回缓存内容非常有用。

HTTP Response 脚本返回值:

$done({
    status: 200,                              // 修改状态码
    headers: {"X-Custom": "value"},           // 修改响应头
    body: "new response body",                // 修改响应 Body
    url: "https://other.example.com"          // 触发 302 重定向
})

当提供 url 时,Chute 返回一个到给定 URL 的 302 重定向,替代原始响应。

DNS 脚本返回值:

$done({address: "1.2.3.4"})                  // 单个 IP
$done({addresses: ["1.2.3.4", "5.6.7.8"]})   // 多个 IP
$done({address: "10.0.0.1", ttl: 300})       // 自定义 TTL(秒,默认 60)
$done({server: "8.8.8.8"})                   // 转发到特定 DNS 服务器

$httpClient — 异步 HTTP 客户端

在脚本中发起 HTTP 请求。所有请求在 $done() 或超时时被取消。

$httpClient.get(url, function(error, response, data) {
    if (error) {
        console.log("Request failed: " + error)
    } else {
        console.log("Status: " + response.status)
        console.log("Response: " + data)
    }
})

$httpClient.post(url, {headers: {...}, body: "...", timeout: 5}, callback)
$httpClient.put(url, options, callback)
$httpClient.del(url, options, callback)
$httpClient.head(url, options, callback)
$httpClient.options(url, options, callback)
$httpClient.patch(url, options, callback)

回调签名:callback(error, response, data)

  • error:错误字符串或 null
  • response{status: Number, headers: Object}null
  • data:UTF-8 字符串响应 Body 或 null

$persistentStore — 键值存储

持久化的键值存储,在脚本和进程重启后仍然保留。底层使用 NSUserDefaults

$persistentStore.write(data, key)   // 存储一个值
$persistentStore.read(key)          // 读取一个值
$persistentStore.remove(key)        // 删除一个值

$notification — 本地通知

发送本地系统通知。

$notification.post("Title", "Subtitle", "Notification body text")

$network — 网络信息

只读的网络状态信息。

$network.dns   // DNS 服务器 IP 数组
$network.wifi  // {ssid: "WiFiName", bssid: "aa:bb:cc:dd:ee:ff"}

$environment — 运行时信息

$environment.system     // "iOS" 或 "macOS"
$environment.appVersion // 应用版本字符串

$utils — 工具函数

$utils.geoip("1.2.3.4")   // 国家代码(例如 "US")
$utils.ipasn("1.2.3.4")   // ASN 号码(例如 "13335")
$utils.ungzip(data)       // 解压 gzip 数据

$klne — 代理控制 API

从脚本中控制代理运行时。

$klne.policyGroups                    // 获取所有策略组
$klne.selectPolicy("Group", "Proxy")  // 切换策略组中的策略
$klne.getActiveConnections()          // 列出活跃连接
$klne.closeConnection("id")           // 关闭连接
$klne.flushDNS()                      // 清除 DNS 缓存
$klne.startURLTest("Group")           // 对策略组触发 URL 测试
$klne.reloadConfiguration()           // 重新加载所有配置
$klne.setOutboundMode("rule")         // 设置模式:"global"、"proxy"、"direct"、"rule"
$klne.setHTTPCaptureEnabled(true)     // 启用/禁用 MITM

$script — 脚本元数据

$script.name       // 配置中的脚本名称
$script.type       // 脚本类型字符串
$script.startTime  // 纪元时间戳

每次执行的全局变量

以下变量在每次脚本执行时注入,并且特定于某些脚本类型。

$argument — 脚本参数

来自脚本配置中 argument= 参数的字符串值。可用范围:http-requesthttp-responsehttp-request-before-sendrulednscron

console.log("Argument: " + $argument)

$domain — DNS 域名(仅 DNS 脚本)

正在查询的域名。仅可在 dns 脚本中使用。

var domain = $domain  // 例如 "example.com"

$cronexp — Cron 表达式(仅 Cron 脚本)

来自脚本配置的 Cron 调度表达式。仅可在 cron 脚本中使用。

console.log("Schedule: " + $cronexp)  // 例如 "*/30 * * * *"

$event — 事件信息(仅 Event 脚本)

有关触发事件的信息。目前仅支持 network-changed

console.log("Event: " + $event.name)  // "network-changed"

console — 日志

console.log("Debug message")    // 详细日志
console.warn("Warning message")  // 警告日志
console.error("Error message")   // 带有 [JS-ERROR] 前缀的警告日志

setTimeout(fn, seconds) — 定时器

安排函数在延迟后执行。

setTimeout(function() {
    console.log("Delayed execution")
}, 2.5)  // 2.5 秒

$script(subScriptPath) — 子脚本加载器

加载并执行另一个 JavaScript 文件。

$script("/path/to/helper.js")
$script("https://example.com/remote-script.js")

// 使用 $persistentStore 在脚本之间传递数据

脚本类型详解

HTTP 请求脚本

在收到请求头时执行。可以在请求转发之前修改 URL、请求头和 Body。

[Script]
ModifyHeaders = type=http-request, script-path=modify.js, pattern=^https://api\.example\.com

HTTP 响应脚本

在收到响应头时执行。可以在返回给客户端之前修改状态码、响应头和 Body。

[Script]
ModifyResponse = type=http-response, script-path=response.js, pattern=^https://api\.example\.com

HTTP 请求发送前脚本

在完整的请求 Body 收集完成后、即将发送到上游之前执行。适用于修改 POST/PUT 请求 Body。

[Script]
BeforeSend = type=http-request-before-send, script-path=before-send.js, pattern=^https://api\.example\.com, requires-body=true

规则脚本

自定义规则匹配。脚本必须调用 $done({matched: true})$done({matched: false})

[Rule]
SCRIPT,MyRuleScript,DIRECT

[Script]
MyRuleScript = type=rule, script-path=rule.js

DNS 脚本

自定义 DNS 解析。接收 $domain 并返回解析后的地址。

// dns.js
var domain = $domain
if (domain === "internal.example.com") {
    $done({address: "10.0.0.1", ttl: 300})
} else {
    $done({})  // 直通到正常的 DNS 解析
}

Cron 脚本

使用 Cron 表达式进行定时执行。最小间隔为 60 秒。

[Script]
HourlyTask = type=cron, script-path=hourly.js, cron-expression=0 * * * *

支持简化的 Cron 语法(例如 */30 * * * * 表示每 30 分钟,支持逗号分隔的值)。

Event 脚本

由系统事件触发。目前支持 network-changed 事件(在 Wi-Fi 或蜂窝网络变更时触发)。

[Script]
NetChange = type=event, script-path=network-changed.js

$event 对象可用:

$event.name  // "network-changed"

实用示例

重定向移动设备

一个根据 User-Agent 重定向移动用户的 http-request 脚本:

[Script]
MobileRedirect = type=http-request, script-path=mobile-redirect.js, pattern=^https://example\\.com
// mobile-redirect.js
var ua = $request.headers["User-Agent"] || ""
if (/Mobile|Android|iPhone/.test(ua)) {
    $done({
        response: {
            status: 302,
            headers: {"Location": "https://m.example.com" + $request.url.replace(/.*example\\.com/, "")},
            body: ""
        }
    })
} else {
    $done({})
}

屏蔽 API 响应中的内容

一个从 JSON API 响应中移除广告和赞助内容的 http-response 脚本:

[Script]
RemoveAds = type=http-response, script-path=remove-ads.js, pattern=^https://api\\.example\\.com/feed, requires-body=true
// remove-ads.js
var body = JSON.parse($response.body)
if (body.ads) {
    delete body.ads
}
if (body.recommendations) {
    body.recommendations = body.recommendations.filter(function(r) {
        return !r.sponsored
    })
}
$done({body: JSON.stringify(body)})

发送前修改请求 Body

一个清理 POST 负载的 http-request-before-send 脚本:

[Script]
SanitizePayload = type=http-request-before-send, script-path=sanitize.js, pattern=^https://api\\.example\\.com/submit, requires-body=true
// sanitize.js
var body = JSON.parse($request.body)
body.clientSecret = "[REDACTED]"
body.timestamp = Math.floor(Date.now() / 1000)
$done({body: JSON.stringify(body)})

自定义规则:基于时间的路由

一个根据时间选择不同代理的 rule 脚本:

[Rule]
SCRIPT,TimeBasedRule,ProxyA

[Script]
TimeBasedRule = type=rule, script-path=time-rule.js
// time-rule.js
var hour = new Date().getHours()
if (hour >= 9 && hour < 18) {
    $done({matched: false})  // 工作时间内让给下一条规则处理
} else {
    $done({matched: true})   // 非工作时间使用 ProxyA
}

为内部域名自定义 DNS

一个将内部主机名解析为本地 IP 的 dns 脚本:

[Script]
InternalDNS = type=dns, script-path=internal-dns.js
// internal-dns.js
var internalHosts = {
    "gitlab.local": "10.0.0.10",
    "registry.local": "10.0.0.11",
    "monitor.local": "10.0.0.12"
}
if (internalHosts[$domain]) {
    $done({address: internalHosts[$domain], ttl: 3600})
} else {
    $done({})  // 直通到正常的 DNS 解析
}

网络变更时自动切换策略

一个在蜂窝网络时切换到保守策略组的 event 脚本:

[Script]
NetSwitch = type=event, script-path=network-switch.js
// network-switch.js
if (!$network.wifi.ssid) {
    // 在蜂窝网络上 — 使用低流量策略组
    $klne.selectPolicy("MainGroup", "LowDataProxy")
    console.log("Switched to cellular profile")
} else if ($network.wifi.ssid === "Office") {
    $klne.selectPolicy("MainGroup", "DIRECT")
    console.log("Switched to office profile")
}

定期健康检查

一个每 30 分钟检查代理健康状况的 cron 脚本:

[Script]
HealthCheck = type=cron, script-path=health-check.js, cron-expression=*/30 * * * *
// health-check.js
$httpClient.head("https://www.google.com/generate_204", {timeout: 10},
    function(error, response, data) {
        if (error || response.status !== 204) {
            console.error("Health check failed: " + (error || "status " + response.status))
            $notification.post("Chute Alert", "Health Check", "Cannot reach Google")
        } else {
            console.log("Health check OK")
        }
    }
)
$done()

用外部数据丰富 API 响应

一个通过调用辅助 API 来丰富用户数据的 http-response 脚本:

[Script]
EnrichUsers = type=http-response, script-path=enrich.js, pattern=^https://api\\.example\\.com/users, requires-body=true
// enrich.js
var users = JSON.parse($response.body)
var pending = users.length
if (pending === 0) { $done({}) }

users.forEach(function(user, index) {
    $httpClient.get("https://internal-api.example.com/avatar/" + user.id,
        function(error, resp, data) {
            if (!error && resp.status === 200) {
                users[index].avatar = JSON.parse(data).url
            }
            pending--
            if (pending === 0) {
                $done({body: JSON.stringify(users)})
            }
        }
    )
})

执行模型

  • 所有脚本在专用的串行队列上运行,以保证线程安全。
  • 每次脚本执行都有按脚本设置的超时时间;如果在超时时间内未调用 $done(),该脚本将被视为直通。
  • 使用 JSContext 池(大小 3)以提高性能;上下文在执行之间被重用。
  • 脚本编译默认缓存;使用 debug=true 可绕过缓存。
  • 在 iOS/tvOS 上,脚本受 10MB 内存限制;在 macOS 上为 512MB。
  • 远程脚本(HTTP/HTTPS 路径)在启动时获取,并可选择按 script-update-interval 秒重新获取。

模块脚本集成

脚本也可以在 Module 文件(.sgmodule)的 [Script] 部分中定义。

S. Smart Rabbit LLC © All Rights Reserved            updated 2026-06-28 02:09:17

results matching ""

    No results matching ""