sub-bridge 1.0.5 → 1.1.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.
@@ -1,9 +1,15 @@
1
- import type { TunnelProvider, TunnelInstance } from '../types';
1
+ import type { TunnelProvider, TunnelInstance, NamedTunnelInfo } from '../types';
2
2
  export declare class CloudflareTunnelProvider implements TunnelProvider {
3
3
  id: string;
4
4
  name: string;
5
5
  supportsNamedTunnels: boolean;
6
6
  isAvailable(): Promise<boolean>;
7
+ isAuthenticated(): Promise<boolean>;
8
+ listTunnels(): Promise<NamedTunnelInfo[]>;
9
+ private getTunnelHostname;
10
+ private runCloudflaredCommand;
7
11
  start(localPort: number, namedUrl?: string): Promise<TunnelInstance>;
12
+ private startNamedTunnel;
13
+ private startQuickTunnel;
8
14
  }
9
15
  //# sourceMappingURL=cloudflare.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cloudflare.d.ts","sourceRoot":"","sources":["../../../src/tunnel/providers/cloudflare.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAE9D,qBAAa,wBAAyB,YAAW,cAAc;IAC7D,EAAE,SAAe;IACjB,IAAI,SAAe;IACnB,oBAAoB,UAAO;IAErB,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAK/B,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;CAkF3E"}
1
+ {"version":3,"file":"cloudflare.d.ts","sourceRoot":"","sources":["../../../src/tunnel/providers/cloudflare.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAS/E,qBAAa,wBAAyB,YAAW,cAAc;IAC7D,EAAE,SAAe;IACjB,IAAI,SAAe;IACnB,oBAAoB,UAAO;IAErB,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAK/B,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC;IAUnC,WAAW,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;YA0BjC,iBAAiB;IAiB/B,OAAO,CAAC,qBAAqB;IAwBvB,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;YAO5D,gBAAgB;YAwFhB,gBAAgB;CAwE/B"}
@@ -8,9 +8,16 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.CloudflareTunnelProvider = void 0;
10
10
  const cloudflared_1 = require("cloudflared");
11
+ const node_child_process_1 = require("node:child_process");
11
12
  const promises_1 = __importDefault(require("node:fs/promises"));
12
13
  const node_os_1 = __importDefault(require("node:os"));
13
14
  const node_path_1 = __importDefault(require("node:path"));
15
+ function getCloudflaredConfigDir() {
16
+ if (process.platform === 'win32') {
17
+ return node_path_1.default.join(process.env.USERPROFILE || node_os_1.default.homedir(), '.cloudflared');
18
+ }
19
+ return node_path_1.default.join(node_os_1.default.homedir(), '.cloudflared');
20
+ }
14
21
  class CloudflareTunnelProvider {
15
22
  id = 'cloudflare';
16
23
  name = 'Cloudflare';
@@ -19,16 +26,155 @@ class CloudflareTunnelProvider {
19
26
  // The cloudflared npm package auto-installs the binary
20
27
  return true;
21
28
  }
29
+ async isAuthenticated() {
30
+ const certPath = node_path_1.default.join(getCloudflaredConfigDir(), 'cert.pem');
31
+ try {
32
+ await promises_1.default.access(certPath);
33
+ return true;
34
+ }
35
+ catch {
36
+ return false;
37
+ }
38
+ }
39
+ async listTunnels() {
40
+ if (!await this.isAuthenticated()) {
41
+ return [];
42
+ }
43
+ try {
44
+ // Run cloudflared tunnel list and parse output
45
+ const output = await this.runCloudflaredCommand(['tunnel', 'list', '--output', 'json']);
46
+ const tunnels = JSON.parse(output);
47
+ // Get DNS routes for each tunnel
48
+ const results = [];
49
+ for (const tunnel of tunnels) {
50
+ const hostname = await this.getTunnelHostname(tunnel.id);
51
+ results.push({
52
+ id: tunnel.id,
53
+ name: tunnel.name,
54
+ hostname,
55
+ });
56
+ }
57
+ return results;
58
+ }
59
+ catch {
60
+ return [];
61
+ }
62
+ }
63
+ async getTunnelHostname(tunnelId) {
64
+ try {
65
+ // Check config.yml for ingress rules
66
+ const configPath = node_path_1.default.join(getCloudflaredConfigDir(), 'config.yml');
67
+ const config = await promises_1.default.readFile(configPath, 'utf-8');
68
+ // Simple regex to extract hostname from ingress rules
69
+ const hostnameMatch = config.match(/hostname:\s*(\S+)/);
70
+ if (hostnameMatch) {
71
+ return hostnameMatch[1];
72
+ }
73
+ }
74
+ catch {
75
+ // Config file doesn't exist or is unreadable
76
+ }
77
+ return undefined;
78
+ }
79
+ runCloudflaredCommand(args) {
80
+ return new Promise((resolve, reject) => {
81
+ const proc = (0, node_child_process_1.spawn)('cloudflared', args, {
82
+ stdio: ['ignore', 'pipe', 'pipe'],
83
+ });
84
+ let stdout = '';
85
+ let stderr = '';
86
+ proc.stdout.on('data', (data) => { stdout += data.toString(); });
87
+ proc.stderr.on('data', (data) => { stderr += data.toString(); });
88
+ proc.on('close', (code) => {
89
+ if (code === 0) {
90
+ resolve(stdout);
91
+ }
92
+ else {
93
+ reject(new Error(stderr || `cloudflared exited with code ${code}`));
94
+ }
95
+ });
96
+ proc.on('error', reject);
97
+ });
98
+ }
22
99
  async start(localPort, namedUrl) {
23
100
  if (namedUrl) {
24
- // Named tunnel: user provides their own tunnel hostname
25
- // They need to configure this in Cloudflare dashboard and run cloudflared separately
26
- return {
27
- providerId: this.id,
28
- publicUrl: namedUrl.startsWith('https://') ? namedUrl : `https://${namedUrl}`,
29
- stop: () => { }
30
- };
101
+ return this.startNamedTunnel(localPort, namedUrl);
102
+ }
103
+ return this.startQuickTunnel(localPort);
104
+ }
105
+ async startNamedTunnel(localPort, namedUrl) {
106
+ // Check if authenticated
107
+ if (!await this.isAuthenticated()) {
108
+ throw new Error('Cloudflare authentication required. Run: cloudflared tunnel login');
109
+ }
110
+ // Find the tunnel info
111
+ const tunnels = await this.listTunnels();
112
+ const tunnel = tunnels.find(t => t.name === namedUrl ||
113
+ t.hostname === namedUrl ||
114
+ t.id === namedUrl);
115
+ if (!tunnel) {
116
+ throw new Error(`Tunnel not found: ${namedUrl}. Available: ${tunnels.map(t => t.name).join(', ') || 'none'}`);
31
117
  }
118
+ if (!tunnel.hostname) {
119
+ throw new Error(`Tunnel "${tunnel.name}" has no DNS route configured. Configure one in Cloudflare dashboard.`);
120
+ }
121
+ // Create temp config with the requested port
122
+ const tmpDir = await promises_1.default.mkdtemp(node_path_1.default.join(node_os_1.default.tmpdir(), 'sub-bridge-cloudflared-'));
123
+ const credentialsFile = node_path_1.default.join(getCloudflaredConfigDir(), `${tunnel.id}.json`);
124
+ const configContent = `tunnel: ${tunnel.id}
125
+ credentials-file: ${credentialsFile}
126
+ protocol: http2
127
+ ingress:
128
+ - hostname: ${tunnel.hostname}
129
+ service: http://localhost:${localPort}
130
+ - service: http_status:404
131
+ `;
132
+ const configPath = node_path_1.default.join(tmpDir, 'config.yml');
133
+ await promises_1.default.writeFile(configPath, configContent);
134
+ // Spawn cloudflared tunnel run
135
+ const proc = (0, node_child_process_1.spawn)('cloudflared', ['tunnel', 'run', '--config', configPath], {
136
+ stdio: ['ignore', 'pipe', 'pipe'],
137
+ detached: false,
138
+ });
139
+ // Wait for connection
140
+ const publicUrl = `https://${tunnel.hostname}`;
141
+ await new Promise((resolve, reject) => {
142
+ const timeout = setTimeout(() => {
143
+ proc.kill();
144
+ reject(new Error('Named tunnel connection timeout (60s)'));
145
+ }, 60000);
146
+ let lastError = '';
147
+ proc.stderr.on('data', (data) => {
148
+ const line = data.toString();
149
+ if (line.includes('Registered tunnel connection')) {
150
+ clearTimeout(timeout);
151
+ resolve();
152
+ }
153
+ if (line.includes('ERR') || line.includes('error')) {
154
+ lastError = line.trim();
155
+ }
156
+ });
157
+ proc.on('close', (code) => {
158
+ clearTimeout(timeout);
159
+ if (code !== 0) {
160
+ reject(new Error(lastError || `cloudflared exited with code ${code}`));
161
+ }
162
+ });
163
+ proc.on('error', (err) => {
164
+ clearTimeout(timeout);
165
+ reject(err);
166
+ });
167
+ });
168
+ return {
169
+ providerId: this.id,
170
+ publicUrl,
171
+ stop: () => {
172
+ proc.kill();
173
+ void promises_1.default.rm(tmpDir, { recursive: true, force: true });
174
+ }
175
+ };
176
+ }
177
+ async startQuickTunnel(localPort) {
32
178
  // Anonymous tunnel using cloudflared npm package
33
179
  const tmpDir = await promises_1.default.mkdtemp(node_path_1.default.join(node_os_1.default.tmpdir(), 'sub-bridge-cloudflared-'));
34
180
  const configPath = node_path_1.default.join(tmpDir, 'config.yml');
@@ -54,7 +200,7 @@ class CloudflareTunnelProvider {
54
200
  if (data.includes('ERR') || data.includes('error')) {
55
201
  // Extract meaningful error message
56
202
  if (data.includes('Too Many Requests') || data.includes('1015')) {
57
- lastError = 'Cloudflare rate limit exceeded. Please wait a few minutes or use ngrok instead.';
203
+ lastError = 'Cloudflare rate limit exceeded. Please wait a few minutes and try again.';
58
204
  }
59
205
  else if (data.includes('failed to unmarshal')) {
60
206
  lastError = data.split('\n').find(line => line.includes('failed'))?.trim() || data;
@@ -1 +1 @@
1
- {"version":3,"file":"cloudflare.js","sourceRoot":"","sources":["../../../src/tunnel/providers/cloudflare.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cloudflare.js","sourceRoot":"","sources":["../../../src/tunnel/providers/cloudflare.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,2 @@
1
1
  export { CloudflareTunnelProvider } from './cloudflare';
2
- export { NgrokTunnelProvider } from './ngrok';
3
- export { TailscaleTunnelProvider } from './tailscale';
4
2
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tunnel/providers/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAA;AACvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAA;AAC7C,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tunnel/providers/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAA"}
@@ -3,11 +3,7 @@
3
3
  // Tunnel Providers Index
4
4
  // ============================================================================
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.TailscaleTunnelProvider = exports.NgrokTunnelProvider = exports.CloudflareTunnelProvider = void 0;
6
+ exports.CloudflareTunnelProvider = void 0;
7
7
  var cloudflare_1 = require("./cloudflare");
8
8
  Object.defineProperty(exports, "CloudflareTunnelProvider", { enumerable: true, get: function () { return cloudflare_1.CloudflareTunnelProvider; } });
9
- var ngrok_1 = require("./ngrok");
10
- Object.defineProperty(exports, "NgrokTunnelProvider", { enumerable: true, get: function () { return ngrok_1.NgrokTunnelProvider; } });
11
- var tailscale_1 = require("./tailscale");
12
- Object.defineProperty(exports, "TailscaleTunnelProvider", { enumerable: true, get: function () { return tailscale_1.TailscaleTunnelProvider; } });
13
9
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/tunnel/providers/index.ts"],"names":[],"mappings":";AAAA,+EAA+E;AAC/E,yBAAyB;AACzB,+EAA+E;;;AAE/E,2CAAuD;AAA9C,sHAAA,wBAAwB,OAAA;AACjC,iCAA6C;AAApC,4GAAA,mBAAmB,OAAA;AAC5B,yCAAqD;AAA5C,oHAAA,uBAAuB,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/tunnel/providers/index.ts"],"names":[],"mappings":";AAAA,+EAA+E;AAC/E,yBAAyB;AACzB,+EAA+E;;;AAE/E,2CAAuD;AAA9C,sHAAA,wBAAwB,OAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/tunnel/registry.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAkC,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAuDzF,qBAAa,cAAc;IACzB,OAAO,CAAC,SAAS,CAAyC;IAC1D,OAAO,CAAC,YAAY,CAA8B;IAClD,OAAO,CAAC,SAAS,CAAsB;IACvC,OAAO,CAAC,SAAS,CAAsB;;IAajC,YAAY,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAa7C,SAAS,IAAI,YAAY;IAenB,KAAK,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAkD5F,IAAI,IAAI,YAAY;IASpB,YAAY,IAAI,MAAM,GAAG,IAAI;CAG9B"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/tunnel/registry.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAkC,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAmDzF,qBAAa,cAAc;IACzB,OAAO,CAAC,SAAS,CAAyC;IAC1D,OAAO,CAAC,YAAY,CAA8B;IAClD,OAAO,CAAC,SAAS,CAAsB;IACvC,OAAO,CAAC,SAAS,CAAsB;;IAOjC,YAAY,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAgB7C,SAAS,IAAI,YAAY;IAenB,KAAK,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAkD5F,IAAI,IAAI,YAAY;IASpB,YAAY,IAAI,MAAM,GAAG,IAAI;CAG9B"}
@@ -58,23 +58,20 @@ class TunnelRegistry {
58
58
  startedAt = null;
59
59
  lastError = null;
60
60
  constructor() {
61
- const allProviders = [
62
- new providers_1.CloudflareTunnelProvider(),
63
- new providers_1.NgrokTunnelProvider(),
64
- new providers_1.TailscaleTunnelProvider(),
65
- ];
66
- for (const provider of allProviders) {
67
- this.providers.set(provider.id, provider);
68
- }
61
+ const provider = new providers_1.CloudflareTunnelProvider();
62
+ this.providers.set(provider.id, provider);
69
63
  }
70
64
  async getProviders() {
71
65
  const results = [];
72
66
  for (const [id, provider] of this.providers) {
67
+ const authenticated = await provider.isAuthenticated();
73
68
  results.push({
74
69
  id,
75
70
  name: provider.name,
76
71
  available: await provider.isAvailable(),
77
72
  supportsNamedTunnels: provider.supportsNamedTunnels,
73
+ authenticated,
74
+ namedTunnels: authenticated ? await provider.listTunnels() : undefined,
78
75
  });
79
76
  }
80
77
  return results;
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/tunnel/registry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/tunnel/registry.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,15 @@
1
+ export interface NamedTunnelInfo {
2
+ id: string;
3
+ name: string;
4
+ hostname?: string;
5
+ }
1
6
  export interface TunnelProvider {
2
7
  id: string;
3
8
  name: string;
4
9
  supportsNamedTunnels: boolean;
5
10
  isAvailable(): Promise<boolean>;
11
+ isAuthenticated(): Promise<boolean>;
12
+ listTunnels(): Promise<NamedTunnelInfo[]>;
6
13
  start(localPort: number, namedUrl?: string): Promise<TunnelInstance>;
7
14
  }
8
15
  export interface TunnelInstance {
@@ -22,5 +29,7 @@ export interface ProviderInfo {
22
29
  name: string;
23
30
  available: boolean;
24
31
  supportsNamedTunnels: boolean;
32
+ authenticated?: boolean;
33
+ namedTunnels?: NamedTunnelInfo[];
25
34
  }
26
35
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/tunnel/types.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,oBAAoB,EAAE,OAAO,CAAA;IAC7B,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC,CAAA;IAC/B,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAAA;CACrE;AAED,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,IAAI,IAAI,CAAA;CACb;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,OAAO,CAAA;IACf,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,OAAO,CAAA;IAClB,oBAAoB,EAAE,OAAO,CAAA;CAC9B"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/tunnel/types.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,oBAAoB,EAAE,OAAO,CAAA;IAC7B,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC,CAAA;IAC/B,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC,CAAA;IACnC,WAAW,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC,CAAA;IACzC,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAAA;CACrE;AAED,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,IAAI,IAAI,CAAA;CACb;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,OAAO,CAAA;IACf,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,OAAO,CAAA;IAClB,oBAAoB,EAAE,OAAO,CAAA;IAC7B,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,YAAY,CAAC,EAAE,eAAe,EAAE,CAAA;CACjC"}
package/index.html CHANGED
@@ -240,39 +240,39 @@
240
240
  </template>
241
241
  </div>
242
242
 
243
- <div class="space-y-1">
244
- <label
245
- class="text-[10px] font-semibold uppercase tracking-wider text-neutral-500"
246
- x-text="tunnelLabel"
247
- ></label>
248
- <input
249
- type="text"
250
- :value="hasTunnel ? effectivePublicUrl + '/v1' : tunnel.customDomain"
251
- :readonly="hasTunnel"
252
- @input="if (!hasTunnel) tunnel.customDomain = $event.target.value"
253
- @focus="$el.select()"
254
- :placeholder="hasTunnel ? '' : (tunnel.loading ? 'Loading providers...' : 'e.g. yourname.example.com')"
255
- class="w-full rounded-lg border border-neutral-800 bg-neutral-950 px-3 py-2 text-sm text-neutral-100 placeholder:text-neutral-600 focus:border-neutral-600 focus:outline-none font-mono"
256
- >
257
- </div>
243
+ <template x-if="hasTunnel">
244
+ <div class="space-y-1">
245
+ <label class="text-[10px] font-semibold uppercase tracking-wider text-neutral-500">OpenAI Base URL</label>
246
+ <input
247
+ type="text"
248
+ :value="effectivePublicUrl + '/v1'"
249
+ readonly
250
+ @focus="$el.select()"
251
+ class="w-full rounded-lg border border-neutral-800 bg-neutral-950 px-3 py-2 text-sm text-neutral-100 focus:border-neutral-600 focus:outline-none font-mono"
252
+ >
253
+ </div>
254
+ </template>
258
255
 
259
256
  <template x-if="!hasTunnel">
260
257
  <div class="space-y-3">
261
- <div class="flex flex-wrap items-center justify-between gap-3">
262
- <div class="flex flex-wrap gap-2" x-show="!tunnel.loading && tunnel.providers.some(p => p.available)">
263
- <template x-for="p in tunnel.providers.filter(p => p.available)" :key="p.id">
264
- <button
265
- @click="tunnel.selectedProvider = p.id"
266
- :class="tunnel.selectedProvider === p.id ? 'bg-neutral-700 text-white border-neutral-600' : 'bg-neutral-950 text-neutral-400 border-neutral-800'"
267
- class="inline-flex items-center justify-center rounded-lg border px-3 py-1.5 text-xs font-semibold uppercase tracking-wide transition"
268
- x-text="p.name"
269
- ></button>
270
- </template>
258
+ <!-- Named tunnel selector (only when authenticated) -->
259
+ <template x-if="selectedProvider?.authenticated && selectedProvider?.namedTunnels?.length">
260
+ <div class="space-y-2">
261
+ <label class="text-[10px] font-semibold uppercase tracking-wider text-neutral-500">Use Named Tunnel (optional)</label>
262
+ <select
263
+ x-model="tunnel.customDomain"
264
+ class="w-full rounded-lg border border-neutral-800 bg-neutral-950 px-3 py-2 text-sm text-neutral-100 focus:border-neutral-600 focus:outline-none"
265
+ >
266
+ <option value="">Quick tunnel (random URL)</option>
267
+ <template x-for="t in selectedProvider.namedTunnels" :key="t.id">
268
+ <option :value="t.hostname || t.name" x-text="t.hostname ? t.name + ' (' + t.hostname + ')' : t.name"></option>
269
+ </template>
270
+ </select>
271
271
  </div>
272
+ </template>
273
+ <div class="flex flex-wrap items-center justify-between gap-3">
272
274
  <div class="flex flex-wrap gap-2" x-show="tunnel.loading">
273
275
  <span class="h-7 w-20 rounded-lg border border-neutral-800 bg-neutral-900/60 animate-pulse"></span>
274
- <span class="h-7 w-16 rounded-lg border border-neutral-800 bg-neutral-900/60 animate-pulse"></span>
275
- <span class="h-7 w-12 rounded-lg border border-neutral-800 bg-neutral-900/60 animate-pulse"></span>
276
276
  </div>
277
277
  <template x-if="!tunnel.status.active && !hasExternalUrl">
278
278
  <button
@@ -285,7 +285,7 @@
285
285
  </template>
286
286
  </div>
287
287
  <p x-show="!tunnel.providers.some(p => p.available) && !tunnel.loading" class="text-xs text-neutral-400">
288
- No tunnel providers found (install cloudflared, ngrok, or tailscale).
288
+ No tunnel providers found. Install cloudflared to create tunnels.
289
289
  </p>
290
290
  </div>
291
291
  </template>
@@ -514,10 +514,14 @@
514
514
  return this.tunnel.status.active || this.hasExternalUrl;
515
515
  },
516
516
 
517
+ // Selected tunnel provider object
518
+ get selectedProvider() {
519
+ return this.tunnel.providers.find(p => p.id === this.tunnel.selectedProvider) || null;
520
+ },
521
+
517
522
  // Selected tunnel provider name (falls back to generic label)
518
523
  get selectedTunnelName() {
519
- const selected = this.tunnel.providers.find(p => p.id === this.tunnel.selectedProvider);
520
- return selected ? selected.name : 'Tunnel';
524
+ return this.selectedProvider ? this.selectedProvider.name : 'Tunnel';
521
525
  },
522
526
 
523
527
  get firstClaudeAccountId() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sub-bridge",
3
- "version": "1.0.5",
3
+ "version": "1.1.0",
4
4
  "description": "MCP bridge to use ChatGPT Pro, Claude Max, etc. in Cursor via an OpenAI-compatible proxy",
5
5
  "main": "dist/cli.js",
6
6
  "bin": {
@@ -3,10 +3,18 @@
3
3
  // ============================================================================
4
4
 
5
5
  import { Tunnel } from 'cloudflared'
6
+ import { spawn } from 'node:child_process'
6
7
  import fs from 'node:fs/promises'
7
8
  import os from 'node:os'
8
9
  import path from 'node:path'
9
- import type { TunnelProvider, TunnelInstance } from '../types'
10
+ import type { TunnelProvider, TunnelInstance, NamedTunnelInfo } from '../types'
11
+
12
+ function getCloudflaredConfigDir(): string {
13
+ if (process.platform === 'win32') {
14
+ return path.join(process.env.USERPROFILE || os.homedir(), '.cloudflared')
15
+ }
16
+ return path.join(os.homedir(), '.cloudflared')
17
+ }
10
18
 
11
19
  export class CloudflareTunnelProvider implements TunnelProvider {
12
20
  id = 'cloudflare'
@@ -18,17 +26,179 @@ export class CloudflareTunnelProvider implements TunnelProvider {
18
26
  return true
19
27
  }
20
28
 
29
+ async isAuthenticated(): Promise<boolean> {
30
+ const certPath = path.join(getCloudflaredConfigDir(), 'cert.pem')
31
+ try {
32
+ await fs.access(certPath)
33
+ return true
34
+ } catch {
35
+ return false
36
+ }
37
+ }
38
+
39
+ async listTunnels(): Promise<NamedTunnelInfo[]> {
40
+ if (!await this.isAuthenticated()) {
41
+ return []
42
+ }
43
+
44
+ try {
45
+ // Run cloudflared tunnel list and parse output
46
+ const output = await this.runCloudflaredCommand(['tunnel', 'list', '--output', 'json'])
47
+ const tunnels = JSON.parse(output) as Array<{ id: string; name: string }>
48
+
49
+ // Get DNS routes for each tunnel
50
+ const results: NamedTunnelInfo[] = []
51
+ for (const tunnel of tunnels) {
52
+ const hostname = await this.getTunnelHostname(tunnel.id)
53
+ results.push({
54
+ id: tunnel.id,
55
+ name: tunnel.name,
56
+ hostname,
57
+ })
58
+ }
59
+ return results
60
+ } catch {
61
+ return []
62
+ }
63
+ }
64
+
65
+ private async getTunnelHostname(tunnelId: string): Promise<string | undefined> {
66
+ try {
67
+ // Check config.yml for ingress rules
68
+ const configPath = path.join(getCloudflaredConfigDir(), 'config.yml')
69
+ const config = await fs.readFile(configPath, 'utf-8')
70
+
71
+ // Simple regex to extract hostname from ingress rules
72
+ const hostnameMatch = config.match(/hostname:\s*(\S+)/)
73
+ if (hostnameMatch) {
74
+ return hostnameMatch[1]
75
+ }
76
+ } catch {
77
+ // Config file doesn't exist or is unreadable
78
+ }
79
+ return undefined
80
+ }
81
+
82
+ private runCloudflaredCommand(args: string[]): Promise<string> {
83
+ return new Promise((resolve, reject) => {
84
+ const proc = spawn('cloudflared', args, {
85
+ stdio: ['ignore', 'pipe', 'pipe'],
86
+ })
87
+
88
+ let stdout = ''
89
+ let stderr = ''
90
+
91
+ proc.stdout.on('data', (data) => { stdout += data.toString() })
92
+ proc.stderr.on('data', (data) => { stderr += data.toString() })
93
+
94
+ proc.on('close', (code) => {
95
+ if (code === 0) {
96
+ resolve(stdout)
97
+ } else {
98
+ reject(new Error(stderr || `cloudflared exited with code ${code}`))
99
+ }
100
+ })
101
+
102
+ proc.on('error', reject)
103
+ })
104
+ }
105
+
21
106
  async start(localPort: number, namedUrl?: string): Promise<TunnelInstance> {
22
107
  if (namedUrl) {
23
- // Named tunnel: user provides their own tunnel hostname
24
- // They need to configure this in Cloudflare dashboard and run cloudflared separately
25
- return {
26
- providerId: this.id,
27
- publicUrl: namedUrl.startsWith('https://') ? namedUrl : `https://${namedUrl}`,
28
- stop: () => {}
108
+ return this.startNamedTunnel(localPort, namedUrl)
109
+ }
110
+ return this.startQuickTunnel(localPort)
111
+ }
112
+
113
+ private async startNamedTunnel(localPort: number, namedUrl: string): Promise<TunnelInstance> {
114
+ // Check if authenticated
115
+ if (!await this.isAuthenticated()) {
116
+ throw new Error('Cloudflare authentication required. Run: cloudflared tunnel login')
117
+ }
118
+
119
+ // Find the tunnel info
120
+ const tunnels = await this.listTunnels()
121
+ const tunnel = tunnels.find(t =>
122
+ t.name === namedUrl ||
123
+ t.hostname === namedUrl ||
124
+ t.id === namedUrl
125
+ )
126
+
127
+ if (!tunnel) {
128
+ throw new Error(`Tunnel not found: ${namedUrl}. Available: ${tunnels.map(t => t.name).join(', ') || 'none'}`)
129
+ }
130
+
131
+ if (!tunnel.hostname) {
132
+ throw new Error(`Tunnel "${tunnel.name}" has no DNS route configured. Configure one in Cloudflare dashboard.`)
133
+ }
134
+
135
+ // Create temp config with the requested port
136
+ const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'sub-bridge-cloudflared-'))
137
+ const credentialsFile = path.join(getCloudflaredConfigDir(), `${tunnel.id}.json`)
138
+
139
+ const configContent = `tunnel: ${tunnel.id}
140
+ credentials-file: ${credentialsFile}
141
+ protocol: http2
142
+ ingress:
143
+ - hostname: ${tunnel.hostname}
144
+ service: http://localhost:${localPort}
145
+ - service: http_status:404
146
+ `
147
+ const configPath = path.join(tmpDir, 'config.yml')
148
+ await fs.writeFile(configPath, configContent)
149
+
150
+ // Spawn cloudflared tunnel run
151
+ const proc = spawn('cloudflared', ['tunnel', 'run', '--config', configPath], {
152
+ stdio: ['ignore', 'pipe', 'pipe'],
153
+ detached: false,
154
+ })
155
+
156
+ // Wait for connection
157
+ const publicUrl = `https://${tunnel.hostname}`
158
+
159
+ await new Promise<void>((resolve, reject) => {
160
+ const timeout = setTimeout(() => {
161
+ proc.kill()
162
+ reject(new Error('Named tunnel connection timeout (60s)'))
163
+ }, 60000)
164
+
165
+ let lastError = ''
166
+
167
+ proc.stderr.on('data', (data: Buffer) => {
168
+ const line = data.toString()
169
+ if (line.includes('Registered tunnel connection')) {
170
+ clearTimeout(timeout)
171
+ resolve()
172
+ }
173
+ if (line.includes('ERR') || line.includes('error')) {
174
+ lastError = line.trim()
175
+ }
176
+ })
177
+
178
+ proc.on('close', (code) => {
179
+ clearTimeout(timeout)
180
+ if (code !== 0) {
181
+ reject(new Error(lastError || `cloudflared exited with code ${code}`))
182
+ }
183
+ })
184
+
185
+ proc.on('error', (err) => {
186
+ clearTimeout(timeout)
187
+ reject(err)
188
+ })
189
+ })
190
+
191
+ return {
192
+ providerId: this.id,
193
+ publicUrl,
194
+ stop: () => {
195
+ proc.kill()
196
+ void fs.rm(tmpDir, { recursive: true, force: true })
29
197
  }
30
198
  }
199
+ }
31
200
 
201
+ private async startQuickTunnel(localPort: number): Promise<TunnelInstance> {
32
202
  // Anonymous tunnel using cloudflared npm package
33
203
  const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'sub-bridge-cloudflared-'))
34
204
  const configPath = path.join(tmpDir, 'config.yml')
@@ -57,7 +227,7 @@ export class CloudflareTunnelProvider implements TunnelProvider {
57
227
  if (data.includes('ERR') || data.includes('error')) {
58
228
  // Extract meaningful error message
59
229
  if (data.includes('Too Many Requests') || data.includes('1015')) {
60
- lastError = 'Cloudflare rate limit exceeded. Please wait a few minutes or use ngrok instead.'
230
+ lastError = 'Cloudflare rate limit exceeded. Please wait a few minutes and try again.'
61
231
  } else if (data.includes('failed to unmarshal')) {
62
232
  lastError = data.split('\n').find(line => line.includes('failed'))?.trim() || data
63
233
  } else {
@@ -3,5 +3,3 @@
3
3
  // ============================================================================
4
4
 
5
5
  export { CloudflareTunnelProvider } from './cloudflare'
6
- export { NgrokTunnelProvider } from './ngrok'
7
- export { TailscaleTunnelProvider } from './tailscale'
@@ -51,11 +51,7 @@ async function verifyTunnelHealth(publicUrl: string, expectedPort: number): Prom
51
51
  }
52
52
  }
53
53
  }
54
- import {
55
- CloudflareTunnelProvider,
56
- NgrokTunnelProvider,
57
- TailscaleTunnelProvider,
58
- } from './providers'
54
+ import { CloudflareTunnelProvider } from './providers'
59
55
 
60
56
  export class TunnelRegistry {
61
57
  private providers: Map<string, TunnelProvider> = new Map()
@@ -64,24 +60,21 @@ export class TunnelRegistry {
64
60
  private lastError: string | null = null
65
61
 
66
62
  constructor() {
67
- const allProviders: TunnelProvider[] = [
68
- new CloudflareTunnelProvider(),
69
- new NgrokTunnelProvider(),
70
- new TailscaleTunnelProvider(),
71
- ]
72
- for (const provider of allProviders) {
73
- this.providers.set(provider.id, provider)
74
- }
63
+ const provider = new CloudflareTunnelProvider()
64
+ this.providers.set(provider.id, provider)
75
65
  }
76
66
 
77
67
  async getProviders(): Promise<ProviderInfo[]> {
78
68
  const results: ProviderInfo[] = []
79
69
  for (const [id, provider] of this.providers) {
70
+ const authenticated = await provider.isAuthenticated()
80
71
  results.push({
81
72
  id,
82
73
  name: provider.name,
83
74
  available: await provider.isAvailable(),
84
75
  supportsNamedTunnels: provider.supportsNamedTunnels,
76
+ authenticated,
77
+ namedTunnels: authenticated ? await provider.listTunnels() : undefined,
85
78
  })
86
79
  }
87
80
  return results
@@ -2,11 +2,19 @@
2
2
  // Tunnel Types
3
3
  // ============================================================================
4
4
 
5
+ export interface NamedTunnelInfo {
6
+ id: string
7
+ name: string
8
+ hostname?: string
9
+ }
10
+
5
11
  export interface TunnelProvider {
6
12
  id: string
7
13
  name: string
8
14
  supportsNamedTunnels: boolean
9
15
  isAvailable(): Promise<boolean>
16
+ isAuthenticated(): Promise<boolean>
17
+ listTunnels(): Promise<NamedTunnelInfo[]>
10
18
  start(localPort: number, namedUrl?: string): Promise<TunnelInstance>
11
19
  }
12
20
 
@@ -29,4 +37,6 @@ export interface ProviderInfo {
29
37
  name: string
30
38
  available: boolean
31
39
  supportsNamedTunnels: boolean
40
+ authenticated?: boolean
41
+ namedTunnels?: NamedTunnelInfo[]
32
42
  }
@@ -1,10 +0,0 @@
1
- import type { TunnelProvider, TunnelInstance } from '../types';
2
- export declare class NgrokTunnelProvider implements TunnelProvider {
3
- id: string;
4
- name: string;
5
- supportsNamedTunnels: boolean;
6
- private process;
7
- isAvailable(): Promise<boolean>;
8
- start(localPort: number, namedUrl?: string): Promise<TunnelInstance>;
9
- }
10
- //# sourceMappingURL=ngrok.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ngrok.d.ts","sourceRoot":"","sources":["../../../src/tunnel/providers/ngrok.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAG9D,qBAAa,mBAAoB,YAAW,cAAc;IACxD,EAAE,SAAU;IACZ,IAAI,SAAU;IACd,oBAAoB,UAAO;IAC3B,OAAO,CAAC,OAAO,CAA4B;IAErC,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAI/B,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;CAqC3E"}
@@ -1,52 +0,0 @@
1
- "use strict";
2
- // ============================================================================
3
- // ngrok Tunnel Provider
4
- // ============================================================================
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.NgrokTunnelProvider = void 0;
7
- const utils_1 = require("../utils");
8
- class NgrokTunnelProvider {
9
- id = 'ngrok';
10
- name = 'ngrok';
11
- supportsNamedTunnels = true;
12
- process = null;
13
- async isAvailable() {
14
- return (0, utils_1.binaryExists)('ngrok');
15
- }
16
- async start(localPort, namedUrl) {
17
- const args = ['http', String(localPort)];
18
- // Add subdomain if provided (requires ngrok account)
19
- if (namedUrl) {
20
- const subdomain = namedUrl.replace(/\.ngrok\.(io|app)$/, '');
21
- args.push('--subdomain', subdomain);
22
- }
23
- this.process = (0, utils_1.spawnTunnelProcess)('ngrok', args);
24
- // Poll ngrok's local API for tunnel URL
25
- const url = await (0, utils_1.pollUntil)(async () => {
26
- try {
27
- const response = await fetch('http://127.0.0.1:4040/api/tunnels');
28
- if (!response.ok)
29
- return null;
30
- const data = await response.json();
31
- const httpsTunnel = data.tunnels.find(t => t.public_url.startsWith('https://'));
32
- return httpsTunnel?.public_url || null;
33
- }
34
- catch {
35
- return null;
36
- }
37
- }, 30000).catch(() => {
38
- throw new Error('ngrok tunnel timeout');
39
- });
40
- const proc = this.process;
41
- return {
42
- providerId: this.id,
43
- publicUrl: url,
44
- stop: () => {
45
- proc?.kill('SIGTERM');
46
- this.process = null;
47
- }
48
- };
49
- }
50
- }
51
- exports.NgrokTunnelProvider = NgrokTunnelProvider;
52
- //# sourceMappingURL=ngrok.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ngrok.js","sourceRoot":"","sources":["../../../src/tunnel/providers/ngrok.ts"],"names":[],"mappings":";AAAA,+EAA+E;AAC/E,wBAAwB;AACxB,+EAA+E;;;AAI/E,oCAAsE;AAEtE,MAAa,mBAAmB;IAC9B,EAAE,GAAG,OAAO,CAAA;IACZ,IAAI,GAAG,OAAO,CAAA;IACd,oBAAoB,GAAG,IAAI,CAAA;IACnB,OAAO,GAAwB,IAAI,CAAA;IAE3C,KAAK,CAAC,WAAW;QACf,OAAO,IAAA,oBAAY,EAAC,OAAO,CAAC,CAAA;IAC9B,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,SAAiB,EAAE,QAAiB;QAC9C,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC,CAAA;QAExC,qDAAqD;QACrD,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,SAAS,GAAG,QAAQ,CAAC,OAAO,CAAC,oBAAoB,EAAE,EAAE,CAAC,CAAA;YAC5D,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,SAAS,CAAC,CAAA;QACrC,CAAC;QAED,IAAI,CAAC,OAAO,GAAG,IAAA,0BAAkB,EAAC,OAAO,EAAE,IAAI,CAAC,CAAA;QAEhD,wCAAwC;QACxC,MAAM,GAAG,GAAG,MAAM,IAAA,iBAAS,EAAC,KAAK,IAAI,EAAE;YACrC,IAAI,CAAC;gBACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,mCAAmC,CAAC,CAAA;gBACjE,IAAI,CAAC,QAAQ,CAAC,EAAE;oBAAE,OAAO,IAAI,CAAA;gBAE7B,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAgD,CAAA;gBAChF,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAA;gBAC/E,OAAO,WAAW,EAAE,UAAU,IAAI,IAAI,CAAA;YACxC,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,IAAI,CAAA;YACb,CAAC;QACH,CAAC,EAAE,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;YACnB,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAA;QACzC,CAAC,CAAC,CAAA;QAEF,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAA;QACzB,OAAO;YACL,UAAU,EAAE,IAAI,CAAC,EAAE;YACnB,SAAS,EAAE,GAAG;YACd,IAAI,EAAE,GAAG,EAAE;gBACT,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,CAAA;gBACrB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAA;YACrB,CAAC;SACF,CAAA;IACH,CAAC;CACF;AA/CD,kDA+CC"}
@@ -1,10 +0,0 @@
1
- import type { TunnelProvider, TunnelInstance } from '../types';
2
- export declare class TailscaleTunnelProvider implements TunnelProvider {
3
- id: string;
4
- name: string;
5
- supportsNamedTunnels: boolean;
6
- private process;
7
- isAvailable(): Promise<boolean>;
8
- start(localPort: number): Promise<TunnelInstance>;
9
- }
10
- //# sourceMappingURL=tailscale.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tailscale.d.ts","sourceRoot":"","sources":["../../../src/tunnel/providers/tailscale.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAG9D,qBAAa,uBAAwB,YAAW,cAAc;IAC5D,EAAE,SAAc;IAChB,IAAI,SAAqB;IACzB,oBAAoB,UAAQ;IAC5B,OAAO,CAAC,OAAO,CAA4B;IAErC,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAW/B,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;CAwBxD"}
@@ -1,48 +0,0 @@
1
- "use strict";
2
- // ============================================================================
3
- // Tailscale Funnel Provider
4
- // ============================================================================
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.TailscaleTunnelProvider = void 0;
7
- const child_process_1 = require("child_process");
8
- const utils_1 = require("../utils");
9
- class TailscaleTunnelProvider {
10
- id = 'tailscale';
11
- name = 'Tailscale Funnel';
12
- supportsNamedTunnels = false;
13
- process = null;
14
- async isAvailable() {
15
- if (!await (0, utils_1.binaryExists)('tailscale'))
16
- return false;
17
- try {
18
- const status = await (0, utils_1.execJson)('tailscale status --json');
19
- return status.BackendState === 'Running';
20
- }
21
- catch {
22
- return false;
23
- }
24
- }
25
- async start(localPort) {
26
- // Get hostname from Tailscale status
27
- const status = await (0, utils_1.execJson)('tailscale status --json');
28
- const hostname = status.Self?.DNSName?.replace(/\.$/, '');
29
- if (!hostname)
30
- throw new Error('Could not determine Tailscale hostname');
31
- // Start funnel
32
- this.process = (0, utils_1.spawnTunnelProcess)('tailscale', ['funnel', String(localPort)]);
33
- // Wait for funnel to initialize
34
- await new Promise(r => setTimeout(r, 2000));
35
- const proc = this.process;
36
- return {
37
- providerId: this.id,
38
- publicUrl: `https://${hostname}`,
39
- stop: () => {
40
- (0, child_process_1.spawn)('tailscale', ['funnel', 'off'], { stdio: 'ignore' });
41
- proc?.kill('SIGTERM');
42
- this.process = null;
43
- }
44
- };
45
- }
46
- }
47
- exports.TailscaleTunnelProvider = TailscaleTunnelProvider;
48
- //# sourceMappingURL=tailscale.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tailscale.js","sourceRoot":"","sources":["../../../src/tunnel/providers/tailscale.ts"],"names":[],"mappings":";AAAA,+EAA+E;AAC/E,4BAA4B;AAC5B,+EAA+E;;;AAE/E,iDAAmD;AAEnD,oCAAqE;AAErE,MAAa,uBAAuB;IAClC,EAAE,GAAG,WAAW,CAAA;IAChB,IAAI,GAAG,kBAAkB,CAAA;IACzB,oBAAoB,GAAG,KAAK,CAAA;IACpB,OAAO,GAAwB,IAAI,CAAA;IAE3C,KAAK,CAAC,WAAW;QACf,IAAI,CAAC,MAAM,IAAA,oBAAY,EAAC,WAAW,CAAC;YAAE,OAAO,KAAK,CAAA;QAElD,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAA,gBAAQ,EAA2B,yBAAyB,CAAC,CAAA;YAClF,OAAO,MAAM,CAAC,YAAY,KAAK,SAAS,CAAA;QAC1C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAA;QACd,CAAC;IACH,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,SAAiB;QAC3B,qCAAqC;QACrC,MAAM,MAAM,GAAG,MAAM,IAAA,gBAAQ,EAAkC,yBAAyB,CAAC,CAAA;QACzF,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;QAEzD,IAAI,CAAC,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAA;QAExE,eAAe;QACf,IAAI,CAAC,OAAO,GAAG,IAAA,0BAAkB,EAAC,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAA;QAE7E,gCAAgC;QAChC,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAA;QAE3C,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAA;QACzB,OAAO;YACL,UAAU,EAAE,IAAI,CAAC,EAAE;YACnB,SAAS,EAAE,WAAW,QAAQ,EAAE;YAChC,IAAI,EAAE,GAAG,EAAE;gBACT,IAAA,qBAAK,EAAC,WAAW,EAAE,CAAC,QAAQ,EAAE,KAAK,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAA;gBAC1D,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,CAAA;gBACrB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAA;YACrB,CAAC;SACF,CAAA;IACH,CAAC;CACF;AAzCD,0DAyCC"}
@@ -1,56 +0,0 @@
1
- // ============================================================================
2
- // ngrok Tunnel Provider
3
- // ============================================================================
4
-
5
- import { ChildProcess } from 'child_process'
6
- import type { TunnelProvider, TunnelInstance } from '../types'
7
- import { binaryExists, spawnTunnelProcess, pollUntil } from '../utils'
8
-
9
- export class NgrokTunnelProvider implements TunnelProvider {
10
- id = 'ngrok'
11
- name = 'ngrok'
12
- supportsNamedTunnels = true
13
- private process: ChildProcess | null = null
14
-
15
- async isAvailable(): Promise<boolean> {
16
- return binaryExists('ngrok')
17
- }
18
-
19
- async start(localPort: number, namedUrl?: string): Promise<TunnelInstance> {
20
- const args = ['http', String(localPort)]
21
-
22
- // Add subdomain if provided (requires ngrok account)
23
- if (namedUrl) {
24
- const subdomain = namedUrl.replace(/\.ngrok\.(io|app)$/, '')
25
- args.push('--subdomain', subdomain)
26
- }
27
-
28
- this.process = spawnTunnelProcess('ngrok', args)
29
-
30
- // Poll ngrok's local API for tunnel URL
31
- const url = await pollUntil(async () => {
32
- try {
33
- const response = await fetch('http://127.0.0.1:4040/api/tunnels')
34
- if (!response.ok) return null
35
-
36
- const data = await response.json() as { tunnels: Array<{ public_url: string }> }
37
- const httpsTunnel = data.tunnels.find(t => t.public_url.startsWith('https://'))
38
- return httpsTunnel?.public_url || null
39
- } catch {
40
- return null
41
- }
42
- }, 30000).catch(() => {
43
- throw new Error('ngrok tunnel timeout')
44
- })
45
-
46
- const proc = this.process
47
- return {
48
- providerId: this.id,
49
- publicUrl: url,
50
- stop: () => {
51
- proc?.kill('SIGTERM')
52
- this.process = null
53
- }
54
- }
55
- }
56
- }
@@ -1,50 +0,0 @@
1
- // ============================================================================
2
- // Tailscale Funnel Provider
3
- // ============================================================================
4
-
5
- import { spawn, ChildProcess } from 'child_process'
6
- import type { TunnelProvider, TunnelInstance } from '../types'
7
- import { binaryExists, spawnTunnelProcess, execJson } from '../utils'
8
-
9
- export class TailscaleTunnelProvider implements TunnelProvider {
10
- id = 'tailscale'
11
- name = 'Tailscale Funnel'
12
- supportsNamedTunnels = false
13
- private process: ChildProcess | null = null
14
-
15
- async isAvailable(): Promise<boolean> {
16
- if (!await binaryExists('tailscale')) return false
17
-
18
- try {
19
- const status = await execJson<{ BackendState: string }>('tailscale status --json')
20
- return status.BackendState === 'Running'
21
- } catch {
22
- return false
23
- }
24
- }
25
-
26
- async start(localPort: number): Promise<TunnelInstance> {
27
- // Get hostname from Tailscale status
28
- const status = await execJson<{ Self?: { DNSName?: string } }>('tailscale status --json')
29
- const hostname = status.Self?.DNSName?.replace(/\.$/, '')
30
-
31
- if (!hostname) throw new Error('Could not determine Tailscale hostname')
32
-
33
- // Start funnel
34
- this.process = spawnTunnelProcess('tailscale', ['funnel', String(localPort)])
35
-
36
- // Wait for funnel to initialize
37
- await new Promise(r => setTimeout(r, 2000))
38
-
39
- const proc = this.process
40
- return {
41
- providerId: this.id,
42
- publicUrl: `https://${hostname}`,
43
- stop: () => {
44
- spawn('tailscale', ['funnel', 'off'], { stdio: 'ignore' })
45
- proc?.kill('SIGTERM')
46
- this.process = null
47
- }
48
- }
49
- }
50
- }