@camstack/addon-cloudflare-tunnel 0.1.13 → 0.1.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-VHOC5TFB.mjs +55 -0
- package/dist/chunk-VHOC5TFB.mjs.map +1 -0
- package/dist/cloudflare-tunnel.addon.d.mts +13 -33
- package/dist/cloudflare-tunnel.addon.d.ts +13 -33
- package/dist/cloudflare-tunnel.addon.js +44 -164
- package/dist/cloudflare-tunnel.addon.js.map +1 -1
- package/dist/cloudflare-tunnel.addon.mjs +1 -1
- package/dist/index.d.mts +25 -2
- package/dist/index.d.ts +25 -2
- package/dist/index.js +53 -92
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +81 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/dist/chunk-HHH5U2SN.mjs +0 -174
- package/dist/chunk-HHH5U2SN.mjs.map +0 -1
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// src/cloudflare-tunnel.addon.ts
|
|
2
|
+
import { BaseAddon, networkAccessCapability } from "@camstack/types";
|
|
3
|
+
var CloudflareTunnelAddon = class extends BaseAddon {
|
|
4
|
+
constructor() {
|
|
5
|
+
super({ mode: "quick", namedTunnelToken: "", localPort: 3e3 });
|
|
6
|
+
}
|
|
7
|
+
async onInitialize() {
|
|
8
|
+
this.ctx.logger.info("Cloudflare Tunnel addon initialized");
|
|
9
|
+
return [{ capability: networkAccessCapability, provider: this }];
|
|
10
|
+
}
|
|
11
|
+
globalSettingsSchema() {
|
|
12
|
+
return this.schema({
|
|
13
|
+
sections: [{
|
|
14
|
+
id: "tunnel",
|
|
15
|
+
title: "Tunnel Settings",
|
|
16
|
+
fields: [
|
|
17
|
+
this.field({
|
|
18
|
+
type: "select",
|
|
19
|
+
key: "mode",
|
|
20
|
+
label: "Tunnel Mode",
|
|
21
|
+
description: "Quick mode creates a temporary public URL; Named mode uses a persistent named tunnel.",
|
|
22
|
+
default: "quick",
|
|
23
|
+
options: [
|
|
24
|
+
{ value: "quick", label: "Quick Tunnel", description: "Temporary public URL, no Cloudflare account required" },
|
|
25
|
+
{ value: "named", label: "Named Tunnel", description: "Persistent tunnel using a Cloudflare tunnel token" }
|
|
26
|
+
]
|
|
27
|
+
}),
|
|
28
|
+
this.field({
|
|
29
|
+
type: "password",
|
|
30
|
+
key: "namedTunnelToken",
|
|
31
|
+
label: "Tunnel Token",
|
|
32
|
+
description: "Token from your Cloudflare Zero Trust dashboard (required for Named Tunnel mode)",
|
|
33
|
+
showToggle: true,
|
|
34
|
+
showWhen: { field: "mode", equals: "named" }
|
|
35
|
+
}),
|
|
36
|
+
this.field({
|
|
37
|
+
type: "number",
|
|
38
|
+
key: "localPort",
|
|
39
|
+
label: "Local Port",
|
|
40
|
+
description: "The local port that the tunnel will expose publicly",
|
|
41
|
+
min: 1,
|
|
42
|
+
max: 65535,
|
|
43
|
+
step: 1,
|
|
44
|
+
default: 3e3
|
|
45
|
+
})
|
|
46
|
+
]
|
|
47
|
+
}]
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export {
|
|
53
|
+
CloudflareTunnelAddon
|
|
54
|
+
};
|
|
55
|
+
//# sourceMappingURL=chunk-VHOC5TFB.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cloudflare-tunnel.addon.ts"],"sourcesContent":["import type { ProviderRegistration } from '@camstack/types'\nimport { BaseAddon, networkAccessCapability } from '@camstack/types'\n\ninterface TunnelConfig {\n readonly mode: 'quick' | 'named'\n readonly namedTunnelToken: string\n readonly localPort: number\n}\n\n/**\n * Cloudflare Tunnel — exposes CamStack via Cloudflare's network.\n * Settings appear under Cluster → NodeDetail → Settings.\n */\nexport class CloudflareTunnelAddon extends BaseAddon<TunnelConfig> {\n constructor() {\n super({ mode: 'quick', namedTunnelToken: '', localPort: 3000 })\n }\n\n protected async onInitialize(): Promise<ProviderRegistration[]> {\n this.ctx.logger.info('Cloudflare Tunnel addon initialized')\n return [{ capability: networkAccessCapability, provider: this }]\n }\n\n protected globalSettingsSchema() {\n return this.schema({\n sections: [{\n id: 'tunnel',\n title: 'Tunnel Settings',\n fields: [\n this.field({\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.',\n default: 'quick',\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 this.field({\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 this.field({\n type: 'number',\n key: 'localPort',\n label: 'Local Port',\n description: 'The local port that the tunnel will expose publicly',\n min: 1, max: 65535, step: 1, default: 3000,\n }),\n ],\n }],\n })\n }\n}\n"],"mappings":";AACA,SAAS,WAAW,+BAA+B;AAY5C,IAAM,wBAAN,cAAoC,UAAwB;AAAA,EACjE,cAAc;AACZ,UAAM,EAAE,MAAM,SAAS,kBAAkB,IAAI,WAAW,IAAK,CAAC;AAAA,EAChE;AAAA,EAEA,MAAgB,eAAgD;AAC9D,SAAK,IAAI,OAAO,KAAK,qCAAqC;AAC1D,WAAO,CAAC,EAAE,YAAY,yBAAyB,UAAU,KAAK,CAAC;AAAA,EACjE;AAAA,EAEU,uBAAuB;AAC/B,WAAO,KAAK,OAAO;AAAA,MACjB,UAAU,CAAC;AAAA,QACT,IAAI;AAAA,QACJ,OAAO;AAAA,QACP,QAAQ;AAAA,UACN,KAAK,MAAM;AAAA,YACT,MAAM;AAAA,YACN,KAAK;AAAA,YACL,OAAO;AAAA,YACP,aAAa;AAAA,YACb,SAAS;AAAA,YACT,SAAS;AAAA,cACP,EAAE,OAAO,SAAS,OAAO,gBAAgB,aAAa,uDAAuD;AAAA,cAC7G,EAAE,OAAO,SAAS,OAAO,gBAAgB,aAAa,oDAAoD;AAAA,YAC5G;AAAA,UACF,CAAC;AAAA,UACD,KAAK,MAAM;AAAA,YACT,MAAM;AAAA,YACN,KAAK;AAAA,YACL,OAAO;AAAA,YACP,aAAa;AAAA,YACb,YAAY;AAAA,YACZ,UAAU,EAAE,OAAO,QAAQ,QAAQ,QAAQ;AAAA,UAC7C,CAAC;AAAA,UACD,KAAK,MAAM;AAAA,YACT,MAAM;AAAA,YACN,KAAK;AAAA,YACL,OAAO;AAAA,YACP,aAAa;AAAA,YACb,KAAK;AAAA,YAAG,KAAK;AAAA,YAAO,MAAM;AAAA,YAAG,SAAS;AAAA,UACxC,CAAC;AAAA,QACH;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AACF;","names":[]}
|
|
@@ -1,39 +1,19 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as _camstack_types from '@camstack/types';
|
|
2
|
+
import { BaseAddon, ProviderRegistration } from '@camstack/types';
|
|
2
3
|
|
|
3
|
-
interface
|
|
4
|
+
interface TunnelConfig {
|
|
4
5
|
readonly mode: 'quick' | 'named';
|
|
5
|
-
readonly namedTunnelToken
|
|
6
|
+
readonly namedTunnelToken: string;
|
|
6
7
|
readonly localPort: number;
|
|
7
8
|
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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;
|
|
9
|
+
/**
|
|
10
|
+
* Cloudflare Tunnel — exposes CamStack via Cloudflare's network.
|
|
11
|
+
* Settings appear under Cluster → NodeDetail → Settings.
|
|
12
|
+
*/
|
|
13
|
+
declare class CloudflareTunnelAddon extends BaseAddon<TunnelConfig> {
|
|
14
|
+
constructor();
|
|
15
|
+
protected onInitialize(): Promise<ProviderRegistration[]>;
|
|
16
|
+
protected globalSettingsSchema(): _camstack_types.ConfigUISchema;
|
|
22
17
|
}
|
|
23
18
|
|
|
24
|
-
|
|
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 { type CloudflareTunnelConfig as C, CloudflareTunnelAddon, CloudflareTunnelService as a };
|
|
19
|
+
export { CloudflareTunnelAddon };
|
|
@@ -1,39 +1,19 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as _camstack_types from '@camstack/types';
|
|
2
|
+
import { BaseAddon, ProviderRegistration } from '@camstack/types';
|
|
2
3
|
|
|
3
|
-
interface
|
|
4
|
+
interface TunnelConfig {
|
|
4
5
|
readonly mode: 'quick' | 'named';
|
|
5
|
-
readonly namedTunnelToken
|
|
6
|
+
readonly namedTunnelToken: string;
|
|
6
7
|
readonly localPort: number;
|
|
7
8
|
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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;
|
|
9
|
+
/**
|
|
10
|
+
* Cloudflare Tunnel — exposes CamStack via Cloudflare's network.
|
|
11
|
+
* Settings appear under Cluster → NodeDetail → Settings.
|
|
12
|
+
*/
|
|
13
|
+
declare class CloudflareTunnelAddon extends BaseAddon<TunnelConfig> {
|
|
14
|
+
constructor();
|
|
15
|
+
protected onInitialize(): Promise<ProviderRegistration[]>;
|
|
16
|
+
protected globalSettingsSchema(): _camstack_types.ConfigUISchema;
|
|
22
17
|
}
|
|
23
18
|
|
|
24
|
-
|
|
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 { type CloudflareTunnelConfig as C, CloudflareTunnelAddon, CloudflareTunnelService as a };
|
|
19
|
+
export { CloudflareTunnelAddon };
|
|
@@ -23,174 +23,54 @@ __export(cloudflare_tunnel_addon_exports, {
|
|
|
23
23
|
CloudflareTunnelAddon: () => CloudflareTunnelAddon
|
|
24
24
|
});
|
|
25
25
|
module.exports = __toCommonJS(cloudflare_tunnel_addon_exports);
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
constructor(config, logger, eventBus, processManager) {
|
|
31
|
-
this.config = config;
|
|
32
|
-
this.logger = logger;
|
|
33
|
-
this.eventBus = eventBus;
|
|
34
|
-
this.processManager = processManager;
|
|
26
|
+
var import_types = require("@camstack/types");
|
|
27
|
+
var CloudflareTunnelAddon = class extends import_types.BaseAddon {
|
|
28
|
+
constructor() {
|
|
29
|
+
super({ mode: "quick", namedTunnelToken: "", localPort: 3e3 });
|
|
35
30
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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;
|
|
31
|
+
async onInitialize() {
|
|
32
|
+
this.ctx.logger.info("Cloudflare Tunnel addon initialized");
|
|
33
|
+
return [{ capability: import_types.networkAccessCapability, provider: this }];
|
|
76
34
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
35
|
+
globalSettingsSchema() {
|
|
36
|
+
return this.schema({
|
|
37
|
+
sections: [{
|
|
38
|
+
id: "tunnel",
|
|
39
|
+
title: "Tunnel Settings",
|
|
40
|
+
fields: [
|
|
41
|
+
this.field({
|
|
42
|
+
type: "select",
|
|
43
|
+
key: "mode",
|
|
44
|
+
label: "Tunnel Mode",
|
|
45
|
+
description: "Quick mode creates a temporary public URL; Named mode uses a persistent named tunnel.",
|
|
46
|
+
default: "quick",
|
|
47
|
+
options: [
|
|
48
|
+
{ value: "quick", label: "Quick Tunnel", description: "Temporary public URL, no Cloudflare account required" },
|
|
49
|
+
{ value: "named", label: "Named Tunnel", description: "Persistent tunnel using a Cloudflare tunnel token" }
|
|
50
|
+
]
|
|
51
|
+
}),
|
|
52
|
+
this.field({
|
|
53
|
+
type: "password",
|
|
54
|
+
key: "namedTunnelToken",
|
|
55
|
+
label: "Tunnel Token",
|
|
56
|
+
description: "Token from your Cloudflare Zero Trust dashboard (required for Named Tunnel mode)",
|
|
57
|
+
showToggle: true,
|
|
58
|
+
showWhen: { field: "mode", equals: "named" }
|
|
59
|
+
}),
|
|
60
|
+
this.field({
|
|
61
|
+
type: "number",
|
|
62
|
+
key: "localPort",
|
|
63
|
+
label: "Local Port",
|
|
64
|
+
description: "The local port that the tunnel will expose publicly",
|
|
65
|
+
min: 1,
|
|
66
|
+
max: 65535,
|
|
67
|
+
step: 1,
|
|
68
|
+
default: 3e3
|
|
69
|
+
})
|
|
70
|
+
]
|
|
71
|
+
}]
|
|
88
72
|
});
|
|
89
73
|
}
|
|
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
74
|
};
|
|
195
75
|
// Annotate the CommonJS export names for ESM import in node:
|
|
196
76
|
0 && (module.exports = {
|
|
@@ -1 +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":[]}
|
|
1
|
+
{"version":3,"sources":["../src/cloudflare-tunnel.addon.ts"],"sourcesContent":["import type { ProviderRegistration } from '@camstack/types'\nimport { BaseAddon, networkAccessCapability } from '@camstack/types'\n\ninterface TunnelConfig {\n readonly mode: 'quick' | 'named'\n readonly namedTunnelToken: string\n readonly localPort: number\n}\n\n/**\n * Cloudflare Tunnel — exposes CamStack via Cloudflare's network.\n * Settings appear under Cluster → NodeDetail → Settings.\n */\nexport class CloudflareTunnelAddon extends BaseAddon<TunnelConfig> {\n constructor() {\n super({ mode: 'quick', namedTunnelToken: '', localPort: 3000 })\n }\n\n protected async onInitialize(): Promise<ProviderRegistration[]> {\n this.ctx.logger.info('Cloudflare Tunnel addon initialized')\n return [{ capability: networkAccessCapability, provider: this }]\n }\n\n protected globalSettingsSchema() {\n return this.schema({\n sections: [{\n id: 'tunnel',\n title: 'Tunnel Settings',\n fields: [\n this.field({\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.',\n default: 'quick',\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 this.field({\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 this.field({\n type: 'number',\n key: 'localPort',\n label: 'Local Port',\n description: 'The local port that the tunnel will expose publicly',\n min: 1, max: 65535, step: 1, default: 3000,\n }),\n ],\n }],\n })\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,mBAAmD;AAY5C,IAAM,wBAAN,cAAoC,uBAAwB;AAAA,EACjE,cAAc;AACZ,UAAM,EAAE,MAAM,SAAS,kBAAkB,IAAI,WAAW,IAAK,CAAC;AAAA,EAChE;AAAA,EAEA,MAAgB,eAAgD;AAC9D,SAAK,IAAI,OAAO,KAAK,qCAAqC;AAC1D,WAAO,CAAC,EAAE,YAAY,sCAAyB,UAAU,KAAK,CAAC;AAAA,EACjE;AAAA,EAEU,uBAAuB;AAC/B,WAAO,KAAK,OAAO;AAAA,MACjB,UAAU,CAAC;AAAA,QACT,IAAI;AAAA,QACJ,OAAO;AAAA,QACP,QAAQ;AAAA,UACN,KAAK,MAAM;AAAA,YACT,MAAM;AAAA,YACN,KAAK;AAAA,YACL,OAAO;AAAA,YACP,aAAa;AAAA,YACb,SAAS;AAAA,YACT,SAAS;AAAA,cACP,EAAE,OAAO,SAAS,OAAO,gBAAgB,aAAa,uDAAuD;AAAA,cAC7G,EAAE,OAAO,SAAS,OAAO,gBAAgB,aAAa,oDAAoD;AAAA,YAC5G;AAAA,UACF,CAAC;AAAA,UACD,KAAK,MAAM;AAAA,YACT,MAAM;AAAA,YACN,KAAK;AAAA,YACL,OAAO;AAAA,YACP,aAAa;AAAA,YACb,YAAY;AAAA,YACZ,UAAU,EAAE,OAAO,QAAQ,QAAQ,QAAQ;AAAA,UAC7C,CAAC;AAAA,UACD,KAAK,MAAM;AAAA,YACT,MAAM;AAAA,YACN,KAAK;AAAA,YACL,OAAO;AAAA,YACP,aAAa;AAAA,YACb,KAAK;AAAA,YAAG,KAAK;AAAA,YAAO,MAAM;AAAA,YAAG,SAAS;AAAA,UACxC,CAAC;AAAA,QACH;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AACF;","names":[]}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,2 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { INetworkAccessProvider, IScopedLogger, IEventBus, IProcessManager, INetworkEndpoint, NetworkAccessStatus } from '@camstack/types';
|
|
2
|
+
export { CloudflareTunnelAddon } from './cloudflare-tunnel.addon.mjs';
|
|
3
|
+
|
|
4
|
+
interface CloudflareTunnelConfig {
|
|
5
|
+
readonly mode: 'quick' | 'named';
|
|
6
|
+
readonly namedTunnelToken?: string;
|
|
7
|
+
readonly localPort: number;
|
|
8
|
+
}
|
|
9
|
+
declare class CloudflareTunnelService implements INetworkAccessProvider {
|
|
10
|
+
private readonly config;
|
|
11
|
+
private readonly logger;
|
|
12
|
+
private readonly eventBus;
|
|
13
|
+
private readonly processManager;
|
|
14
|
+
readonly id = "cloudflare-tunnel";
|
|
15
|
+
readonly type = "cloudflare";
|
|
16
|
+
private endpoint;
|
|
17
|
+
private processId;
|
|
18
|
+
constructor(config: CloudflareTunnelConfig, logger: IScopedLogger, eventBus: IEventBus, processManager: IProcessManager);
|
|
19
|
+
start(): Promise<INetworkEndpoint>;
|
|
20
|
+
stop(): Promise<void>;
|
|
21
|
+
getEndpoint(): INetworkEndpoint | null;
|
|
22
|
+
getStatus(): NetworkAccessStatus;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export { type CloudflareTunnelConfig, CloudflareTunnelService };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { INetworkAccessProvider, IScopedLogger, IEventBus, IProcessManager, INetworkEndpoint, NetworkAccessStatus } from '@camstack/types';
|
|
2
|
+
export { CloudflareTunnelAddon } from './cloudflare-tunnel.addon.js';
|
|
3
|
+
|
|
4
|
+
interface CloudflareTunnelConfig {
|
|
5
|
+
readonly mode: 'quick' | 'named';
|
|
6
|
+
readonly namedTunnelToken?: string;
|
|
7
|
+
readonly localPort: number;
|
|
8
|
+
}
|
|
9
|
+
declare class CloudflareTunnelService implements INetworkAccessProvider {
|
|
10
|
+
private readonly config;
|
|
11
|
+
private readonly logger;
|
|
12
|
+
private readonly eventBus;
|
|
13
|
+
private readonly processManager;
|
|
14
|
+
readonly id = "cloudflare-tunnel";
|
|
15
|
+
readonly type = "cloudflare";
|
|
16
|
+
private endpoint;
|
|
17
|
+
private processId;
|
|
18
|
+
constructor(config: CloudflareTunnelConfig, logger: IScopedLogger, eventBus: IEventBus, processManager: IProcessManager);
|
|
19
|
+
start(): Promise<INetworkEndpoint>;
|
|
20
|
+
stop(): Promise<void>;
|
|
21
|
+
getEndpoint(): INetworkEndpoint | null;
|
|
22
|
+
getStatus(): NetworkAccessStatus;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export { type CloudflareTunnelConfig, CloudflareTunnelService };
|
package/dist/index.js
CHANGED
|
@@ -27,6 +27,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
27
27
|
|
|
28
28
|
// src/cloudflare-tunnel.ts
|
|
29
29
|
var import_node_crypto = require("crypto");
|
|
30
|
+
var import_types = require("@camstack/types");
|
|
30
31
|
var CloudflareTunnelService = class {
|
|
31
32
|
constructor(config, logger, eventBus, processManager) {
|
|
32
33
|
this.config = config;
|
|
@@ -34,12 +35,16 @@ var CloudflareTunnelService = class {
|
|
|
34
35
|
this.eventBus = eventBus;
|
|
35
36
|
this.processManager = processManager;
|
|
36
37
|
}
|
|
38
|
+
config;
|
|
39
|
+
logger;
|
|
40
|
+
eventBus;
|
|
41
|
+
processManager;
|
|
37
42
|
id = "cloudflare-tunnel";
|
|
38
43
|
type = "cloudflare";
|
|
39
44
|
endpoint = null;
|
|
40
45
|
processId = null;
|
|
41
46
|
async start() {
|
|
42
|
-
this.logger.info(
|
|
47
|
+
this.logger.info("Starting Cloudflare tunnel", { meta: { mode: this.config.mode } });
|
|
43
48
|
const processConfig = {
|
|
44
49
|
id: "cloudflared-tunnel",
|
|
45
50
|
label: "Cloudflare Tunnel",
|
|
@@ -70,7 +75,7 @@ var CloudflareTunnelService = class {
|
|
|
70
75
|
id: (0, import_node_crypto.randomUUID)(),
|
|
71
76
|
timestamp: /* @__PURE__ */ new Date(),
|
|
72
77
|
source: { type: "addon", id: "cloudflare-tunnel" },
|
|
73
|
-
category:
|
|
78
|
+
category: import_types.EventCategory.NetworkTunnelStarted,
|
|
74
79
|
data: { url: publicUrl }
|
|
75
80
|
});
|
|
76
81
|
return this.endpoint;
|
|
@@ -84,7 +89,7 @@ var CloudflareTunnelService = class {
|
|
|
84
89
|
id: (0, import_node_crypto.randomUUID)(),
|
|
85
90
|
timestamp: /* @__PURE__ */ new Date(),
|
|
86
91
|
source: { type: "addon", id: "cloudflare-tunnel" },
|
|
87
|
-
category:
|
|
92
|
+
category: import_types.EventCategory.NetworkTunnelStopped,
|
|
88
93
|
data: {}
|
|
89
94
|
});
|
|
90
95
|
}
|
|
@@ -100,97 +105,53 @@ var CloudflareTunnelService = class {
|
|
|
100
105
|
};
|
|
101
106
|
|
|
102
107
|
// src/cloudflare-tunnel.addon.ts
|
|
103
|
-
var
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
version: "1.0.0",
|
|
108
|
-
capabilities: ["network-access"]
|
|
109
|
-
};
|
|
110
|
-
service = null;
|
|
111
|
-
currentConfig = {
|
|
112
|
-
mode: "quick",
|
|
113
|
-
localPort: 3e3
|
|
114
|
-
};
|
|
115
|
-
async initialize(context) {
|
|
116
|
-
this.currentConfig = {
|
|
117
|
-
mode: context.addonConfig.mode ?? this.currentConfig.mode,
|
|
118
|
-
namedTunnelToken: context.addonConfig.namedTunnelToken ?? this.currentConfig.namedTunnelToken,
|
|
119
|
-
localPort: context.addonConfig.localPort ?? this.currentConfig.localPort
|
|
120
|
-
};
|
|
121
|
-
context.logger.info("Cloudflare Tunnel addon initialized");
|
|
122
|
-
}
|
|
123
|
-
async shutdown() {
|
|
124
|
-
await this.service?.stop();
|
|
125
|
-
this.service = null;
|
|
126
|
-
}
|
|
127
|
-
/** Provide the ProcessManagerService to enable tunnel management */
|
|
128
|
-
setProcessManager(processManager, config, context) {
|
|
129
|
-
this.service = new CloudflareTunnelService(
|
|
130
|
-
config,
|
|
131
|
-
context.logger,
|
|
132
|
-
context.eventBus,
|
|
133
|
-
processManager
|
|
134
|
-
);
|
|
108
|
+
var import_types2 = require("@camstack/types");
|
|
109
|
+
var CloudflareTunnelAddon = class extends import_types2.BaseAddon {
|
|
110
|
+
constructor() {
|
|
111
|
+
super({ mode: "quick", namedTunnelToken: "", localPort: 3e3 });
|
|
135
112
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
return this
|
|
113
|
+
async onInitialize() {
|
|
114
|
+
this.ctx.logger.info("Cloudflare Tunnel addon initialized");
|
|
115
|
+
return [{ capability: import_types2.networkAccessCapability, provider: this }];
|
|
139
116
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
{
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
step: 1
|
|
179
|
-
}
|
|
180
|
-
]
|
|
181
|
-
}
|
|
182
|
-
]
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
getConfig() {
|
|
186
|
-
return { ...this.currentConfig };
|
|
187
|
-
}
|
|
188
|
-
async onConfigChange(config) {
|
|
189
|
-
this.currentConfig = {
|
|
190
|
-
mode: config.mode ?? this.currentConfig.mode,
|
|
191
|
-
namedTunnelToken: config.namedTunnelToken ?? this.currentConfig.namedTunnelToken,
|
|
192
|
-
localPort: config.localPort ?? this.currentConfig.localPort
|
|
193
|
-
};
|
|
117
|
+
globalSettingsSchema() {
|
|
118
|
+
return this.schema({
|
|
119
|
+
sections: [{
|
|
120
|
+
id: "tunnel",
|
|
121
|
+
title: "Tunnel Settings",
|
|
122
|
+
fields: [
|
|
123
|
+
this.field({
|
|
124
|
+
type: "select",
|
|
125
|
+
key: "mode",
|
|
126
|
+
label: "Tunnel Mode",
|
|
127
|
+
description: "Quick mode creates a temporary public URL; Named mode uses a persistent named tunnel.",
|
|
128
|
+
default: "quick",
|
|
129
|
+
options: [
|
|
130
|
+
{ value: "quick", label: "Quick Tunnel", description: "Temporary public URL, no Cloudflare account required" },
|
|
131
|
+
{ value: "named", label: "Named Tunnel", description: "Persistent tunnel using a Cloudflare tunnel token" }
|
|
132
|
+
]
|
|
133
|
+
}),
|
|
134
|
+
this.field({
|
|
135
|
+
type: "password",
|
|
136
|
+
key: "namedTunnelToken",
|
|
137
|
+
label: "Tunnel Token",
|
|
138
|
+
description: "Token from your Cloudflare Zero Trust dashboard (required for Named Tunnel mode)",
|
|
139
|
+
showToggle: true,
|
|
140
|
+
showWhen: { field: "mode", equals: "named" }
|
|
141
|
+
}),
|
|
142
|
+
this.field({
|
|
143
|
+
type: "number",
|
|
144
|
+
key: "localPort",
|
|
145
|
+
label: "Local Port",
|
|
146
|
+
description: "The local port that the tunnel will expose publicly",
|
|
147
|
+
min: 1,
|
|
148
|
+
max: 65535,
|
|
149
|
+
step: 1,
|
|
150
|
+
default: 3e3
|
|
151
|
+
})
|
|
152
|
+
]
|
|
153
|
+
}]
|
|
154
|
+
});
|
|
194
155
|
}
|
|
195
156
|
};
|
|
196
157
|
// Annotate the CommonJS export names for ESM import in node:
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/cloudflare-tunnel.ts","../src/cloudflare-tunnel.addon.ts"],"sourcesContent":["export { CloudflareTunnelService } from './cloudflare-tunnel'\nexport type { CloudflareTunnelConfig } from './cloudflare-tunnel'\nexport { CloudflareTunnelAddon } from './cloudflare-tunnel.addon'\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","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":["../src/index.ts","../src/cloudflare-tunnel.ts","../src/cloudflare-tunnel.addon.ts"],"sourcesContent":["export { CloudflareTunnelService } from './cloudflare-tunnel'\nexport type { CloudflareTunnelConfig } from './cloudflare-tunnel'\nexport { CloudflareTunnelAddon } from './cloudflare-tunnel.addon'\n","import { randomUUID } from 'node:crypto'\nimport { EventCategory } from '@camstack/types'\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', { meta: { mode: 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: EventCategory.NetworkTunnelStarted,\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: EventCategory.NetworkTunnelStopped,\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 { ProviderRegistration } from '@camstack/types'\nimport { BaseAddon, networkAccessCapability } from '@camstack/types'\n\ninterface TunnelConfig {\n readonly mode: 'quick' | 'named'\n readonly namedTunnelToken: string\n readonly localPort: number\n}\n\n/**\n * Cloudflare Tunnel — exposes CamStack via Cloudflare's network.\n * Settings appear under Cluster → NodeDetail → Settings.\n */\nexport class CloudflareTunnelAddon extends BaseAddon<TunnelConfig> {\n constructor() {\n super({ mode: 'quick', namedTunnelToken: '', localPort: 3000 })\n }\n\n protected async onInitialize(): Promise<ProviderRegistration[]> {\n this.ctx.logger.info('Cloudflare Tunnel addon initialized')\n return [{ capability: networkAccessCapability, provider: this }]\n }\n\n protected globalSettingsSchema() {\n return this.schema({\n sections: [{\n id: 'tunnel',\n title: 'Tunnel Settings',\n fields: [\n this.field({\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.',\n default: 'quick',\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 this.field({\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 this.field({\n type: 'number',\n key: 'localPort',\n label: 'Local Port',\n description: 'The local port that the tunnel will expose publicly',\n min: 1, max: 65535, step: 1, default: 3000,\n }),\n ],\n }],\n })\n }\n}\n"],"mappings":"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","names":["import_types"]}
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,85 @@
|
|
|
1
1
|
import {
|
|
2
|
-
CloudflareTunnelAddon
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
CloudflareTunnelAddon
|
|
3
|
+
} from "./chunk-VHOC5TFB.mjs";
|
|
4
|
+
|
|
5
|
+
// src/cloudflare-tunnel.ts
|
|
6
|
+
import { randomUUID } from "crypto";
|
|
7
|
+
import { EventCategory } from "@camstack/types";
|
|
8
|
+
var CloudflareTunnelService = class {
|
|
9
|
+
constructor(config, logger, eventBus, processManager) {
|
|
10
|
+
this.config = config;
|
|
11
|
+
this.logger = logger;
|
|
12
|
+
this.eventBus = eventBus;
|
|
13
|
+
this.processManager = processManager;
|
|
14
|
+
}
|
|
15
|
+
config;
|
|
16
|
+
logger;
|
|
17
|
+
eventBus;
|
|
18
|
+
processManager;
|
|
19
|
+
id = "cloudflare-tunnel";
|
|
20
|
+
type = "cloudflare";
|
|
21
|
+
endpoint = null;
|
|
22
|
+
processId = null;
|
|
23
|
+
async start() {
|
|
24
|
+
this.logger.info("Starting Cloudflare tunnel", { meta: { mode: this.config.mode } });
|
|
25
|
+
const processConfig = {
|
|
26
|
+
id: "cloudflared-tunnel",
|
|
27
|
+
label: "Cloudflare Tunnel",
|
|
28
|
+
command: "cloudflared",
|
|
29
|
+
args: this.config.mode === "quick" ? ["tunnel", "--url", `http://localhost:${this.config.localPort}`] : ["tunnel", "run", "--token", this.config.namedTunnelToken],
|
|
30
|
+
autoRestart: true,
|
|
31
|
+
maxRestarts: 5
|
|
32
|
+
};
|
|
33
|
+
this.processManager.register(processConfig);
|
|
34
|
+
await this.processManager.start("cloudflared-tunnel");
|
|
35
|
+
this.processId = "cloudflared-tunnel";
|
|
36
|
+
const publicUrl = this.config.mode === "named" ? "https://tunnel.example.com" : "https://pending.trycloudflare.com";
|
|
37
|
+
this.endpoint = {
|
|
38
|
+
id: "cloudflare-tunnel",
|
|
39
|
+
type: "tunnel",
|
|
40
|
+
provider: "cloudflare",
|
|
41
|
+
url: publicUrl,
|
|
42
|
+
internal: false,
|
|
43
|
+
capabilities: {
|
|
44
|
+
supportsWebRTC: false,
|
|
45
|
+
supportsWebSocket: true,
|
|
46
|
+
supportsSSE: true
|
|
47
|
+
},
|
|
48
|
+
priority: 50,
|
|
49
|
+
status: "online"
|
|
50
|
+
};
|
|
51
|
+
this.eventBus.emit({
|
|
52
|
+
id: randomUUID(),
|
|
53
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
54
|
+
source: { type: "addon", id: "cloudflare-tunnel" },
|
|
55
|
+
category: EventCategory.NetworkTunnelStarted,
|
|
56
|
+
data: { url: publicUrl }
|
|
57
|
+
});
|
|
58
|
+
return this.endpoint;
|
|
59
|
+
}
|
|
60
|
+
async stop() {
|
|
61
|
+
if (this.processId) {
|
|
62
|
+
await this.processManager.stop(this.processId);
|
|
63
|
+
}
|
|
64
|
+
this.endpoint = null;
|
|
65
|
+
this.eventBus.emit({
|
|
66
|
+
id: randomUUID(),
|
|
67
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
68
|
+
source: { type: "addon", id: "cloudflare-tunnel" },
|
|
69
|
+
category: EventCategory.NetworkTunnelStopped,
|
|
70
|
+
data: {}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
getEndpoint() {
|
|
74
|
+
return this.endpoint;
|
|
75
|
+
}
|
|
76
|
+
getStatus() {
|
|
77
|
+
return {
|
|
78
|
+
connected: this.endpoint !== null,
|
|
79
|
+
publicUrl: this.endpoint?.url
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
};
|
|
5
83
|
export {
|
|
6
84
|
CloudflareTunnelAddon,
|
|
7
85
|
CloudflareTunnelService
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/cloudflare-tunnel.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto'\nimport { EventCategory } from '@camstack/types'\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', { meta: { mode: 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: EventCategory.NetworkTunnelStarted,\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: EventCategory.NetworkTunnelStopped,\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":";;;;;AAAA,SAAS,kBAAkB;AAC3B,SAAS,qBAAqB;AAavB,IAAM,0BAAN,MAAgE;AAAA,EAOrE,YACmB,QACA,QACA,UACA,gBACjB;AAJiB;AACA;AACA;AACA;AAAA,EAChB;AAAA,EAJgB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAVV,KAAK;AAAA,EACL,OAAO;AAAA,EAER,WAAoC;AAAA,EACpC,YAA2B;AAAA,EASnC,MAAM,QAAmC;AACvC,SAAK,OAAO,KAAK,8BAA8B,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,KAAK,EAAE,CAAC;AAEnF,UAAM,gBAA+B;AAAA,MACnC,IAAI;AAAA,MACJ,OAAO;AAAA,MACP,SAAS;AAAA,MACT,MACE,KAAK,OAAO,SAAS,UACjB,CAAC,UAAU,SAAS,oBAAoB,KAAK,OAAO,SAAS,EAAE,IAC/D,CAAC,UAAU,OAAO,WAAW,KAAK,OAAO,gBAAiB;AAAA,MAChE,aAAa;AAAA,MACb,aAAa;AAAA,IACf;AAEA,SAAK,eAAe,SAAS,aAAa;AAC1C,UAAM,KAAK,eAAe,MAAM,oBAAoB;AACpD,SAAK,YAAY;AAEjB,UAAM,YACJ,KAAK,OAAO,SAAS,UACjB,+BACA;AAEN,SAAK,WAAW;AAAA,MACd,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,UAAU;AAAA,MACV,KAAK;AAAA,MACL,UAAU;AAAA,MACV,cAAc;AAAA,QACZ,gBAAgB;AAAA,QAChB,mBAAmB;AAAA,QACnB,aAAa;AAAA,MACf;AAAA,MACA,UAAU;AAAA,MACV,QAAQ;AAAA,IACV;AAEA,SAAK,SAAS,KAAK;AAAA,MACjB,IAAI,WAAW;AAAA,MACf,WAAW,oBAAI,KAAK;AAAA,MACpB,QAAQ,EAAE,MAAM,SAAS,IAAI,oBAAoB;AAAA,MACjD,UAAU,cAAc;AAAA,MACxB,MAAM,EAAE,KAAK,UAAU;AAAA,IACzB,CAAC;AAED,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAM,OAAsB;AAC1B,QAAI,KAAK,WAAW;AAClB,YAAM,KAAK,eAAe,KAAK,KAAK,SAAS;AAAA,IAC/C;AACA,SAAK,WAAW;AAEhB,SAAK,SAAS,KAAK;AAAA,MACjB,IAAI,WAAW;AAAA,MACf,WAAW,oBAAI,KAAK;AAAA,MACpB,QAAQ,EAAE,MAAM,SAAS,IAAI,oBAAoB;AAAA,MACjD,UAAU,cAAc;AAAA,MACxB,MAAM,CAAC;AAAA,IACT,CAAC;AAAA,EACH;AAAA,EAEA,cAAuC;AACrC,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,YAAiC;AAC/B,WAAO;AAAA,MACL,WAAW,KAAK,aAAa;AAAA,MAC7B,WAAW,KAAK,UAAU;AAAA,IAC5B;AAAA,EACF;AACF;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-cloudflare-tunnel",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.14",
|
|
4
4
|
"description": "Cloudflare Tunnel addon for CamStack",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"camstack",
|
|
@@ -32,12 +32,12 @@
|
|
|
32
32
|
"addons": [
|
|
33
33
|
{
|
|
34
34
|
"id": "cloudflare-tunnel",
|
|
35
|
+
"name": "Cloudflare Tunnel",
|
|
36
|
+
"version": "1.0.0",
|
|
35
37
|
"entry": "./dist/cloudflare-tunnel.addon.js",
|
|
36
|
-
"slot": null,
|
|
37
38
|
"capabilities": [
|
|
38
39
|
{
|
|
39
|
-
"name": "network-access"
|
|
40
|
-
"mode": "collection"
|
|
40
|
+
"name": "network-access"
|
|
41
41
|
}
|
|
42
42
|
]
|
|
43
43
|
}
|
package/dist/chunk-HHH5U2SN.mjs
DELETED
|
@@ -1,174 +0,0 @@
|
|
|
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
|
|
@@ -1 +0,0 @@
|
|
|
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":[]}
|