sub-bridge 1.1.2 → 1.2.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,3 +1,3 @@
1
1
  {
2
- ".": "1.1.2"
2
+ ".": "1.2.0"
3
3
  }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.2.0](https://github.com/buremba/sub-bridge/compare/sub-bridge-v1.1.2...sub-bridge-v1.2.0) (2025-12-25)
4
+
5
+
6
+ ### Features
7
+
8
+ * add custom URL provider for users with their own public IP/domain ([ccc5719](https://github.com/buremba/sub-bridge/commit/ccc57191df3ddfaf567a257e50ecae12f4a63dbe))
9
+
3
10
  ## [1.1.2](https://github.com/buremba/sub-bridge/compare/sub-bridge-v1.1.1...sub-bridge-v1.1.2) (2025-12-25)
4
11
 
5
12
 
@@ -1,2 +1,3 @@
1
1
  export { CloudflareTunnelProvider } from './cloudflare';
2
+ export { ManualTunnelProvider } from './manual';
2
3
  //# 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"}
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,oBAAoB,EAAE,MAAM,UAAU,CAAA"}
@@ -3,7 +3,9 @@
3
3
  // Tunnel Providers Index
4
4
  // ============================================================================
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.CloudflareTunnelProvider = void 0;
6
+ exports.ManualTunnelProvider = 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 manual_1 = require("./manual");
10
+ Object.defineProperty(exports, "ManualTunnelProvider", { enumerable: true, get: function () { return manual_1.ManualTunnelProvider; } });
9
11
  //# 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"}
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,mCAA+C;AAAtC,8GAAA,oBAAoB,OAAA"}
@@ -0,0 +1,11 @@
1
+ import type { TunnelProvider, TunnelInstance, NamedTunnelInfo } from '../types';
2
+ export declare class ManualTunnelProvider implements TunnelProvider {
3
+ id: string;
4
+ name: string;
5
+ supportsNamedTunnels: boolean;
6
+ isAvailable(): Promise<boolean>;
7
+ isAuthenticated(): Promise<boolean>;
8
+ listTunnels(): Promise<NamedTunnelInfo[]>;
9
+ start(localPort: number, customUrl?: string): Promise<TunnelInstance>;
10
+ }
11
+ //# sourceMappingURL=manual.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manual.d.ts","sourceRoot":"","sources":["../../../src/tunnel/providers/manual.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAE/E,qBAAa,oBAAqB,YAAW,cAAc;IACzD,EAAE,SAAW;IACb,IAAI,SAAe;IACnB,oBAAoB,UAAQ;IAEtB,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAI/B,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC;IAInC,WAAW,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;IAIzC,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;CAsB5E"}
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ // ============================================================================
3
+ // Manual/Custom URL Provider - User provides their own public URL
4
+ // ============================================================================
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ManualTunnelProvider = void 0;
7
+ class ManualTunnelProvider {
8
+ id = 'manual';
9
+ name = 'Custom URL';
10
+ supportsNamedTunnels = false;
11
+ async isAvailable() {
12
+ return true; // Always available - no dependencies
13
+ }
14
+ async isAuthenticated() {
15
+ return true; // No authentication needed
16
+ }
17
+ async listTunnels() {
18
+ return []; // No named tunnels for manual provider
19
+ }
20
+ async start(localPort, customUrl) {
21
+ if (!customUrl) {
22
+ throw new Error('Custom URL is required. Enter your public URL (e.g., https://api.mydomain.com)');
23
+ }
24
+ // Normalize URL - ensure it has a protocol
25
+ let publicUrl = customUrl.trim();
26
+ if (!publicUrl.startsWith('http://') && !publicUrl.startsWith('https://')) {
27
+ publicUrl = 'https://' + publicUrl;
28
+ }
29
+ // Remove trailing slash for consistency
30
+ publicUrl = publicUrl.replace(/\/$/, '');
31
+ return {
32
+ providerId: this.id,
33
+ publicUrl,
34
+ stop: () => {
35
+ // No-op - nothing to stop since no process was started
36
+ }
37
+ };
38
+ }
39
+ }
40
+ exports.ManualTunnelProvider = ManualTunnelProvider;
41
+ //# sourceMappingURL=manual.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manual.js","sourceRoot":"","sources":["../../../src/tunnel/providers/manual.ts"],"names":[],"mappings":";AAAA,+EAA+E;AAC/E,kEAAkE;AAClE,+EAA+E;;;AAI/E,MAAa,oBAAoB;IAC/B,EAAE,GAAG,QAAQ,CAAA;IACb,IAAI,GAAG,YAAY,CAAA;IACnB,oBAAoB,GAAG,KAAK,CAAA;IAE5B,KAAK,CAAC,WAAW;QACf,OAAO,IAAI,CAAA,CAAC,qCAAqC;IACnD,CAAC;IAED,KAAK,CAAC,eAAe;QACnB,OAAO,IAAI,CAAA,CAAC,2BAA2B;IACzC,CAAC;IAED,KAAK,CAAC,WAAW;QACf,OAAO,EAAE,CAAA,CAAC,uCAAuC;IACnD,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,SAAiB,EAAE,SAAkB;QAC/C,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,gFAAgF,CAAC,CAAA;QACnG,CAAC;QAED,2CAA2C;QAC3C,IAAI,SAAS,GAAG,SAAS,CAAC,IAAI,EAAE,CAAA;QAChC,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC1E,SAAS,GAAG,UAAU,GAAG,SAAS,CAAA;QACpC,CAAC;QAED,wCAAwC;QACxC,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;QAExC,OAAO;YACL,UAAU,EAAE,IAAI,CAAC,EAAE;YACnB,SAAS;YACT,IAAI,EAAE,GAAG,EAAE;gBACT,uDAAuD;YACzD,CAAC;SACF,CAAA;IACH,CAAC;CACF;AAvCD,oDAuCC"}
@@ -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;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"}
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;;IAUjC,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;IAsD5F,IAAI,IAAI,YAAY;IASpB,YAAY,IAAI,MAAM,GAAG,IAAI;CAG9B"}
@@ -58,8 +58,10 @@ class TunnelRegistry {
58
58
  startedAt = null;
59
59
  lastError = null;
60
60
  constructor() {
61
- const provider = new providers_1.CloudflareTunnelProvider();
62
- this.providers.set(provider.id, provider);
61
+ const cloudflare = new providers_1.CloudflareTunnelProvider();
62
+ this.providers.set(cloudflare.id, cloudflare);
63
+ const manual = new providers_1.ManualTunnelProvider();
64
+ this.providers.set(manual.id, manual);
63
65
  }
64
66
  async getProviders() {
65
67
  const results = [];
@@ -104,6 +106,7 @@ class TunnelRegistry {
104
106
  throw new Error(`Tunnel provider ${provider.name} is not available`);
105
107
  }
106
108
  const isQuickCloudflare = providerId === 'cloudflare' && !namedUrl;
109
+ const isManual = providerId === 'manual';
107
110
  const maxAttempts = isQuickCloudflare ? 4 : 1;
108
111
  let lastError = null;
109
112
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
@@ -111,7 +114,10 @@ class TunnelRegistry {
111
114
  this.lastError = null;
112
115
  this.activeTunnel = await provider.start(localPort, namedUrl);
113
116
  this.startedAt = new Date().toISOString();
114
- await verifyTunnelHealth(this.activeTunnel.publicUrl, localPort);
117
+ // Skip health check for manual provider - user trusts their own URL
118
+ if (!isManual) {
119
+ await verifyTunnelHealth(this.activeTunnel.publicUrl, localPort);
120
+ }
115
121
  return this.getStatus();
116
122
  }
117
123
  catch (error) {
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/tunnel/registry.ts"],"names":[],"mappings":";AAAA,+EAA+E;AAC/E,wDAAwD;AACxD,+EAA+E;;;AAG/E,wCAAkD;AAElD,SAAS,KAAK,CAAC,EAAU;IACvB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAA;AAC1D,CAAC;AAED,KAAK,UAAU,kBAAkB,CAAC,SAAiB,EAAE,YAAoB;IACvE,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;IAC5C,MAAM,SAAS,GAAG,GAAG,OAAO,SAAS,CAAA;IACrC,MAAM,WAAW,GAAG,EAAE,CAAA;IACtB,IAAI,SAAS,GAAiB,IAAI,CAAA;IAElC,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,WAAW,EAAE,OAAO,EAAE,EAAE,CAAC;QACxD,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAA;QACxC,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,CAAA;QAE1D,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,SAAS,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC,CAAA;YACtE,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,IAAI,KAAK,CAAC,6BAA6B,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAA;YAClE,CAAC;YAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAyC,CAAA;YACzE,IAAI,IAAI,CAAC,OAAO,KAAK,yBAAkB,EAAE,CAAC;gBACxC,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAA;YAC7D,CAAC;YACD,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;gBAChE,MAAM,IAAI,KAAK,CAAC,wCAAwC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAA;YACvE,CAAC;YACD,OAAM;QACR,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;gBAC1D,SAAS,GAAG,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAA;YACjD,CAAC;iBAAM,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;gBAClC,SAAS,GAAG,KAAK,CAAA;YACnB,CAAC;iBAAM,CAAC;gBACN,SAAS,GAAG,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;YACtC,CAAC;YACD,IAAI,OAAO,GAAG,WAAW,EAAE,CAAC;gBAC1B,MAAM,KAAK,CAAC,GAAG,CAAC,CAAA;gBAChB,SAAQ;YACV,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,wBAAwB,SAAS,CAAC,OAAO,KAAK,SAAS,GAAG,CAAC,CAAA;QAC7E,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,OAAO,CAAC,CAAA;QACvB,CAAC;IACH,CAAC;AACH,CAAC;AACD,2CAAsD;AAEtD,MAAa,cAAc;IACjB,SAAS,GAAgC,IAAI,GAAG,EAAE,CAAA;IAClD,YAAY,GAA0B,IAAI,CAAA;IAC1C,SAAS,GAAkB,IAAI,CAAA;IAC/B,SAAS,GAAkB,IAAI,CAAA;IAEvC;QACE,MAAM,QAAQ,GAAG,IAAI,oCAAwB,EAAE,CAAA;QAC/C,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAA;IAC3C,CAAC;IAED,KAAK,CAAC,YAAY;QAChB,MAAM,OAAO,GAAmB,EAAE,CAAA;QAClC,KAAK,MAAM,CAAC,EAAE,EAAE,QAAQ,CAAC,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YAC5C,MAAM,aAAa,GAAG,MAAM,QAAQ,CAAC,eAAe,EAAE,CAAA;YACtD,OAAO,CAAC,IAAI,CAAC;gBACX,EAAE;gBACF,IAAI,EAAE,QAAQ,CAAC,IAAI;gBACnB,SAAS,EAAE,MAAM,QAAQ,CAAC,WAAW,EAAE;gBACvC,oBAAoB,EAAE,QAAQ,CAAC,oBAAoB;gBACnD,aAAa;gBACb,YAAY,EAAE,aAAa,CAAC,CAAC,CAAC,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,SAAS;aACvE,CAAC,CAAA;QACJ,CAAC;QACD,OAAO,OAAO,CAAA;IAChB,CAAC;IAED,SAAS;QACP,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,OAAO;gBACL,MAAM,EAAE,IAAI;gBACZ,UAAU,EAAE,IAAI,CAAC,YAAY,CAAC,UAAU;gBACxC,SAAS,EAAE,IAAI,CAAC,YAAY,CAAC,SAAS;gBACtC,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,SAAS;aACvC,CAAA;QACH,CAAC;QACD,OAAO;YACL,MAAM,EAAE,KAAK;YACb,KAAK,EAAE,IAAI,CAAC,SAAS,IAAI,SAAS;SACnC,CAAA;IACH,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,UAAkB,EAAE,SAAiB,EAAE,QAAiB;QAClE,8BAA8B;QAC9B,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,CAAA;YACxB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAA;QAC1B,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC,CAAA;QAC/C,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CAAC,4BAA4B,UAAU,EAAE,CAAC,CAAA;QAC3D,CAAC;QAED,IAAI,CAAC,MAAM,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAC;YAClC,MAAM,IAAI,KAAK,CAAC,mBAAmB,QAAQ,CAAC,IAAI,mBAAmB,CAAC,CAAA;QACtE,CAAC;QAED,MAAM,iBAAiB,GAAG,UAAU,KAAK,YAAY,IAAI,CAAC,QAAQ,CAAA;QAClE,MAAM,WAAW,GAAG,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QAC7C,IAAI,SAAS,GAAiB,IAAI,CAAA;QAElC,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,WAAW,EAAE,OAAO,EAAE,EAAE,CAAC;YACxD,IAAI,CAAC;gBACH,IAAI,CAAC,SAAS,GAAG,IAAI,CAAA;gBACrB,IAAI,CAAC,YAAY,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAA;gBAC7D,IAAI,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;gBACzC,MAAM,kBAAkB,CAAC,IAAI,CAAC,YAAY,CAAC,SAAS,EAAE,SAAS,CAAC,CAAA;gBAChE,OAAO,IAAI,CAAC,SAAS,EAAE,CAAA;YACzB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,SAAS,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;gBACrE,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC,OAAO,CAAA;gBAClC,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;oBACtB,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,CAAA;oBACxB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAA;oBACxB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAA;gBACvB,CAAC;gBACD,IAAI,OAAO,GAAG,WAAW,EAAE,CAAC;oBAC1B,MAAM,KAAK,CAAC,GAAG,CAAC,CAAA;oBAChB,SAAQ;gBACV,CAAC;YACH,CAAC;QACH,CAAC;QAED,MAAM,OAAO,GAAG,SAAS,EAAE,OAAO,IAAI,eAAe,CAAA;QACrD,MAAM,IAAI,KAAK,CACb,WAAW,GAAG,CAAC;YACb,CAAC,CAAC,gCAAgC,WAAW,cAAc,OAAO,EAAE;YACpE,CAAC,CAAC,OAAO,CACZ,CAAA;IACH,CAAC;IAED,IAAI;QACF,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,CAAA;YACxB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAA;YACxB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAA;QACvB,CAAC;QACD,OAAO,IAAI,CAAC,SAAS,EAAE,CAAA;IACzB,CAAC;IAED,YAAY;QACV,OAAO,IAAI,CAAC,YAAY,EAAE,SAAS,IAAI,IAAI,CAAA;IAC7C,CAAC;CACF;AAxGD,wCAwGC"}
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/tunnel/registry.ts"],"names":[],"mappings":"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"}
package/index.html CHANGED
@@ -255,8 +255,37 @@
255
255
 
256
256
  <template x-if="!hasTunnel">
257
257
  <div class="space-y-3">
258
- <!-- Named tunnel selector (only when authenticated) -->
259
- <template x-if="selectedProvider?.authenticated && selectedProvider?.namedTunnels?.length">
258
+ <!-- Provider selector -->
259
+ <template x-if="tunnel.providers.length > 1">
260
+ <div class="space-y-2">
261
+ <label class="text-[10px] font-semibold uppercase tracking-wider text-neutral-500">Provider</label>
262
+ <select
263
+ x-model="tunnel.selectedProvider"
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
+ <template x-for="p in tunnel.providers.filter(p => p.available)" :key="p.id">
267
+ <option :value="p.id" x-text="p.name"></option>
268
+ </template>
269
+ </select>
270
+ </div>
271
+ </template>
272
+
273
+ <!-- Custom URL input (for manual provider) -->
274
+ <template x-if="tunnel.selectedProvider === 'manual'">
275
+ <div class="space-y-2">
276
+ <label class="text-[10px] font-semibold uppercase tracking-wider text-neutral-500">Your Public URL</label>
277
+ <input
278
+ type="text"
279
+ x-model="tunnel.customDomain"
280
+ placeholder="https://your-domain.com or http://your-ip:port"
281
+ class="w-full rounded-lg border border-neutral-800 bg-neutral-950 px-3 py-2 text-sm text-neutral-100 placeholder:text-neutral-500 focus:border-neutral-600 focus:outline-none"
282
+ >
283
+ <p class="text-[10px] text-neutral-500">Enter the public URL where this server is accessible (via reverse proxy, port forwarding, etc.)</p>
284
+ </div>
285
+ </template>
286
+
287
+ <!-- Named tunnel selector (only for Cloudflare when authenticated) -->
288
+ <template x-if="tunnel.selectedProvider === 'cloudflare' && selectedProvider?.authenticated && selectedProvider?.namedTunnels?.length">
260
289
  <div class="space-y-2">
261
290
  <label class="text-[10px] font-semibold uppercase tracking-wider text-neutral-500">Use Named Tunnel (optional)</label>
262
291
  <select
@@ -270,6 +299,7 @@
270
299
  </select>
271
300
  </div>
272
301
  </template>
302
+
273
303
  <div class="flex flex-wrap items-center justify-between gap-3">
274
304
  <div class="flex flex-wrap gap-2" x-show="tunnel.loading">
275
305
  <span class="h-7 w-20 rounded-lg border border-neutral-800 bg-neutral-900/60 animate-pulse"></span>
@@ -277,9 +307,9 @@
277
307
  <template x-if="!tunnel.status.active && !hasExternalUrl">
278
308
  <button
279
309
  @click="startTunnel()"
280
- :disabled="tunnel.starting || !tunnel.selectedProvider || !tunnel.providers.some(p => p.available)"
310
+ :disabled="tunnel.starting || !tunnel.selectedProvider || !tunnel.providers.some(p => p.available) || (tunnel.selectedProvider === 'manual' && !tunnel.customDomain)"
281
311
  class="rounded-lg bg-neutral-100 px-3 py-2 text-xs font-semibold text-neutral-950 hover:bg-white disabled:opacity-50"
282
- x-text="tunnel.starting ? 'Creating...' : 'Create Tunnel'"
312
+ x-text="tunnel.starting ? (tunnel.selectedProvider === 'manual' ? 'Setting...' : 'Creating...') : (tunnel.selectedProvider === 'manual' ? 'Set URL' : 'Create Tunnel')"
283
313
  x-show="!tunnel.loading"
284
314
  ></button>
285
315
  </template>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sub-bridge",
3
- "version": "1.1.2",
3
+ "version": "1.2.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,3 +3,4 @@
3
3
  // ============================================================================
4
4
 
5
5
  export { CloudflareTunnelProvider } from './cloudflare'
6
+ export { ManualTunnelProvider } from './manual'
@@ -0,0 +1,46 @@
1
+ // ============================================================================
2
+ // Manual/Custom URL Provider - User provides their own public URL
3
+ // ============================================================================
4
+
5
+ import type { TunnelProvider, TunnelInstance, NamedTunnelInfo } from '../types'
6
+
7
+ export class ManualTunnelProvider implements TunnelProvider {
8
+ id = 'manual'
9
+ name = 'Custom URL'
10
+ supportsNamedTunnels = false
11
+
12
+ async isAvailable(): Promise<boolean> {
13
+ return true // Always available - no dependencies
14
+ }
15
+
16
+ async isAuthenticated(): Promise<boolean> {
17
+ return true // No authentication needed
18
+ }
19
+
20
+ async listTunnels(): Promise<NamedTunnelInfo[]> {
21
+ return [] // No named tunnels for manual provider
22
+ }
23
+
24
+ async start(localPort: number, customUrl?: string): Promise<TunnelInstance> {
25
+ if (!customUrl) {
26
+ throw new Error('Custom URL is required. Enter your public URL (e.g., https://api.mydomain.com)')
27
+ }
28
+
29
+ // Normalize URL - ensure it has a protocol
30
+ let publicUrl = customUrl.trim()
31
+ if (!publicUrl.startsWith('http://') && !publicUrl.startsWith('https://')) {
32
+ publicUrl = 'https://' + publicUrl
33
+ }
34
+
35
+ // Remove trailing slash for consistency
36
+ publicUrl = publicUrl.replace(/\/$/, '')
37
+
38
+ return {
39
+ providerId: this.id,
40
+ publicUrl,
41
+ stop: () => {
42
+ // No-op - nothing to stop since no process was started
43
+ }
44
+ }
45
+ }
46
+ }
@@ -51,7 +51,7 @@ async function verifyTunnelHealth(publicUrl: string, expectedPort: number): Prom
51
51
  }
52
52
  }
53
53
  }
54
- import { CloudflareTunnelProvider } from './providers'
54
+ import { CloudflareTunnelProvider, ManualTunnelProvider } from './providers'
55
55
 
56
56
  export class TunnelRegistry {
57
57
  private providers: Map<string, TunnelProvider> = new Map()
@@ -60,8 +60,11 @@ export class TunnelRegistry {
60
60
  private lastError: string | null = null
61
61
 
62
62
  constructor() {
63
- const provider = new CloudflareTunnelProvider()
64
- this.providers.set(provider.id, provider)
63
+ const cloudflare = new CloudflareTunnelProvider()
64
+ this.providers.set(cloudflare.id, cloudflare)
65
+
66
+ const manual = new ManualTunnelProvider()
67
+ this.providers.set(manual.id, manual)
65
68
  }
66
69
 
67
70
  async getProviders(): Promise<ProviderInfo[]> {
@@ -112,6 +115,7 @@ export class TunnelRegistry {
112
115
  }
113
116
 
114
117
  const isQuickCloudflare = providerId === 'cloudflare' && !namedUrl
118
+ const isManual = providerId === 'manual'
115
119
  const maxAttempts = isQuickCloudflare ? 4 : 1
116
120
  let lastError: Error | null = null
117
121
 
@@ -120,7 +124,10 @@ export class TunnelRegistry {
120
124
  this.lastError = null
121
125
  this.activeTunnel = await provider.start(localPort, namedUrl)
122
126
  this.startedAt = new Date().toISOString()
123
- await verifyTunnelHealth(this.activeTunnel.publicUrl, localPort)
127
+ // Skip health check for manual provider - user trusts their own URL
128
+ if (!isManual) {
129
+ await verifyTunnelHealth(this.activeTunnel.publicUrl, localPort)
130
+ }
124
131
  return this.getStatus()
125
132
  } catch (error) {
126
133
  lastError = error instanceof Error ? error : new Error(String(error))