@burdenoff/vibe-plugin-tunnel-cloudflare 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -0
- package/dist/index.d.ts +93 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +484 -0
- package/dist/index.js.map +1 -0
- package/package.json +69 -0
package/README.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# @burdenoff/vibe-plugin-tunnel-cloudflare
|
|
2
|
+
|
|
3
|
+
Cloudflare Tunnel provider for [VibeControls Agent](https://www.npmjs.com/package/@burdenoff/vibe-agent).
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
vibe plugin install @burdenoff/vibe-plugin-tunnel-cloudflare
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or install globally alongside the agent:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install -g @burdenoff/vibe-plugin-tunnel-cloudflare
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Features
|
|
18
|
+
|
|
19
|
+
- **Cloudflare Tunnels** -- Create and manage Cloudflare quick tunnels (no account required)
|
|
20
|
+
- **Auto-Start** -- Automatically starts an agent tunnel on server boot
|
|
21
|
+
- **URL Extraction** -- Extracts the tunnel URL from cloudflared output
|
|
22
|
+
- **Process Management** -- Graceful start/stop with process cleanup
|
|
23
|
+
- **Storage Persistence** -- Tunnel state persisted via agent KV storage
|
|
24
|
+
|
|
25
|
+
## Provider Interface
|
|
26
|
+
|
|
27
|
+
This plugin registers a `tunnel` provider with the following capabilities:
|
|
28
|
+
|
|
29
|
+
| Method | Description |
|
|
30
|
+
| --- | --- |
|
|
31
|
+
| `start(config)` | Start a new Cloudflare tunnel for a local port |
|
|
32
|
+
| `stop(id)` | Stop a running tunnel |
|
|
33
|
+
| `get(id)` | Get tunnel info by ID |
|
|
34
|
+
| `list()` | List all managed tunnels |
|
|
35
|
+
| `delete(id)` | Delete a tunnel record |
|
|
36
|
+
| `getStatus()` | Get overall tunnel provider status |
|
|
37
|
+
|
|
38
|
+
## Requirements
|
|
39
|
+
|
|
40
|
+
- VibeControls Agent >= 2.0.0
|
|
41
|
+
- `cloudflared` installed on the host system
|
|
42
|
+
- Bun runtime >= 1.3.0
|
|
43
|
+
|
|
44
|
+
## License
|
|
45
|
+
|
|
46
|
+
Proprietary -- Copyright Burdenoff Consultancy Services Pvt. Ltd.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @burdenoff/vibe-plugin-tunnel-cloudflare
|
|
3
|
+
*
|
|
4
|
+
* Cloudflare Tunnel provider plugin for VibeControls Agent.
|
|
5
|
+
* Spawns `cloudflared tunnel --url` processes to expose local ports via
|
|
6
|
+
* trycloudflare.com quick tunnels and manages their full lifecycle.
|
|
7
|
+
*
|
|
8
|
+
* Storage namespace: "tunnel-cloudflare"
|
|
9
|
+
* - "tunnels" → JSON array of TunnelInfo records
|
|
10
|
+
* - "agent-tunnel-url" → string | null (the main agent tunnel URL)
|
|
11
|
+
* - "agent-tunnel-pid" → string (PID of the agent tunnel process)
|
|
12
|
+
*/
|
|
13
|
+
type TunnelStatus = "starting" | "active" | "stopping" | "stopped" | "error";
|
|
14
|
+
interface TunnelConfig {
|
|
15
|
+
port: number;
|
|
16
|
+
hostname?: string;
|
|
17
|
+
protocol?: "http" | "https";
|
|
18
|
+
name?: string;
|
|
19
|
+
metadata?: Record<string, unknown>;
|
|
20
|
+
}
|
|
21
|
+
interface TunnelInfo {
|
|
22
|
+
id: string;
|
|
23
|
+
url: string;
|
|
24
|
+
port: number;
|
|
25
|
+
status: TunnelStatus;
|
|
26
|
+
provider: string;
|
|
27
|
+
pid?: number;
|
|
28
|
+
createdAt: string;
|
|
29
|
+
updatedAt?: string;
|
|
30
|
+
metadata?: Record<string, unknown>;
|
|
31
|
+
}
|
|
32
|
+
interface TunnelProvider {
|
|
33
|
+
readonly name: string;
|
|
34
|
+
start(config: TunnelConfig): Promise<TunnelInfo>;
|
|
35
|
+
stop(tunnelId: string): Promise<void>;
|
|
36
|
+
getStatus(tunnelId: string): Promise<TunnelInfo | null>;
|
|
37
|
+
getActiveTunnelUrl(): Promise<string | null>;
|
|
38
|
+
list(): Promise<TunnelInfo[]>;
|
|
39
|
+
delete(tunnelId: string): Promise<void>;
|
|
40
|
+
healthCheck(): Promise<{
|
|
41
|
+
ok: boolean;
|
|
42
|
+
message?: string;
|
|
43
|
+
}>;
|
|
44
|
+
}
|
|
45
|
+
interface StorageProvider {
|
|
46
|
+
get(namespace: string, key: string): Promise<string | null>;
|
|
47
|
+
set(namespace: string, key: string, value: string): Promise<void>;
|
|
48
|
+
delete(namespace: string, key: string): Promise<void>;
|
|
49
|
+
list(namespace: string): Promise<string[]>;
|
|
50
|
+
deleteAll(namespace: string): Promise<void>;
|
|
51
|
+
}
|
|
52
|
+
interface Logger {
|
|
53
|
+
debug(message: string, ...args: unknown[]): void;
|
|
54
|
+
info(message: string, ...args: unknown[]): void;
|
|
55
|
+
warn(message: string, ...args: unknown[]): void;
|
|
56
|
+
error(message: string, ...args: unknown[]): void;
|
|
57
|
+
}
|
|
58
|
+
interface ServiceRegistry {
|
|
59
|
+
[key: string]: unknown;
|
|
60
|
+
}
|
|
61
|
+
interface HostServices {
|
|
62
|
+
storage: StorageProvider;
|
|
63
|
+
logger: Logger;
|
|
64
|
+
serviceRegistry: ServiceRegistry;
|
|
65
|
+
getProvider<T>(type: "tunnel" | "session"): T | undefined;
|
|
66
|
+
getAgentBaseUrl(): string;
|
|
67
|
+
getAgentVersion(): string;
|
|
68
|
+
}
|
|
69
|
+
type Elysia = unknown;
|
|
70
|
+
type Command = unknown;
|
|
71
|
+
interface SessionProvider {
|
|
72
|
+
[key: string]: unknown;
|
|
73
|
+
}
|
|
74
|
+
interface VibePlugin {
|
|
75
|
+
name: string;
|
|
76
|
+
version: string;
|
|
77
|
+
description?: string;
|
|
78
|
+
tags?: Array<"backend" | "frontend" | "cli" | "provider" | "adapter" | "integration">;
|
|
79
|
+
cliCommand?: string;
|
|
80
|
+
apiPrefix?: string;
|
|
81
|
+
dependencies?: string[];
|
|
82
|
+
providers?: {
|
|
83
|
+
tunnel?: TunnelProvider;
|
|
84
|
+
session?: SessionProvider;
|
|
85
|
+
};
|
|
86
|
+
onCliSetup?: (program: Command, hostServices: HostServices) => void | Promise<void>;
|
|
87
|
+
onServerStart?: (app: Elysia, hostServices: HostServices) => void | Promise<void>;
|
|
88
|
+
onServerReady?: (app: Elysia, hostServices: HostServices) => void | Promise<void>;
|
|
89
|
+
onServerStop?: () => void | Promise<void>;
|
|
90
|
+
}
|
|
91
|
+
export declare const vibePlugin: VibePlugin;
|
|
92
|
+
export {};
|
|
93
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @burdenoff/vibe-plugin-tunnel-cloudflare
|
|
3
|
+
*
|
|
4
|
+
* Cloudflare Tunnel provider plugin for VibeControls Agent.
|
|
5
|
+
* Spawns `cloudflared tunnel --url` processes to expose local ports via
|
|
6
|
+
* trycloudflare.com quick tunnels and manages their full lifecycle.
|
|
7
|
+
*
|
|
8
|
+
* Storage namespace: "tunnel-cloudflare"
|
|
9
|
+
* - "tunnels" → JSON array of TunnelInfo records
|
|
10
|
+
* - "agent-tunnel-url" → string | null (the main agent tunnel URL)
|
|
11
|
+
* - "agent-tunnel-pid" → string (PID of the agent tunnel process)
|
|
12
|
+
*/
|
|
13
|
+
import { spawn, execSync } from "node:child_process";
|
|
14
|
+
import { randomUUID } from "node:crypto";
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// Constants
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
const PROVIDER_NAME = "tunnel-cloudflare";
|
|
19
|
+
const STORAGE_NS = "tunnel-cloudflare";
|
|
20
|
+
/** Key under which the full tunnel list is persisted. */
|
|
21
|
+
const KEY_TUNNELS = "tunnels";
|
|
22
|
+
/** Key for the active agent tunnel URL. */
|
|
23
|
+
const KEY_AGENT_URL = "agent-tunnel-url";
|
|
24
|
+
/** Key for the agent tunnel process PID. */
|
|
25
|
+
const KEY_AGENT_PID = "agent-tunnel-pid";
|
|
26
|
+
/** Regex to extract the quick-tunnel URL from cloudflared output. */
|
|
27
|
+
const TUNNEL_URL_RE = /(https:\/\/[a-zA-Z0-9-]+\.trycloudflare\.com)/;
|
|
28
|
+
/** Maximum time (ms) to wait for cloudflared to print its URL. */
|
|
29
|
+
const URL_EXTRACT_TIMEOUT_MS = 30_000;
|
|
30
|
+
/** Grace period (ms) between SIGTERM and SIGKILL during shutdown. */
|
|
31
|
+
const KILL_GRACE_MS = 3_000;
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
// Helpers
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
/**
|
|
36
|
+
* Returns `true` if a process with the given PID is still running.
|
|
37
|
+
*/
|
|
38
|
+
function isProcessAlive(pid) {
|
|
39
|
+
try {
|
|
40
|
+
// Sending signal 0 doesn't kill the process — it just checks existence.
|
|
41
|
+
process.kill(pid, 0);
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Gracefully terminate a process: SIGTERM first, then SIGKILL after a timeout.
|
|
50
|
+
*/
|
|
51
|
+
async function gracefulKill(proc, pid) {
|
|
52
|
+
// Already dead — nothing to do.
|
|
53
|
+
if (!isProcessAlive(pid))
|
|
54
|
+
return;
|
|
55
|
+
proc.kill("SIGTERM");
|
|
56
|
+
await new Promise((resolve) => {
|
|
57
|
+
const timer = setTimeout(() => {
|
|
58
|
+
if (isProcessAlive(pid)) {
|
|
59
|
+
try {
|
|
60
|
+
process.kill(pid, "SIGKILL");
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// Already gone — ignore.
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
resolve();
|
|
67
|
+
}, KILL_GRACE_MS);
|
|
68
|
+
// If the process exits before the timeout we can resolve early.
|
|
69
|
+
proc.once("exit", () => {
|
|
70
|
+
clearTimeout(timer);
|
|
71
|
+
resolve();
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Spawn `cloudflared tunnel` and wait for the quick-tunnel URL to appear on
|
|
77
|
+
* stderr/stdout.
|
|
78
|
+
*
|
|
79
|
+
* IMPORTANT: After URL extraction we **resume** the stdio streams but do NOT
|
|
80
|
+
* destroy them. Destroying piped streams sends SIGPIPE to the child which
|
|
81
|
+
* would kill the tunnel process.
|
|
82
|
+
*/
|
|
83
|
+
function extractTunnelUrl(proc) {
|
|
84
|
+
return new Promise((resolve, reject) => {
|
|
85
|
+
let settled = false;
|
|
86
|
+
let combined = "";
|
|
87
|
+
const timeout = setTimeout(() => {
|
|
88
|
+
if (!settled) {
|
|
89
|
+
settled = true;
|
|
90
|
+
cleanup();
|
|
91
|
+
reject(new Error(`Timed out after ${URL_EXTRACT_TIMEOUT_MS}ms waiting for tunnel URL`));
|
|
92
|
+
}
|
|
93
|
+
}, URL_EXTRACT_TIMEOUT_MS);
|
|
94
|
+
const onData = (chunk) => {
|
|
95
|
+
if (settled)
|
|
96
|
+
return;
|
|
97
|
+
combined += chunk.toString();
|
|
98
|
+
const match = TUNNEL_URL_RE.exec(combined);
|
|
99
|
+
if (match) {
|
|
100
|
+
settled = true;
|
|
101
|
+
clearTimeout(timeout);
|
|
102
|
+
cleanup();
|
|
103
|
+
resolve(match[1]);
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
const onError = (err) => {
|
|
107
|
+
if (!settled) {
|
|
108
|
+
settled = true;
|
|
109
|
+
clearTimeout(timeout);
|
|
110
|
+
cleanup();
|
|
111
|
+
reject(err);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
const onExit = (code) => {
|
|
115
|
+
if (!settled) {
|
|
116
|
+
settled = true;
|
|
117
|
+
clearTimeout(timeout);
|
|
118
|
+
cleanup();
|
|
119
|
+
reject(new Error(`cloudflared exited with code ${code} before producing a URL`));
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
/**
|
|
123
|
+
* Remove all listeners we attached and resume the streams so they don't
|
|
124
|
+
* buffer indefinitely. We intentionally do NOT destroy() the streams.
|
|
125
|
+
*/
|
|
126
|
+
function cleanup() {
|
|
127
|
+
proc.stdout?.removeListener("data", onData);
|
|
128
|
+
proc.stderr?.removeListener("data", onData);
|
|
129
|
+
proc.removeListener("error", onError);
|
|
130
|
+
proc.removeListener("exit", onExit);
|
|
131
|
+
// Resume streams so the child process doesn't block on write().
|
|
132
|
+
proc.stdout?.resume();
|
|
133
|
+
proc.stderr?.resume();
|
|
134
|
+
}
|
|
135
|
+
proc.stdout?.on("data", onData);
|
|
136
|
+
proc.stderr?.on("data", onData);
|
|
137
|
+
proc.once("error", onError);
|
|
138
|
+
proc.once("exit", onExit);
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
// ---------------------------------------------------------------------------
|
|
142
|
+
// CloudflareTunnelProvider
|
|
143
|
+
// ---------------------------------------------------------------------------
|
|
144
|
+
class CloudflareTunnelProvider {
|
|
145
|
+
hostServices;
|
|
146
|
+
name = PROVIDER_NAME;
|
|
147
|
+
/** In-memory map of tunnel ID → spawned ChildProcess. */
|
|
148
|
+
processes = new Map();
|
|
149
|
+
storage;
|
|
150
|
+
log;
|
|
151
|
+
constructor(hostServices) {
|
|
152
|
+
this.hostServices = hostServices;
|
|
153
|
+
this.storage = hostServices.storage;
|
|
154
|
+
this.log = hostServices.logger;
|
|
155
|
+
}
|
|
156
|
+
// -----------------------------------------------------------------------
|
|
157
|
+
// Storage helpers
|
|
158
|
+
// -----------------------------------------------------------------------
|
|
159
|
+
/** Load the persisted tunnel list. */
|
|
160
|
+
async loadTunnels() {
|
|
161
|
+
const raw = await this.storage.get(STORAGE_NS, KEY_TUNNELS);
|
|
162
|
+
if (!raw)
|
|
163
|
+
return [];
|
|
164
|
+
try {
|
|
165
|
+
return JSON.parse(raw);
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
this.log.warn("[tunnel-cloudflare] Corrupt tunnel list in storage — resetting");
|
|
169
|
+
return [];
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/** Persist the tunnel list. */
|
|
173
|
+
async saveTunnels(tunnels) {
|
|
174
|
+
await this.storage.set(STORAGE_NS, KEY_TUNNELS, JSON.stringify(tunnels));
|
|
175
|
+
}
|
|
176
|
+
/** Update a single tunnel record in the persisted list. */
|
|
177
|
+
async upsertTunnel(info) {
|
|
178
|
+
const tunnels = await this.loadTunnels();
|
|
179
|
+
const idx = tunnels.findIndex((t) => t.id === info.id);
|
|
180
|
+
if (idx >= 0) {
|
|
181
|
+
tunnels[idx] = info;
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
tunnels.push(info);
|
|
185
|
+
}
|
|
186
|
+
await this.saveTunnels(tunnels);
|
|
187
|
+
}
|
|
188
|
+
/** Remove a tunnel record from the persisted list. */
|
|
189
|
+
async removeTunnelRecord(tunnelId) {
|
|
190
|
+
const tunnels = await this.loadTunnels();
|
|
191
|
+
await this.saveTunnels(tunnels.filter((t) => t.id !== tunnelId));
|
|
192
|
+
}
|
|
193
|
+
// -----------------------------------------------------------------------
|
|
194
|
+
// TunnelProvider implementation
|
|
195
|
+
// -----------------------------------------------------------------------
|
|
196
|
+
async start(config) {
|
|
197
|
+
const id = randomUUID();
|
|
198
|
+
const protocol = config.protocol ?? "http";
|
|
199
|
+
const localUrl = `${protocol}://${config.hostname ?? "localhost"}:${config.port}`;
|
|
200
|
+
const now = new Date().toISOString();
|
|
201
|
+
const info = {
|
|
202
|
+
id,
|
|
203
|
+
url: "", // Populated after URL extraction.
|
|
204
|
+
port: config.port,
|
|
205
|
+
status: "starting",
|
|
206
|
+
provider: PROVIDER_NAME,
|
|
207
|
+
createdAt: now,
|
|
208
|
+
metadata: {
|
|
209
|
+
...(config.metadata ?? {}),
|
|
210
|
+
...(config.name ? { name: config.name } : {}),
|
|
211
|
+
localUrl,
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
// Persist the "starting" record immediately so callers can track it.
|
|
215
|
+
await this.upsertTunnel(info);
|
|
216
|
+
this.log.info(`[tunnel-cloudflare] Starting tunnel ${id} → ${localUrl}`);
|
|
217
|
+
// Spawn cloudflared as a detached process so it survives short-lived
|
|
218
|
+
// parent re-spawns. stdio is piped so we can capture the URL.
|
|
219
|
+
const proc = spawn("cloudflared", ["tunnel", "--url", localUrl], {
|
|
220
|
+
detached: true,
|
|
221
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
222
|
+
});
|
|
223
|
+
// Prevent the parent from waiting on the child.
|
|
224
|
+
proc.unref();
|
|
225
|
+
// Track the process in-memory.
|
|
226
|
+
this.processes.set(id, proc);
|
|
227
|
+
// Handle unexpected early death.
|
|
228
|
+
proc.once("exit", (code) => {
|
|
229
|
+
// Only act if the tunnel wasn't intentionally stopped (still in map).
|
|
230
|
+
if (this.processes.has(id)) {
|
|
231
|
+
this.log.warn(`[tunnel-cloudflare] Tunnel ${id} process exited unexpectedly (code=${code})`);
|
|
232
|
+
this.processes.delete(id);
|
|
233
|
+
// Fire-and-forget status update.
|
|
234
|
+
const errorInfo = {
|
|
235
|
+
...info,
|
|
236
|
+
status: "error",
|
|
237
|
+
updatedAt: new Date().toISOString(),
|
|
238
|
+
metadata: {
|
|
239
|
+
...info.metadata,
|
|
240
|
+
exitCode: code,
|
|
241
|
+
},
|
|
242
|
+
};
|
|
243
|
+
void this.upsertTunnel(errorInfo);
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
try {
|
|
247
|
+
const url = await extractTunnelUrl(proc);
|
|
248
|
+
const pid = proc.pid;
|
|
249
|
+
const activeInfo = {
|
|
250
|
+
...info,
|
|
251
|
+
url,
|
|
252
|
+
status: "active",
|
|
253
|
+
pid: pid ?? undefined,
|
|
254
|
+
updatedAt: new Date().toISOString(),
|
|
255
|
+
};
|
|
256
|
+
await this.upsertTunnel(activeInfo);
|
|
257
|
+
this.log.info(`[tunnel-cloudflare] Tunnel ${id} active at ${url} (PID ${pid})`);
|
|
258
|
+
return activeInfo;
|
|
259
|
+
}
|
|
260
|
+
catch (err) {
|
|
261
|
+
// URL extraction failed — kill the process and record error state.
|
|
262
|
+
this.processes.delete(id);
|
|
263
|
+
if (proc.pid && isProcessAlive(proc.pid)) {
|
|
264
|
+
await gracefulKill(proc, proc.pid);
|
|
265
|
+
}
|
|
266
|
+
const errorInfo = {
|
|
267
|
+
...info,
|
|
268
|
+
status: "error",
|
|
269
|
+
updatedAt: new Date().toISOString(),
|
|
270
|
+
metadata: {
|
|
271
|
+
...info.metadata,
|
|
272
|
+
error: err instanceof Error ? err.message : String(err),
|
|
273
|
+
},
|
|
274
|
+
};
|
|
275
|
+
await this.upsertTunnel(errorInfo);
|
|
276
|
+
throw err;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
async stop(tunnelId) {
|
|
280
|
+
this.log.info(`[tunnel-cloudflare] Stopping tunnel ${tunnelId}`);
|
|
281
|
+
const tunnels = await this.loadTunnels();
|
|
282
|
+
const tunnel = tunnels.find((t) => t.id === tunnelId);
|
|
283
|
+
// Update status to "stopping".
|
|
284
|
+
if (tunnel) {
|
|
285
|
+
tunnel.status = "stopping";
|
|
286
|
+
tunnel.updatedAt = new Date().toISOString();
|
|
287
|
+
await this.upsertTunnel(tunnel);
|
|
288
|
+
}
|
|
289
|
+
// Kill the in-memory process if we still have a handle.
|
|
290
|
+
const proc = this.processes.get(tunnelId);
|
|
291
|
+
if (proc?.pid) {
|
|
292
|
+
await gracefulKill(proc, proc.pid);
|
|
293
|
+
this.processes.delete(tunnelId);
|
|
294
|
+
}
|
|
295
|
+
else if (tunnel?.pid && isProcessAlive(tunnel.pid)) {
|
|
296
|
+
// Fallback: we lost the ChildProcess handle but the PID is still alive
|
|
297
|
+
// (e.g. after an agent restart). Kill directly by PID.
|
|
298
|
+
try {
|
|
299
|
+
process.kill(tunnel.pid, "SIGTERM");
|
|
300
|
+
await new Promise((resolve) => {
|
|
301
|
+
setTimeout(() => {
|
|
302
|
+
if (tunnel.pid && isProcessAlive(tunnel.pid)) {
|
|
303
|
+
try {
|
|
304
|
+
process.kill(tunnel.pid, "SIGKILL");
|
|
305
|
+
}
|
|
306
|
+
catch {
|
|
307
|
+
// Already gone.
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
resolve();
|
|
311
|
+
}, KILL_GRACE_MS);
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
catch {
|
|
315
|
+
// Process already gone — no action needed.
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
// Mark as stopped in storage.
|
|
319
|
+
if (tunnel) {
|
|
320
|
+
tunnel.status = "stopped";
|
|
321
|
+
tunnel.pid = undefined;
|
|
322
|
+
tunnel.updatedAt = new Date().toISOString();
|
|
323
|
+
await this.upsertTunnel(tunnel);
|
|
324
|
+
}
|
|
325
|
+
// If this was the agent tunnel, clear the agent-specific keys.
|
|
326
|
+
const agentPidRaw = await this.storage.get(STORAGE_NS, KEY_AGENT_PID);
|
|
327
|
+
if (agentPidRaw && tunnel?.pid && String(tunnel.pid) === agentPidRaw) {
|
|
328
|
+
await this.storage.delete(STORAGE_NS, KEY_AGENT_URL);
|
|
329
|
+
await this.storage.delete(STORAGE_NS, KEY_AGENT_PID);
|
|
330
|
+
}
|
|
331
|
+
this.log.info(`[tunnel-cloudflare] Tunnel ${tunnelId} stopped`);
|
|
332
|
+
}
|
|
333
|
+
async getStatus(tunnelId) {
|
|
334
|
+
const tunnels = await this.loadTunnels();
|
|
335
|
+
const tunnel = tunnels.find((t) => t.id === tunnelId) ?? null;
|
|
336
|
+
if (!tunnel)
|
|
337
|
+
return null;
|
|
338
|
+
// Verify the process is actually alive if the status says "active".
|
|
339
|
+
if (tunnel.status === "active" && tunnel.pid) {
|
|
340
|
+
if (!isProcessAlive(tunnel.pid)) {
|
|
341
|
+
tunnel.status = "stopped";
|
|
342
|
+
tunnel.updatedAt = new Date().toISOString();
|
|
343
|
+
await this.upsertTunnel(tunnel);
|
|
344
|
+
this.processes.delete(tunnelId);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return tunnel;
|
|
348
|
+
}
|
|
349
|
+
async getActiveTunnelUrl() {
|
|
350
|
+
return this.storage.get(STORAGE_NS, KEY_AGENT_URL);
|
|
351
|
+
}
|
|
352
|
+
async list() {
|
|
353
|
+
return this.loadTunnels();
|
|
354
|
+
}
|
|
355
|
+
async delete(tunnelId) {
|
|
356
|
+
this.log.info(`[tunnel-cloudflare] Deleting tunnel ${tunnelId}`);
|
|
357
|
+
// Stop the process if it's still running.
|
|
358
|
+
const tunnels = await this.loadTunnels();
|
|
359
|
+
const tunnel = tunnels.find((t) => t.id === tunnelId);
|
|
360
|
+
if (tunnel &&
|
|
361
|
+
(tunnel.status === "active" || tunnel.status === "starting")) {
|
|
362
|
+
await this.stop(tunnelId);
|
|
363
|
+
}
|
|
364
|
+
// Remove the record entirely from storage.
|
|
365
|
+
await this.removeTunnelRecord(tunnelId);
|
|
366
|
+
this.log.info(`[tunnel-cloudflare] Tunnel ${tunnelId} deleted`);
|
|
367
|
+
}
|
|
368
|
+
async healthCheck() {
|
|
369
|
+
try {
|
|
370
|
+
const version = execSync("cloudflared --version", {
|
|
371
|
+
encoding: "utf-8",
|
|
372
|
+
timeout: 10_000,
|
|
373
|
+
}).trim();
|
|
374
|
+
return { ok: true, message: version };
|
|
375
|
+
}
|
|
376
|
+
catch (err) {
|
|
377
|
+
return {
|
|
378
|
+
ok: false,
|
|
379
|
+
message: err instanceof Error
|
|
380
|
+
? `cloudflared not available: ${err.message}`
|
|
381
|
+
: "cloudflared not available",
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
// -----------------------------------------------------------------------
|
|
386
|
+
// Agent tunnel helpers
|
|
387
|
+
// -----------------------------------------------------------------------
|
|
388
|
+
/**
|
|
389
|
+
* Start the "agent tunnel" — the primary tunnel pointing at the agent's
|
|
390
|
+
* own HTTP port so the agent is reachable from the internet.
|
|
391
|
+
*/
|
|
392
|
+
async startAgentTunnel(agentPort) {
|
|
393
|
+
this.log.info(`[tunnel-cloudflare] Starting agent tunnel on port ${agentPort}`);
|
|
394
|
+
const info = await this.start({
|
|
395
|
+
port: agentPort,
|
|
396
|
+
name: "agent",
|
|
397
|
+
metadata: { isAgentTunnel: true },
|
|
398
|
+
});
|
|
399
|
+
// Persist agent-specific keys for quick lookup.
|
|
400
|
+
await this.storage.set(STORAGE_NS, KEY_AGENT_URL, info.url);
|
|
401
|
+
if (info.pid !== undefined) {
|
|
402
|
+
await this.storage.set(STORAGE_NS, KEY_AGENT_PID, String(info.pid));
|
|
403
|
+
}
|
|
404
|
+
this.log.info(`[tunnel-cloudflare] Agent tunnel active at ${info.url}`);
|
|
405
|
+
return info;
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Tear down every tunnel this provider is tracking and clear storage.
|
|
409
|
+
*/
|
|
410
|
+
async stopAll() {
|
|
411
|
+
this.log.info("[tunnel-cloudflare] Stopping all tunnels");
|
|
412
|
+
const tunnels = await this.loadTunnels();
|
|
413
|
+
// Stop all active/starting tunnels in parallel.
|
|
414
|
+
await Promise.allSettled(tunnels
|
|
415
|
+
.filter((t) => t.status === "active" || t.status === "starting")
|
|
416
|
+
.map((t) => this.stop(t.id)));
|
|
417
|
+
// Kill any lingering in-memory processes that weren't in the persisted
|
|
418
|
+
// list (defensive — shouldn't normally happen).
|
|
419
|
+
for (const [id, proc] of this.processes) {
|
|
420
|
+
if (proc.pid && isProcessAlive(proc.pid)) {
|
|
421
|
+
this.log.warn(`[tunnel-cloudflare] Killing orphaned process ${proc.pid} (tunnel ${id})`);
|
|
422
|
+
await gracefulKill(proc, proc.pid);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
this.processes.clear();
|
|
426
|
+
// Wipe all storage keys for a clean slate.
|
|
427
|
+
await this.storage.deleteAll(STORAGE_NS);
|
|
428
|
+
this.log.info("[tunnel-cloudflare] All tunnels stopped and storage cleared");
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
// ---------------------------------------------------------------------------
|
|
432
|
+
// Plugin export
|
|
433
|
+
// ---------------------------------------------------------------------------
|
|
434
|
+
/** Singleton provider instance — initialised in onServerStart. */
|
|
435
|
+
let provider = null;
|
|
436
|
+
export const vibePlugin = {
|
|
437
|
+
name: "tunnel-cloudflare",
|
|
438
|
+
version: "1.0.0",
|
|
439
|
+
description: "Cloudflare Tunnel provider for remote access",
|
|
440
|
+
tags: ["backend", "provider"],
|
|
441
|
+
// The `tunnel` slot is populated at runtime during onServerStart.
|
|
442
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
443
|
+
providers: { tunnel: undefined },
|
|
444
|
+
async onServerStart(_app, hostServices) {
|
|
445
|
+
const log = hostServices.logger;
|
|
446
|
+
log.info("[tunnel-cloudflare] Plugin initialising");
|
|
447
|
+
// Create the provider and wire it into the plugin's providers bag.
|
|
448
|
+
provider = new CloudflareTunnelProvider(hostServices);
|
|
449
|
+
vibePlugin.providers.tunnel = provider;
|
|
450
|
+
// Pre-flight: make sure cloudflared is available.
|
|
451
|
+
const health = await provider.healthCheck();
|
|
452
|
+
if (!health.ok) {
|
|
453
|
+
log.warn(`[tunnel-cloudflare] cloudflared is not available — tunnel features will fail. ${health.message ?? ""}`);
|
|
454
|
+
// We don't throw here; the plugin still loads so other features work.
|
|
455
|
+
// Individual start() calls will fail with a clear error.
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
log.info(`[tunnel-cloudflare] ${health.message}`);
|
|
459
|
+
// Start the agent tunnel. Parse the port from the agent's own base URL
|
|
460
|
+
// (e.g. "http://localhost:4100" → 4100).
|
|
461
|
+
try {
|
|
462
|
+
const baseUrl = hostServices.getAgentBaseUrl();
|
|
463
|
+
const parsed = new URL(baseUrl);
|
|
464
|
+
const agentPort = parseInt(parsed.port, 10);
|
|
465
|
+
if (!Number.isFinite(agentPort) || agentPort <= 0) {
|
|
466
|
+
log.warn(`[tunnel-cloudflare] Cannot determine agent port from "${baseUrl}" — skipping agent tunnel`);
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
await provider.startAgentTunnel(agentPort);
|
|
470
|
+
}
|
|
471
|
+
catch (err) {
|
|
472
|
+
// Non-fatal: the agent continues to work on localhost even if the
|
|
473
|
+
// tunnel couldn't be established.
|
|
474
|
+
log.error(`[tunnel-cloudflare] Failed to start agent tunnel: ${err instanceof Error ? err.message : String(err)}`);
|
|
475
|
+
}
|
|
476
|
+
},
|
|
477
|
+
async onServerStop() {
|
|
478
|
+
if (!provider)
|
|
479
|
+
return;
|
|
480
|
+
await provider.stopAll();
|
|
481
|
+
provider = null;
|
|
482
|
+
},
|
|
483
|
+
};
|
|
484
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@burdenoff/vibe-plugin-tunnel-cloudflare",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"description": "Cloudflare Tunnel provider for VibeControls Agent",
|
|
7
|
+
"engines": {
|
|
8
|
+
"bun": ">=1.3.0"
|
|
9
|
+
},
|
|
10
|
+
"author": {
|
|
11
|
+
"name": "Vignesh T.V",
|
|
12
|
+
"email": "vignesh@burdenoff.com",
|
|
13
|
+
"url": "https://github.com/tvvignesh"
|
|
14
|
+
},
|
|
15
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc",
|
|
18
|
+
"dev": "tsc --watch",
|
|
19
|
+
"lint": "eslint ./src",
|
|
20
|
+
"format": "bunx prettier . --write",
|
|
21
|
+
"format:check": "bunx prettier . --check",
|
|
22
|
+
"type:check": "tsc --noEmit",
|
|
23
|
+
"clean": "rimraf dist",
|
|
24
|
+
"prebuild": "bun run clean",
|
|
25
|
+
"prepublishOnly": "bun run build",
|
|
26
|
+
"sanity": "bun run format:check && bun run lint && bun run type:check && bun run build"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/node": "^22.0.0",
|
|
31
|
+
"eslint": "^9.30.1",
|
|
32
|
+
"prettier": "^3.6.2",
|
|
33
|
+
"rimraf": "^6.0.1",
|
|
34
|
+
"typescript": "^5.9.3"
|
|
35
|
+
},
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"@burdenoff/vibe-agent": ">=2.0.0"
|
|
38
|
+
},
|
|
39
|
+
"peerDependenciesMeta": {
|
|
40
|
+
"@burdenoff/vibe-agent": {
|
|
41
|
+
"optional": true
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"keywords": [
|
|
45
|
+
"vibecontrols",
|
|
46
|
+
"vibe",
|
|
47
|
+
"vibe-plugin",
|
|
48
|
+
"cloudflare",
|
|
49
|
+
"tunnel",
|
|
50
|
+
"bun"
|
|
51
|
+
],
|
|
52
|
+
"repository": {
|
|
53
|
+
"type": "git",
|
|
54
|
+
"url": "git+https://github.com/algoshred/vibe-plugin-tunnel-cloudflare.git"
|
|
55
|
+
},
|
|
56
|
+
"bugs": {
|
|
57
|
+
"url": "https://github.com/algoshred/vibe-plugin-tunnel-cloudflare/issues"
|
|
58
|
+
},
|
|
59
|
+
"homepage": "https://vibecontrols.com",
|
|
60
|
+
"publishConfig": {
|
|
61
|
+
"access": "public",
|
|
62
|
+
"registry": "https://registry.npmjs.org/"
|
|
63
|
+
},
|
|
64
|
+
"files": [
|
|
65
|
+
"dist/**/*",
|
|
66
|
+
"README.md",
|
|
67
|
+
"LICENSE"
|
|
68
|
+
]
|
|
69
|
+
}
|