@camstack/addon-cloudflare-tunnel 0.1.3 → 0.1.5

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.
@@ -0,0 +1,174 @@
1
+ // src/cloudflare-tunnel.ts
2
+ import { randomUUID } from "crypto";
3
+ var CloudflareTunnelService = class {
4
+ constructor(config, logger, eventBus, processManager) {
5
+ this.config = config;
6
+ this.logger = logger;
7
+ this.eventBus = eventBus;
8
+ this.processManager = processManager;
9
+ }
10
+ id = "cloudflare-tunnel";
11
+ type = "cloudflare";
12
+ endpoint = null;
13
+ processId = null;
14
+ async start() {
15
+ this.logger.info(`Starting Cloudflare tunnel (${this.config.mode})`);
16
+ const processConfig = {
17
+ id: "cloudflared-tunnel",
18
+ label: "Cloudflare Tunnel",
19
+ command: "cloudflared",
20
+ args: this.config.mode === "quick" ? ["tunnel", "--url", `http://localhost:${this.config.localPort}`] : ["tunnel", "run", "--token", this.config.namedTunnelToken],
21
+ autoRestart: true,
22
+ maxRestarts: 5
23
+ };
24
+ this.processManager.register(processConfig);
25
+ await this.processManager.start("cloudflared-tunnel");
26
+ this.processId = "cloudflared-tunnel";
27
+ const publicUrl = this.config.mode === "named" ? "https://tunnel.example.com" : "https://pending.trycloudflare.com";
28
+ this.endpoint = {
29
+ id: "cloudflare-tunnel",
30
+ type: "tunnel",
31
+ provider: "cloudflare",
32
+ url: publicUrl,
33
+ internal: false,
34
+ capabilities: {
35
+ supportsWebRTC: false,
36
+ supportsWebSocket: true,
37
+ supportsSSE: true
38
+ },
39
+ priority: 50,
40
+ status: "online"
41
+ };
42
+ this.eventBus.emit({
43
+ id: randomUUID(),
44
+ timestamp: /* @__PURE__ */ new Date(),
45
+ source: { type: "addon", id: "cloudflare-tunnel" },
46
+ category: "network.tunnel.started",
47
+ data: { url: publicUrl }
48
+ });
49
+ return this.endpoint;
50
+ }
51
+ async stop() {
52
+ if (this.processId) {
53
+ await this.processManager.stop(this.processId);
54
+ }
55
+ this.endpoint = null;
56
+ this.eventBus.emit({
57
+ id: randomUUID(),
58
+ timestamp: /* @__PURE__ */ new Date(),
59
+ source: { type: "addon", id: "cloudflare-tunnel" },
60
+ category: "network.tunnel.stopped",
61
+ data: {}
62
+ });
63
+ }
64
+ getEndpoint() {
65
+ return this.endpoint;
66
+ }
67
+ getStatus() {
68
+ return {
69
+ connected: this.endpoint !== null,
70
+ publicUrl: this.endpoint?.url
71
+ };
72
+ }
73
+ };
74
+
75
+ // src/cloudflare-tunnel.addon.ts
76
+ var CloudflareTunnelAddon = class {
77
+ manifest = {
78
+ id: "cloudflare-tunnel",
79
+ name: "Cloudflare Tunnel",
80
+ version: "1.0.0",
81
+ capabilities: ["network-access"]
82
+ };
83
+ service = null;
84
+ currentConfig = {
85
+ mode: "quick",
86
+ localPort: 3e3
87
+ };
88
+ async initialize(context) {
89
+ this.currentConfig = {
90
+ mode: context.addonConfig.mode ?? this.currentConfig.mode,
91
+ namedTunnelToken: context.addonConfig.namedTunnelToken ?? this.currentConfig.namedTunnelToken,
92
+ localPort: context.addonConfig.localPort ?? this.currentConfig.localPort
93
+ };
94
+ context.logger.info("Cloudflare Tunnel addon initialized");
95
+ }
96
+ async shutdown() {
97
+ await this.service?.stop();
98
+ this.service = null;
99
+ }
100
+ /** Provide the ProcessManagerService to enable tunnel management */
101
+ setProcessManager(processManager, config, context) {
102
+ this.service = new CloudflareTunnelService(
103
+ config,
104
+ context.logger,
105
+ context.eventBus,
106
+ processManager
107
+ );
108
+ }
109
+ getService() {
110
+ if (!this.service) throw new Error("Cloudflare Tunnel not initialized");
111
+ return this.service;
112
+ }
113
+ getCapabilityProvider(name) {
114
+ if (name === "network-access" && this.service) {
115
+ return this.service;
116
+ }
117
+ return null;
118
+ }
119
+ getConfigSchema() {
120
+ return {
121
+ sections: [
122
+ {
123
+ id: "tunnel",
124
+ title: "Tunnel Settings",
125
+ fields: [
126
+ {
127
+ type: "select",
128
+ key: "mode",
129
+ label: "Tunnel Mode",
130
+ description: "Quick mode creates a temporary public URL; Named mode uses a persistent named tunnel with your Cloudflare account.",
131
+ options: [
132
+ { value: "quick", label: "Quick Tunnel", description: "Temporary public URL, no Cloudflare account required" },
133
+ { value: "named", label: "Named Tunnel", description: "Persistent tunnel using a Cloudflare tunnel token" }
134
+ ]
135
+ },
136
+ {
137
+ type: "password",
138
+ key: "namedTunnelToken",
139
+ label: "Tunnel Token",
140
+ description: "Token from your Cloudflare Zero Trust dashboard (required for Named Tunnel mode)",
141
+ showToggle: true,
142
+ showWhen: { field: "mode", equals: "named" }
143
+ },
144
+ {
145
+ type: "number",
146
+ key: "localPort",
147
+ label: "Local Port",
148
+ description: "The local port that the tunnel will expose publicly",
149
+ min: 1,
150
+ max: 65535,
151
+ step: 1
152
+ }
153
+ ]
154
+ }
155
+ ]
156
+ };
157
+ }
158
+ getConfig() {
159
+ return { ...this.currentConfig };
160
+ }
161
+ async onConfigChange(config) {
162
+ this.currentConfig = {
163
+ mode: config.mode ?? this.currentConfig.mode,
164
+ namedTunnelToken: config.namedTunnelToken ?? this.currentConfig.namedTunnelToken,
165
+ localPort: config.localPort ?? this.currentConfig.localPort
166
+ };
167
+ }
168
+ };
169
+
170
+ export {
171
+ CloudflareTunnelService,
172
+ CloudflareTunnelAddon
173
+ };
174
+ //# sourceMappingURL=chunk-HHH5U2SN.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cloudflare-tunnel.ts","../src/cloudflare-tunnel.addon.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto'\nimport type {\n IScopedLogger, IEventBus,\n INetworkAccessProvider, INetworkEndpoint, NetworkAccessStatus,\n IProcessManager, ProcessConfig,\n} from '@camstack/types'\n\nexport interface CloudflareTunnelConfig {\n readonly mode: 'quick' | 'named'\n readonly namedTunnelToken?: string\n readonly localPort: number\n}\n\nexport class CloudflareTunnelService implements INetworkAccessProvider {\n readonly id = 'cloudflare-tunnel'\n readonly type = 'cloudflare'\n\n private endpoint: INetworkEndpoint | null = null\n private processId: string | null = null\n\n constructor(\n private readonly config: CloudflareTunnelConfig,\n private readonly logger: IScopedLogger,\n private readonly eventBus: IEventBus,\n private readonly processManager: IProcessManager,\n ) {}\n\n async start(): Promise<INetworkEndpoint> {\n this.logger.info(`Starting Cloudflare tunnel (${this.config.mode})`)\n\n const processConfig: ProcessConfig = {\n id: 'cloudflared-tunnel',\n label: 'Cloudflare Tunnel',\n command: 'cloudflared',\n args:\n this.config.mode === 'quick'\n ? ['tunnel', '--url', `http://localhost:${this.config.localPort}`]\n : ['tunnel', 'run', '--token', this.config.namedTunnelToken!],\n autoRestart: true,\n maxRestarts: 5,\n }\n\n this.processManager.register(processConfig)\n await this.processManager.start('cloudflared-tunnel')\n this.processId = 'cloudflared-tunnel'\n\n const publicUrl =\n this.config.mode === 'named'\n ? 'https://tunnel.example.com'\n : 'https://pending.trycloudflare.com'\n\n this.endpoint = {\n id: 'cloudflare-tunnel',\n type: 'tunnel',\n provider: 'cloudflare',\n url: publicUrl,\n internal: false,\n capabilities: {\n supportsWebRTC: false,\n supportsWebSocket: true,\n supportsSSE: true,\n },\n priority: 50,\n status: 'online',\n }\n\n this.eventBus.emit({\n id: randomUUID(),\n timestamp: new Date(),\n source: { type: 'addon', id: 'cloudflare-tunnel' },\n category: 'network.tunnel.started',\n data: { url: publicUrl },\n })\n\n return this.endpoint\n }\n\n async stop(): Promise<void> {\n if (this.processId) {\n await this.processManager.stop(this.processId)\n }\n this.endpoint = null\n\n this.eventBus.emit({\n id: randomUUID(),\n timestamp: new Date(),\n source: { type: 'addon', id: 'cloudflare-tunnel' },\n category: 'network.tunnel.stopped',\n data: {},\n })\n }\n\n getEndpoint(): INetworkEndpoint | null {\n return this.endpoint\n }\n\n getStatus(): NetworkAccessStatus {\n return {\n connected: this.endpoint !== null,\n publicUrl: this.endpoint?.url,\n }\n }\n}\n","import type {\n ICamstackAddon, AddonManifest, AddonContext,\n IConfigurable, ConfigUISchema, CapabilityProviderMap,\n IProcessManager,\n} from '@camstack/types'\nimport { CloudflareTunnelService } from './cloudflare-tunnel'\nimport type { CloudflareTunnelConfig } from './cloudflare-tunnel'\n\nexport class CloudflareTunnelAddon implements ICamstackAddon, IConfigurable {\n readonly manifest: AddonManifest = {\n id: 'cloudflare-tunnel',\n name: 'Cloudflare Tunnel',\n version: '1.0.0',\n capabilities: ['network-access'],\n }\n\n private service: CloudflareTunnelService | null = null\n private currentConfig: Partial<CloudflareTunnelConfig> = {\n mode: 'quick',\n localPort: 3000,\n }\n\n async initialize(context: AddonContext): Promise<void> {\n this.currentConfig = {\n mode: (context.addonConfig.mode as CloudflareTunnelConfig['mode']) ?? this.currentConfig.mode,\n namedTunnelToken: (context.addonConfig.namedTunnelToken as string | undefined) ?? this.currentConfig.namedTunnelToken,\n localPort: (context.addonConfig.localPort as number) ?? this.currentConfig.localPort,\n }\n // Service creation is deferred until processManager is provided via setProcessManager\n context.logger.info('Cloudflare Tunnel addon initialized')\n }\n\n async shutdown(): Promise<void> {\n await this.service?.stop()\n this.service = null\n }\n\n /** Provide the ProcessManagerService to enable tunnel management */\n setProcessManager(\n processManager: IProcessManager,\n config: CloudflareTunnelConfig,\n context: AddonContext,\n ): void {\n this.service = new CloudflareTunnelService(\n config,\n context.logger,\n context.eventBus,\n processManager,\n )\n }\n\n getService(): CloudflareTunnelService {\n if (!this.service) throw new Error('Cloudflare Tunnel not initialized')\n return this.service\n }\n\n getCapabilityProvider<K extends keyof CapabilityProviderMap>(\n name: K,\n ): CapabilityProviderMap[K] | null {\n if (name === 'network-access' && this.service) {\n return this.service as unknown as CapabilityProviderMap[K]\n }\n return null\n }\n\n getConfigSchema(): ConfigUISchema {\n return {\n sections: [\n {\n id: 'tunnel',\n title: 'Tunnel Settings',\n fields: [\n {\n type: 'select',\n key: 'mode',\n label: 'Tunnel Mode',\n description: 'Quick mode creates a temporary public URL; Named mode uses a persistent named tunnel with your Cloudflare account.',\n options: [\n { value: 'quick', label: 'Quick Tunnel', description: 'Temporary public URL, no Cloudflare account required' },\n { value: 'named', label: 'Named Tunnel', description: 'Persistent tunnel using a Cloudflare tunnel token' },\n ],\n },\n {\n type: 'password',\n key: 'namedTunnelToken',\n label: 'Tunnel Token',\n description: 'Token from your Cloudflare Zero Trust dashboard (required for Named Tunnel mode)',\n showToggle: true,\n showWhen: { field: 'mode', equals: 'named' },\n },\n {\n type: 'number',\n key: 'localPort',\n label: 'Local Port',\n description: 'The local port that the tunnel will expose publicly',\n min: 1,\n max: 65535,\n step: 1,\n },\n ],\n },\n ],\n }\n }\n\n getConfig(): Record<string, unknown> {\n return { ...this.currentConfig }\n }\n\n async onConfigChange(config: Record<string, unknown>): Promise<void> {\n this.currentConfig = {\n mode: (config.mode as CloudflareTunnelConfig['mode']) ?? this.currentConfig.mode,\n namedTunnelToken: (config.namedTunnelToken as string | undefined) ?? this.currentConfig.namedTunnelToken,\n localPort: (config.localPort as number) ?? this.currentConfig.localPort,\n }\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,199 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/cloudflare-tunnel.addon.ts
21
+ var cloudflare_tunnel_addon_exports = {};
22
+ __export(cloudflare_tunnel_addon_exports, {
23
+ CloudflareTunnelAddon: () => CloudflareTunnelAddon
24
+ });
25
+ module.exports = __toCommonJS(cloudflare_tunnel_addon_exports);
26
+
27
+ // src/cloudflare-tunnel.ts
28
+ var import_node_crypto = require("crypto");
29
+ var CloudflareTunnelService = class {
30
+ constructor(config, logger, eventBus, processManager) {
31
+ this.config = config;
32
+ this.logger = logger;
33
+ this.eventBus = eventBus;
34
+ this.processManager = processManager;
35
+ }
36
+ id = "cloudflare-tunnel";
37
+ type = "cloudflare";
38
+ endpoint = null;
39
+ processId = null;
40
+ async start() {
41
+ this.logger.info(`Starting Cloudflare tunnel (${this.config.mode})`);
42
+ const processConfig = {
43
+ id: "cloudflared-tunnel",
44
+ label: "Cloudflare Tunnel",
45
+ command: "cloudflared",
46
+ args: this.config.mode === "quick" ? ["tunnel", "--url", `http://localhost:${this.config.localPort}`] : ["tunnel", "run", "--token", this.config.namedTunnelToken],
47
+ autoRestart: true,
48
+ maxRestarts: 5
49
+ };
50
+ this.processManager.register(processConfig);
51
+ await this.processManager.start("cloudflared-tunnel");
52
+ this.processId = "cloudflared-tunnel";
53
+ const publicUrl = this.config.mode === "named" ? "https://tunnel.example.com" : "https://pending.trycloudflare.com";
54
+ this.endpoint = {
55
+ id: "cloudflare-tunnel",
56
+ type: "tunnel",
57
+ provider: "cloudflare",
58
+ url: publicUrl,
59
+ internal: false,
60
+ capabilities: {
61
+ supportsWebRTC: false,
62
+ supportsWebSocket: true,
63
+ supportsSSE: true
64
+ },
65
+ priority: 50,
66
+ status: "online"
67
+ };
68
+ this.eventBus.emit({
69
+ id: (0, import_node_crypto.randomUUID)(),
70
+ timestamp: /* @__PURE__ */ new Date(),
71
+ source: { type: "addon", id: "cloudflare-tunnel" },
72
+ category: "network.tunnel.started",
73
+ data: { url: publicUrl }
74
+ });
75
+ return this.endpoint;
76
+ }
77
+ async stop() {
78
+ if (this.processId) {
79
+ await this.processManager.stop(this.processId);
80
+ }
81
+ this.endpoint = null;
82
+ this.eventBus.emit({
83
+ id: (0, import_node_crypto.randomUUID)(),
84
+ timestamp: /* @__PURE__ */ new Date(),
85
+ source: { type: "addon", id: "cloudflare-tunnel" },
86
+ category: "network.tunnel.stopped",
87
+ data: {}
88
+ });
89
+ }
90
+ getEndpoint() {
91
+ return this.endpoint;
92
+ }
93
+ getStatus() {
94
+ return {
95
+ connected: this.endpoint !== null,
96
+ publicUrl: this.endpoint?.url
97
+ };
98
+ }
99
+ };
100
+
101
+ // src/cloudflare-tunnel.addon.ts
102
+ var CloudflareTunnelAddon = class {
103
+ manifest = {
104
+ id: "cloudflare-tunnel",
105
+ name: "Cloudflare Tunnel",
106
+ version: "1.0.0",
107
+ capabilities: ["network-access"]
108
+ };
109
+ service = null;
110
+ currentConfig = {
111
+ mode: "quick",
112
+ localPort: 3e3
113
+ };
114
+ async initialize(context) {
115
+ this.currentConfig = {
116
+ mode: context.addonConfig.mode ?? this.currentConfig.mode,
117
+ namedTunnelToken: context.addonConfig.namedTunnelToken ?? this.currentConfig.namedTunnelToken,
118
+ localPort: context.addonConfig.localPort ?? this.currentConfig.localPort
119
+ };
120
+ context.logger.info("Cloudflare Tunnel addon initialized");
121
+ }
122
+ async shutdown() {
123
+ await this.service?.stop();
124
+ this.service = null;
125
+ }
126
+ /** Provide the ProcessManagerService to enable tunnel management */
127
+ setProcessManager(processManager, config, context) {
128
+ this.service = new CloudflareTunnelService(
129
+ config,
130
+ context.logger,
131
+ context.eventBus,
132
+ processManager
133
+ );
134
+ }
135
+ getService() {
136
+ if (!this.service) throw new Error("Cloudflare Tunnel not initialized");
137
+ return this.service;
138
+ }
139
+ getCapabilityProvider(name) {
140
+ if (name === "network-access" && this.service) {
141
+ return this.service;
142
+ }
143
+ return null;
144
+ }
145
+ getConfigSchema() {
146
+ return {
147
+ sections: [
148
+ {
149
+ id: "tunnel",
150
+ title: "Tunnel Settings",
151
+ fields: [
152
+ {
153
+ type: "select",
154
+ key: "mode",
155
+ label: "Tunnel Mode",
156
+ description: "Quick mode creates a temporary public URL; Named mode uses a persistent named tunnel with your Cloudflare account.",
157
+ options: [
158
+ { value: "quick", label: "Quick Tunnel", description: "Temporary public URL, no Cloudflare account required" },
159
+ { value: "named", label: "Named Tunnel", description: "Persistent tunnel using a Cloudflare tunnel token" }
160
+ ]
161
+ },
162
+ {
163
+ type: "password",
164
+ key: "namedTunnelToken",
165
+ label: "Tunnel Token",
166
+ description: "Token from your Cloudflare Zero Trust dashboard (required for Named Tunnel mode)",
167
+ showToggle: true,
168
+ showWhen: { field: "mode", equals: "named" }
169
+ },
170
+ {
171
+ type: "number",
172
+ key: "localPort",
173
+ label: "Local Port",
174
+ description: "The local port that the tunnel will expose publicly",
175
+ min: 1,
176
+ max: 65535,
177
+ step: 1
178
+ }
179
+ ]
180
+ }
181
+ ]
182
+ };
183
+ }
184
+ getConfig() {
185
+ return { ...this.currentConfig };
186
+ }
187
+ async onConfigChange(config) {
188
+ this.currentConfig = {
189
+ mode: config.mode ?? this.currentConfig.mode,
190
+ namedTunnelToken: config.namedTunnelToken ?? this.currentConfig.namedTunnelToken,
191
+ localPort: config.localPort ?? this.currentConfig.localPort
192
+ };
193
+ }
194
+ };
195
+ // Annotate the CommonJS export names for ESM import in node:
196
+ 0 && (module.exports = {
197
+ CloudflareTunnelAddon
198
+ });
199
+ //# sourceMappingURL=cloudflare-tunnel.addon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cloudflare-tunnel.addon.ts","../src/cloudflare-tunnel.ts"],"sourcesContent":["import type {\n ICamstackAddon, AddonManifest, AddonContext,\n IConfigurable, ConfigUISchema, CapabilityProviderMap,\n IProcessManager,\n} from '@camstack/types'\nimport { CloudflareTunnelService } from './cloudflare-tunnel'\nimport type { CloudflareTunnelConfig } from './cloudflare-tunnel'\n\nexport class CloudflareTunnelAddon implements ICamstackAddon, IConfigurable {\n readonly manifest: AddonManifest = {\n id: 'cloudflare-tunnel',\n name: 'Cloudflare Tunnel',\n version: '1.0.0',\n capabilities: ['network-access'],\n }\n\n private service: CloudflareTunnelService | null = null\n private currentConfig: Partial<CloudflareTunnelConfig> = {\n mode: 'quick',\n localPort: 3000,\n }\n\n async initialize(context: AddonContext): Promise<void> {\n this.currentConfig = {\n mode: (context.addonConfig.mode as CloudflareTunnelConfig['mode']) ?? this.currentConfig.mode,\n namedTunnelToken: (context.addonConfig.namedTunnelToken as string | undefined) ?? this.currentConfig.namedTunnelToken,\n localPort: (context.addonConfig.localPort as number) ?? this.currentConfig.localPort,\n }\n // Service creation is deferred until processManager is provided via setProcessManager\n context.logger.info('Cloudflare Tunnel addon initialized')\n }\n\n async shutdown(): Promise<void> {\n await this.service?.stop()\n this.service = null\n }\n\n /** Provide the ProcessManagerService to enable tunnel management */\n setProcessManager(\n processManager: IProcessManager,\n config: CloudflareTunnelConfig,\n context: AddonContext,\n ): void {\n this.service = new CloudflareTunnelService(\n config,\n context.logger,\n context.eventBus,\n processManager,\n )\n }\n\n getService(): CloudflareTunnelService {\n if (!this.service) throw new Error('Cloudflare Tunnel not initialized')\n return this.service\n }\n\n getCapabilityProvider<K extends keyof CapabilityProviderMap>(\n name: K,\n ): CapabilityProviderMap[K] | null {\n if (name === 'network-access' && this.service) {\n return this.service as unknown as CapabilityProviderMap[K]\n }\n return null\n }\n\n getConfigSchema(): ConfigUISchema {\n return {\n sections: [\n {\n id: 'tunnel',\n title: 'Tunnel Settings',\n fields: [\n {\n type: 'select',\n key: 'mode',\n label: 'Tunnel Mode',\n description: 'Quick mode creates a temporary public URL; Named mode uses a persistent named tunnel with your Cloudflare account.',\n options: [\n { value: 'quick', label: 'Quick Tunnel', description: 'Temporary public URL, no Cloudflare account required' },\n { value: 'named', label: 'Named Tunnel', description: 'Persistent tunnel using a Cloudflare tunnel token' },\n ],\n },\n {\n type: 'password',\n key: 'namedTunnelToken',\n label: 'Tunnel Token',\n description: 'Token from your Cloudflare Zero Trust dashboard (required for Named Tunnel mode)',\n showToggle: true,\n showWhen: { field: 'mode', equals: 'named' },\n },\n {\n type: 'number',\n key: 'localPort',\n label: 'Local Port',\n description: 'The local port that the tunnel will expose publicly',\n min: 1,\n max: 65535,\n step: 1,\n },\n ],\n },\n ],\n }\n }\n\n getConfig(): Record<string, unknown> {\n return { ...this.currentConfig }\n }\n\n async onConfigChange(config: Record<string, unknown>): Promise<void> {\n this.currentConfig = {\n mode: (config.mode as CloudflareTunnelConfig['mode']) ?? this.currentConfig.mode,\n namedTunnelToken: (config.namedTunnelToken as string | undefined) ?? this.currentConfig.namedTunnelToken,\n localPort: (config.localPort as number) ?? this.currentConfig.localPort,\n }\n }\n}\n","import { randomUUID } from 'node:crypto'\nimport type {\n IScopedLogger, IEventBus,\n INetworkAccessProvider, INetworkEndpoint, NetworkAccessStatus,\n IProcessManager, ProcessConfig,\n} from '@camstack/types'\n\nexport interface CloudflareTunnelConfig {\n readonly mode: 'quick' | 'named'\n readonly namedTunnelToken?: string\n readonly localPort: number\n}\n\nexport class CloudflareTunnelService implements INetworkAccessProvider {\n readonly id = 'cloudflare-tunnel'\n readonly type = 'cloudflare'\n\n private endpoint: INetworkEndpoint | null = null\n private processId: string | null = null\n\n constructor(\n private readonly config: CloudflareTunnelConfig,\n private readonly logger: IScopedLogger,\n private readonly eventBus: IEventBus,\n private readonly processManager: IProcessManager,\n ) {}\n\n async start(): Promise<INetworkEndpoint> {\n this.logger.info(`Starting Cloudflare tunnel (${this.config.mode})`)\n\n const processConfig: ProcessConfig = {\n id: 'cloudflared-tunnel',\n label: 'Cloudflare Tunnel',\n command: 'cloudflared',\n args:\n this.config.mode === 'quick'\n ? ['tunnel', '--url', `http://localhost:${this.config.localPort}`]\n : ['tunnel', 'run', '--token', this.config.namedTunnelToken!],\n autoRestart: true,\n maxRestarts: 5,\n }\n\n this.processManager.register(processConfig)\n await this.processManager.start('cloudflared-tunnel')\n this.processId = 'cloudflared-tunnel'\n\n const publicUrl =\n this.config.mode === 'named'\n ? 'https://tunnel.example.com'\n : 'https://pending.trycloudflare.com'\n\n this.endpoint = {\n id: 'cloudflare-tunnel',\n type: 'tunnel',\n provider: 'cloudflare',\n url: publicUrl,\n internal: false,\n capabilities: {\n supportsWebRTC: false,\n supportsWebSocket: true,\n supportsSSE: true,\n },\n priority: 50,\n status: 'online',\n }\n\n this.eventBus.emit({\n id: randomUUID(),\n timestamp: new Date(),\n source: { type: 'addon', id: 'cloudflare-tunnel' },\n category: 'network.tunnel.started',\n data: { url: publicUrl },\n })\n\n return this.endpoint\n }\n\n async stop(): Promise<void> {\n if (this.processId) {\n await this.processManager.stop(this.processId)\n }\n this.endpoint = null\n\n this.eventBus.emit({\n id: randomUUID(),\n timestamp: new Date(),\n source: { type: 'addon', id: 'cloudflare-tunnel' },\n category: 'network.tunnel.stopped',\n data: {},\n })\n }\n\n getEndpoint(): INetworkEndpoint | null {\n return this.endpoint\n }\n\n getStatus(): NetworkAccessStatus {\n return {\n connected: this.endpoint !== null,\n publicUrl: this.endpoint?.url,\n }\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,7 @@
1
+ import {
2
+ CloudflareTunnelAddon
3
+ } from "./chunk-HHH5U2SN.mjs";
4
+ export {
5
+ CloudflareTunnelAddon
6
+ };
7
+ //# sourceMappingURL=cloudflare-tunnel.addon.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/dist/index.mjs CHANGED
@@ -1,171 +1,7 @@
1
- // src/cloudflare-tunnel.ts
2
- import { randomUUID } from "crypto";
3
- var CloudflareTunnelService = class {
4
- constructor(config, logger, eventBus, processManager) {
5
- this.config = config;
6
- this.logger = logger;
7
- this.eventBus = eventBus;
8
- this.processManager = processManager;
9
- }
10
- id = "cloudflare-tunnel";
11
- type = "cloudflare";
12
- endpoint = null;
13
- processId = null;
14
- async start() {
15
- this.logger.info(`Starting Cloudflare tunnel (${this.config.mode})`);
16
- const processConfig = {
17
- id: "cloudflared-tunnel",
18
- label: "Cloudflare Tunnel",
19
- command: "cloudflared",
20
- args: this.config.mode === "quick" ? ["tunnel", "--url", `http://localhost:${this.config.localPort}`] : ["tunnel", "run", "--token", this.config.namedTunnelToken],
21
- autoRestart: true,
22
- maxRestarts: 5
23
- };
24
- this.processManager.register(processConfig);
25
- await this.processManager.start("cloudflared-tunnel");
26
- this.processId = "cloudflared-tunnel";
27
- const publicUrl = this.config.mode === "named" ? "https://tunnel.example.com" : "https://pending.trycloudflare.com";
28
- this.endpoint = {
29
- id: "cloudflare-tunnel",
30
- type: "tunnel",
31
- provider: "cloudflare",
32
- url: publicUrl,
33
- internal: false,
34
- capabilities: {
35
- supportsWebRTC: false,
36
- supportsWebSocket: true,
37
- supportsSSE: true
38
- },
39
- priority: 50,
40
- status: "online"
41
- };
42
- this.eventBus.emit({
43
- id: randomUUID(),
44
- timestamp: /* @__PURE__ */ new Date(),
45
- source: { type: "addon", id: "cloudflare-tunnel" },
46
- category: "network.tunnel.started",
47
- data: { url: publicUrl }
48
- });
49
- return this.endpoint;
50
- }
51
- async stop() {
52
- if (this.processId) {
53
- await this.processManager.stop(this.processId);
54
- }
55
- this.endpoint = null;
56
- this.eventBus.emit({
57
- id: randomUUID(),
58
- timestamp: /* @__PURE__ */ new Date(),
59
- source: { type: "addon", id: "cloudflare-tunnel" },
60
- category: "network.tunnel.stopped",
61
- data: {}
62
- });
63
- }
64
- getEndpoint() {
65
- return this.endpoint;
66
- }
67
- getStatus() {
68
- return {
69
- connected: this.endpoint !== null,
70
- publicUrl: this.endpoint?.url
71
- };
72
- }
73
- };
74
-
75
- // src/cloudflare-tunnel.addon.ts
76
- var CloudflareTunnelAddon = class {
77
- manifest = {
78
- id: "cloudflare-tunnel",
79
- name: "Cloudflare Tunnel",
80
- version: "1.0.0",
81
- capabilities: ["network-access"]
82
- };
83
- service = null;
84
- currentConfig = {
85
- mode: "quick",
86
- localPort: 3e3
87
- };
88
- async initialize(context) {
89
- this.currentConfig = {
90
- mode: context.addonConfig.mode ?? this.currentConfig.mode,
91
- namedTunnelToken: context.addonConfig.namedTunnelToken ?? this.currentConfig.namedTunnelToken,
92
- localPort: context.addonConfig.localPort ?? this.currentConfig.localPort
93
- };
94
- context.logger.info("Cloudflare Tunnel addon initialized");
95
- }
96
- async shutdown() {
97
- await this.service?.stop();
98
- this.service = null;
99
- }
100
- /** Provide the ProcessManagerService to enable tunnel management */
101
- setProcessManager(processManager, config, context) {
102
- this.service = new CloudflareTunnelService(
103
- config,
104
- context.logger,
105
- context.eventBus,
106
- processManager
107
- );
108
- }
109
- getService() {
110
- if (!this.service) throw new Error("Cloudflare Tunnel not initialized");
111
- return this.service;
112
- }
113
- getCapabilityProvider(name) {
114
- if (name === "network-access" && this.service) {
115
- return this.service;
116
- }
117
- return null;
118
- }
119
- getConfigSchema() {
120
- return {
121
- sections: [
122
- {
123
- id: "tunnel",
124
- title: "Tunnel Settings",
125
- fields: [
126
- {
127
- type: "select",
128
- key: "mode",
129
- label: "Tunnel Mode",
130
- description: "Quick mode creates a temporary public URL; Named mode uses a persistent named tunnel with your Cloudflare account.",
131
- options: [
132
- { value: "quick", label: "Quick Tunnel", description: "Temporary public URL, no Cloudflare account required" },
133
- { value: "named", label: "Named Tunnel", description: "Persistent tunnel using a Cloudflare tunnel token" }
134
- ]
135
- },
136
- {
137
- type: "password",
138
- key: "namedTunnelToken",
139
- label: "Tunnel Token",
140
- description: "Token from your Cloudflare Zero Trust dashboard (required for Named Tunnel mode)",
141
- showToggle: true,
142
- showWhen: { field: "mode", equals: "named" }
143
- },
144
- {
145
- type: "number",
146
- key: "localPort",
147
- label: "Local Port",
148
- description: "The local port that the tunnel will expose publicly",
149
- min: 1,
150
- max: 65535,
151
- step: 1
152
- }
153
- ]
154
- }
155
- ]
156
- };
157
- }
158
- getConfig() {
159
- return { ...this.currentConfig };
160
- }
161
- async onConfigChange(config) {
162
- this.currentConfig = {
163
- mode: config.mode ?? this.currentConfig.mode,
164
- namedTunnelToken: config.namedTunnelToken ?? this.currentConfig.namedTunnelToken,
165
- localPort: config.localPort ?? this.currentConfig.localPort
166
- };
167
- }
168
- };
1
+ import {
2
+ CloudflareTunnelAddon,
3
+ CloudflareTunnelService
4
+ } from "./chunk-HHH5U2SN.mjs";
169
5
  export {
170
6
  CloudflareTunnelAddon,
171
7
  CloudflareTunnelService
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cloudflare-tunnel.ts","../src/cloudflare-tunnel.addon.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto'\nimport type {\n IScopedLogger, IEventBus,\n INetworkAccessProvider, INetworkEndpoint, NetworkAccessStatus,\n IProcessManager, ProcessConfig,\n} from '@camstack/types'\n\nexport interface CloudflareTunnelConfig {\n readonly mode: 'quick' | 'named'\n readonly namedTunnelToken?: string\n readonly localPort: number\n}\n\nexport class CloudflareTunnelService implements INetworkAccessProvider {\n readonly id = 'cloudflare-tunnel'\n readonly type = 'cloudflare'\n\n private endpoint: INetworkEndpoint | null = null\n private processId: string | null = null\n\n constructor(\n private readonly config: CloudflareTunnelConfig,\n private readonly logger: IScopedLogger,\n private readonly eventBus: IEventBus,\n private readonly processManager: IProcessManager,\n ) {}\n\n async start(): Promise<INetworkEndpoint> {\n this.logger.info(`Starting Cloudflare tunnel (${this.config.mode})`)\n\n const processConfig: ProcessConfig = {\n id: 'cloudflared-tunnel',\n label: 'Cloudflare Tunnel',\n command: 'cloudflared',\n args:\n this.config.mode === 'quick'\n ? ['tunnel', '--url', `http://localhost:${this.config.localPort}`]\n : ['tunnel', 'run', '--token', this.config.namedTunnelToken!],\n autoRestart: true,\n maxRestarts: 5,\n }\n\n this.processManager.register(processConfig)\n await this.processManager.start('cloudflared-tunnel')\n this.processId = 'cloudflared-tunnel'\n\n const publicUrl =\n this.config.mode === 'named'\n ? 'https://tunnel.example.com'\n : 'https://pending.trycloudflare.com'\n\n this.endpoint = {\n id: 'cloudflare-tunnel',\n type: 'tunnel',\n provider: 'cloudflare',\n url: publicUrl,\n internal: false,\n capabilities: {\n supportsWebRTC: false,\n supportsWebSocket: true,\n supportsSSE: true,\n },\n priority: 50,\n status: 'online',\n }\n\n this.eventBus.emit({\n id: randomUUID(),\n timestamp: new Date(),\n source: { type: 'addon', id: 'cloudflare-tunnel' },\n category: 'network.tunnel.started',\n data: { url: publicUrl },\n })\n\n return this.endpoint\n }\n\n async stop(): Promise<void> {\n if (this.processId) {\n await this.processManager.stop(this.processId)\n }\n this.endpoint = null\n\n this.eventBus.emit({\n id: randomUUID(),\n timestamp: new Date(),\n source: { type: 'addon', id: 'cloudflare-tunnel' },\n category: 'network.tunnel.stopped',\n data: {},\n })\n }\n\n getEndpoint(): INetworkEndpoint | null {\n return this.endpoint\n }\n\n getStatus(): NetworkAccessStatus {\n return {\n connected: this.endpoint !== null,\n publicUrl: this.endpoint?.url,\n }\n }\n}\n","import type {\n ICamstackAddon, AddonManifest, AddonContext,\n IConfigurable, ConfigUISchema, CapabilityProviderMap,\n IProcessManager,\n} from '@camstack/types'\nimport { CloudflareTunnelService } from './cloudflare-tunnel'\nimport type { CloudflareTunnelConfig } from './cloudflare-tunnel'\n\nexport class CloudflareTunnelAddon implements ICamstackAddon, IConfigurable {\n readonly manifest: AddonManifest = {\n id: 'cloudflare-tunnel',\n name: 'Cloudflare Tunnel',\n version: '1.0.0',\n capabilities: ['network-access'],\n }\n\n private service: CloudflareTunnelService | null = null\n private currentConfig: Partial<CloudflareTunnelConfig> = {\n mode: 'quick',\n localPort: 3000,\n }\n\n async initialize(context: AddonContext): Promise<void> {\n this.currentConfig = {\n mode: (context.addonConfig.mode as CloudflareTunnelConfig['mode']) ?? this.currentConfig.mode,\n namedTunnelToken: (context.addonConfig.namedTunnelToken as string | undefined) ?? this.currentConfig.namedTunnelToken,\n localPort: (context.addonConfig.localPort as number) ?? this.currentConfig.localPort,\n }\n // Service creation is deferred until processManager is provided via setProcessManager\n context.logger.info('Cloudflare Tunnel addon initialized')\n }\n\n async shutdown(): Promise<void> {\n await this.service?.stop()\n this.service = null\n }\n\n /** Provide the ProcessManagerService to enable tunnel management */\n setProcessManager(\n processManager: IProcessManager,\n config: CloudflareTunnelConfig,\n context: AddonContext,\n ): void {\n this.service = new CloudflareTunnelService(\n config,\n context.logger,\n context.eventBus,\n processManager,\n )\n }\n\n getService(): CloudflareTunnelService {\n if (!this.service) throw new Error('Cloudflare Tunnel not initialized')\n return this.service\n }\n\n getCapabilityProvider<K extends keyof CapabilityProviderMap>(\n name: K,\n ): CapabilityProviderMap[K] | null {\n if (name === 'network-access' && this.service) {\n return this.service as unknown as CapabilityProviderMap[K]\n }\n return null\n }\n\n getConfigSchema(): ConfigUISchema {\n return {\n sections: [\n {\n id: 'tunnel',\n title: 'Tunnel Settings',\n fields: [\n {\n type: 'select',\n key: 'mode',\n label: 'Tunnel Mode',\n description: 'Quick mode creates a temporary public URL; Named mode uses a persistent named tunnel with your Cloudflare account.',\n options: [\n { value: 'quick', label: 'Quick Tunnel', description: 'Temporary public URL, no Cloudflare account required' },\n { value: 'named', label: 'Named Tunnel', description: 'Persistent tunnel using a Cloudflare tunnel token' },\n ],\n },\n {\n type: 'password',\n key: 'namedTunnelToken',\n label: 'Tunnel Token',\n description: 'Token from your Cloudflare Zero Trust dashboard (required for Named Tunnel mode)',\n showToggle: true,\n showWhen: { field: 'mode', equals: 'named' },\n },\n {\n type: 'number',\n key: 'localPort',\n label: 'Local Port',\n description: 'The local port that the tunnel will expose publicly',\n min: 1,\n max: 65535,\n step: 1,\n },\n ],\n },\n ],\n }\n }\n\n getConfig(): Record<string, unknown> {\n return { ...this.currentConfig }\n }\n\n async onConfigChange(config: Record<string, unknown>): Promise<void> {\n this.currentConfig = {\n mode: (config.mode as CloudflareTunnelConfig['mode']) ?? this.currentConfig.mode,\n namedTunnelToken: (config.namedTunnelToken as string | undefined) ?? this.currentConfig.namedTunnelToken,\n localPort: (config.localPort as number) ?? this.currentConfig.localPort,\n }\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-cloudflare-tunnel",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Cloudflare Tunnel addon for CamStack",
5
5
  "keywords": [
6
6
  "camstack",
@@ -32,7 +32,7 @@
32
32
  "addons": [
33
33
  {
34
34
  "id": "cloudflare-tunnel",
35
- "entry": "./dist/addon.js",
35
+ "entry": "./dist/cloudflare-tunnel.addon.js",
36
36
  "slot": null,
37
37
  "capabilities": [
38
38
  {
@@ -49,7 +49,8 @@
49
49
  "scripts": {
50
50
  "build": "tsup",
51
51
  "dev": "tsup --watch",
52
- "typecheck": "tsc --noEmit"
52
+ "typecheck": "tsc --noEmit",
53
+ "publish": "npm publish --access public"
53
54
  },
54
55
  "peerDependencies": {
55
56
  "@camstack/types": "^0.1.0"
package/dist/index.d.mts DELETED
@@ -1,39 +0,0 @@
1
- import { INetworkAccessProvider, IScopedLogger, IEventBus, IProcessManager, INetworkEndpoint, NetworkAccessStatus, ICamstackAddon, IConfigurable, AddonManifest, AddonContext, CapabilityProviderMap, ConfigUISchema } from '@camstack/types';
2
-
3
- interface CloudflareTunnelConfig {
4
- readonly mode: 'quick' | 'named';
5
- readonly namedTunnelToken?: string;
6
- readonly localPort: number;
7
- }
8
- declare class CloudflareTunnelService implements INetworkAccessProvider {
9
- private readonly config;
10
- private readonly logger;
11
- private readonly eventBus;
12
- private readonly processManager;
13
- readonly id = "cloudflare-tunnel";
14
- readonly type = "cloudflare";
15
- private endpoint;
16
- private processId;
17
- constructor(config: CloudflareTunnelConfig, logger: IScopedLogger, eventBus: IEventBus, processManager: IProcessManager);
18
- start(): Promise<INetworkEndpoint>;
19
- stop(): Promise<void>;
20
- getEndpoint(): INetworkEndpoint | null;
21
- getStatus(): NetworkAccessStatus;
22
- }
23
-
24
- declare class CloudflareTunnelAddon implements ICamstackAddon, IConfigurable {
25
- readonly manifest: AddonManifest;
26
- private service;
27
- private currentConfig;
28
- initialize(context: AddonContext): Promise<void>;
29
- shutdown(): Promise<void>;
30
- /** Provide the ProcessManagerService to enable tunnel management */
31
- setProcessManager(processManager: IProcessManager, config: CloudflareTunnelConfig, context: AddonContext): void;
32
- getService(): CloudflareTunnelService;
33
- getCapabilityProvider<K extends keyof CapabilityProviderMap>(name: K): CapabilityProviderMap[K] | null;
34
- getConfigSchema(): ConfigUISchema;
35
- getConfig(): Record<string, unknown>;
36
- onConfigChange(config: Record<string, unknown>): Promise<void>;
37
- }
38
-
39
- export { CloudflareTunnelAddon, type CloudflareTunnelConfig, CloudflareTunnelService };
package/dist/index.d.ts DELETED
@@ -1,39 +0,0 @@
1
- import { INetworkAccessProvider, IScopedLogger, IEventBus, IProcessManager, INetworkEndpoint, NetworkAccessStatus, ICamstackAddon, IConfigurable, AddonManifest, AddonContext, CapabilityProviderMap, ConfigUISchema } from '@camstack/types';
2
-
3
- interface CloudflareTunnelConfig {
4
- readonly mode: 'quick' | 'named';
5
- readonly namedTunnelToken?: string;
6
- readonly localPort: number;
7
- }
8
- declare class CloudflareTunnelService implements INetworkAccessProvider {
9
- private readonly config;
10
- private readonly logger;
11
- private readonly eventBus;
12
- private readonly processManager;
13
- readonly id = "cloudflare-tunnel";
14
- readonly type = "cloudflare";
15
- private endpoint;
16
- private processId;
17
- constructor(config: CloudflareTunnelConfig, logger: IScopedLogger, eventBus: IEventBus, processManager: IProcessManager);
18
- start(): Promise<INetworkEndpoint>;
19
- stop(): Promise<void>;
20
- getEndpoint(): INetworkEndpoint | null;
21
- getStatus(): NetworkAccessStatus;
22
- }
23
-
24
- declare class CloudflareTunnelAddon implements ICamstackAddon, IConfigurable {
25
- readonly manifest: AddonManifest;
26
- private service;
27
- private currentConfig;
28
- initialize(context: AddonContext): Promise<void>;
29
- shutdown(): Promise<void>;
30
- /** Provide the ProcessManagerService to enable tunnel management */
31
- setProcessManager(processManager: IProcessManager, config: CloudflareTunnelConfig, context: AddonContext): void;
32
- getService(): CloudflareTunnelService;
33
- getCapabilityProvider<K extends keyof CapabilityProviderMap>(name: K): CapabilityProviderMap[K] | null;
34
- getConfigSchema(): ConfigUISchema;
35
- getConfig(): Record<string, unknown>;
36
- onConfigChange(config: Record<string, unknown>): Promise<void>;
37
- }
38
-
39
- export { CloudflareTunnelAddon, type CloudflareTunnelConfig, CloudflareTunnelService };