/** * SakuraFRP Tunnel Provider * * 使用 SakuraFRP 提供隧道服务 * 用于没有公网 IP 的 Local 节点 * * 凭据就是控制台「配置文件」里那串启动参数 (`<访问密钥>:<隧道ID>`)。公网入口 * **不由用户声明**:控制台只让用户选节点和本地端口,访问地址是平台分配的,因此 * provider 用同一个凭据向 SakuraFrp 开放 API 查询分配结果(节点地址 + 远程端口), * 声明字段只作为 API 不可用时的可选兜底。 */ import type { TunnelProvider, TunnelConfig, TunnelSetupOptions, TunnelStatus } from './TunnelProvider'; /** * SakuraFRP Tunnel Provider 配置 */ export interface SakuraFrpTunnelProviderOptions { /** SakuraFRP 启动参数 (`<访问密钥>:<隧道ID>[,<隧道ID>...]`),来自 SAKURA_TUNNEL_TOKEN */ token: string; /** Optional declared endpoint, used only when the provider cannot be asked. */ publicUrl?: string; /** frpc 可执行文件路径 (默认 'frpc') */ frpcPath?: string; /** 等待代理发布的毫秒数;超时后状态为 failed */ connectTimeoutMs?: number; /** SakuraFRP 服务端地址 (如果需要自定义) */ serverAddr?: string; /** SakuraFrp open API base, where the assigned public entry can be read back. */ apiBaseUrl?: string; /** Injection point for tests; defaults to the global fetch. */ fetchImpl?: typeof fetch; } /** * SakuraFRP Tunnel Provider * * 通过 frpc 客户端连接 SakuraFRP 服务 */ export declare class SakuraFrpTunnelProvider implements TunnelProvider { readonly name = "sakura-frp"; private readonly logger; private readonly token; private readonly publicUrl?; private readonly frpcPath; private readonly connectTimeoutMs; private readonly serverAddr?; private readonly apiBaseUrl; private readonly fetchImpl; /** Endpoint the platform assigned, once discovery has answered. */ private discoveredEndpoint?; /** Credential actually handed to frpc, completed from the platform answer when needed. */ private clientCredential?; /** Why no browser-facing entry can be claimed, when the platform refuses plain HTTP. */ private discoveryBlockedReason?; /** Whether the platform terminates TLS for this entry (auto HTTPS). */ private autoHttpsEnabled; /** Entry the client itself printed, used when the platform API cannot name one. */ private logEndpoint?; private process; private status; private currentConfig; private managedByUs; constructor(options: SakuraFrpTunnelProviderOptions); /** * Reads back the entry the platform assigned to this tunnel. * * The console never asks for a domain: for a TCP tunnel it assigns a node host and a * remote port, and for a bound tunnel it assigns the domain itself. Asking the provider * is therefore the only honest way to learn the entry, and a failure to ask changes * nothing except that the status stops short of naming an endpoint. */ private discoverEndpoint; /** * Builds the parameter frpc needs, completing a bare access key with the tunnel it owns. * * `-f` accepts `<访问密钥>:<隧道ID>`; an operator who pasted only the access key would * otherwise get `Bad fetch parameter` even though discovery can read the tunnel with that * same key. Completion is only safe when the answer is unambiguous. */ private resolveClientCredential; private resolveNodeHost; private fetchJson; /** * The endpoint to report: what the platform assigned, else what the client printed, else * what was declared. Nothing is invented when no source names an entry. */ private currentEndpoint; /** * Setup: 解析 Token 获取配置 */ setup(_options: TunnelSetupOptions): Promise; /** * 启动 frpc 客户端 */ start(config?: TunnelConfig): Promise; private checkConnectionStatus; /** * 停止隧道 */ stop(): Promise; getStatus(): TunnelStatus; getEndpoint(): string | undefined; cleanup(_config: TunnelConfig): Promise; /** * 检测 frpc 是否已经在运行 */ private isFrpcRunning; private waitForConnection; isManagedByUs(): boolean; } /** * The local port the SakuraFrp console told the tunnel to forward to. * * The console already owns this fact, and the provider reads the same API for the public * entry, so the runtime must take the port from there instead of asking the operator to type * it a second time. */ export declare function resolveSakuraAssignedLocalPort(token: string | undefined, options?: { apiBaseUrl?: string; fetchImpl?: typeof fetch; }): Promise; /** Splits the console's startup parameter into the access key and the tunnel ids. */ export declare function parseSakuraCredential(value: string | undefined): { accessKey?: string; tunnelIds: string[]; };