@openacp/cli 2026.330.2 → 2026.331.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -0
- package/dist/adapter-ELG3VRZ3.js +14 -0
- package/dist/{agent-catalog-SZQQERV7.js → agent-catalog-UYD26QDK.js} +3 -3
- package/dist/{api-client-XTLRRFPX.js → api-client-PEMHYL5U.js} +2 -2
- package/dist/{api-server-JLBDKCU4.js → api-server-DATG2KBR.js} +3 -3
- package/dist/api-server-L5Z7XACW.js +7 -0
- package/dist/chunk-23SRIVG4.js +50 -0
- package/dist/chunk-23SRIVG4.js.map +1 -0
- package/dist/{chunk-YIGBJFJL.js → chunk-7GXEMMEV.js} +15 -15
- package/dist/{chunk-QWVHCTCA.js → chunk-7U6IZIJP.js} +37 -23
- package/dist/chunk-7U6IZIJP.js.map +1 -0
- package/dist/{chunk-FCTC7KDT.js → chunk-7YIKTRSM.js} +14 -10
- package/dist/chunk-7YIKTRSM.js.map +1 -0
- package/dist/{chunk-MITTQMGZ.js → chunk-BYCJQPMN.js} +5 -5
- package/dist/chunk-BYCJQPMN.js.map +1 -0
- package/dist/{chunk-5ZNBNIK3.js → chunk-EWVXSTQK.js} +193 -53
- package/dist/chunk-EWVXSTQK.js.map +1 -0
- package/dist/{chunk-UWH7KIAA.js → chunk-FPKQYCQS.js} +88 -13
- package/dist/chunk-FPKQYCQS.js.map +1 -0
- package/dist/{chunk-GEOXPGCO.js → chunk-K6UY5M75.js} +12 -9
- package/dist/chunk-K6UY5M75.js.map +1 -0
- package/dist/{chunk-KDU3ZEWT.js → chunk-KGAQW6F4.js} +12 -3
- package/dist/chunk-KGAQW6F4.js.map +1 -0
- package/dist/{chunk-7RKPIM3E.js → chunk-LRV56K2M.js} +205 -16
- package/dist/chunk-LRV56K2M.js.map +1 -0
- package/dist/{chunk-V2YZWYXT.js → chunk-MDJHCCFS.js} +18 -17
- package/dist/chunk-MDJHCCFS.js.map +1 -0
- package/dist/chunk-NHD5XDD2.js +686 -0
- package/dist/chunk-NHD5XDD2.js.map +1 -0
- package/dist/{chunk-APS6UEFU.js → chunk-NJX75BLK.js} +1 -1
- package/dist/chunk-NJX75BLK.js.map +1 -0
- package/dist/{chunk-5HKQCYOI.js → chunk-NOEAJNTK.js} +14 -3
- package/dist/chunk-NOEAJNTK.js.map +1 -0
- package/dist/chunk-ON7HB5O7.js +58 -0
- package/dist/chunk-ON7HB5O7.js.map +1 -0
- package/dist/{chunk-5OCGO27U.js → chunk-OSBZXY2W.js} +2 -1
- package/dist/chunk-OSBZXY2W.js.map +1 -0
- package/dist/{chunk-PA6MNBG4.js → chunk-P3HHJANC.js} +32 -13
- package/dist/chunk-P3HHJANC.js.map +1 -0
- package/dist/{chunk-BTJHGSLM.js → chunk-R2YLDQLI.js} +9 -10
- package/dist/chunk-R2YLDQLI.js.map +1 -0
- package/dist/{chunk-CFUJGWOP.js → chunk-SSLVNCEA.js} +27 -3
- package/dist/chunk-SSLVNCEA.js.map +1 -0
- package/dist/{chunk-MPGEHTGE.js → chunk-TGP34LQN.js} +9 -7
- package/dist/chunk-TGP34LQN.js.map +1 -0
- package/dist/{chunk-TMVTSWVH.js → chunk-VUSCVRJL.js} +2 -1
- package/dist/chunk-VUSCVRJL.js.map +1 -0
- package/dist/chunk-XRJUS6FE.js +53 -0
- package/dist/chunk-XRJUS6FE.js.map +1 -0
- package/dist/{chunk-W4LK6WJP.js → chunk-YZCKSNRN.js} +24 -17
- package/dist/chunk-YZCKSNRN.js.map +1 -0
- package/dist/{chunk-3NAFXVQM.js → chunk-ZIRH6QWW.js} +7 -5
- package/dist/chunk-ZIRH6QWW.js.map +1 -0
- package/dist/cli.d.ts +11 -0
- package/dist/cli.js +334 -140
- package/dist/cli.js.map +1 -1
- package/dist/config-X4UP7H6R.js +13 -0
- package/dist/config-editor-7BENRVG5.js +11 -0
- package/dist/{config-registry-ZXAIJNYB.js → config-registry-M3FFWEVM.js} +3 -2
- package/dist/context-FVGCU5TI.js +9 -0
- package/dist/core-plugins-JSY2I44L.js +25 -0
- package/dist/{daemon-XFEMMJSZ.js → daemon-UOSRDEXW.js} +8 -3
- package/dist/doctor-6DLACBR4.js +10 -0
- package/dist/{file-service-HHB3JQIO.js → file-service-FQQYME7M.js} +2 -2
- package/dist/index.d.ts +265 -32
- package/dist/index.js +44 -33
- package/dist/index.js.map +1 -1
- package/dist/{install-cloudflared-JRJ4BSOM.js → install-cloudflared-LNS5L5FR.js} +5 -4
- package/dist/install-cloudflared-LNS5L5FR.js.map +1 -0
- package/dist/{install-context-EHYV5WRY.js → install-context-KZO5FR4D.js} +4 -3
- package/dist/install-context-KZO5FR4D.js.map +1 -0
- package/dist/{install-jq-ISTGT263.js → install-jq-SN4IA5K4.js} +3 -3
- package/dist/instance-context-FLCE7VZ4.js +13 -0
- package/dist/instance-registry-SW5FWKHO.js +7 -0
- package/dist/{main-L2M4NTJY.js → main-D7M2AKRM.js} +91 -48
- package/dist/main-D7M2AKRM.js.map +1 -0
- package/dist/{plugin-create-EHL76ZZG.js → plugin-create-HFKS23JY.js} +4 -2
- package/dist/{plugin-create-EHL76ZZG.js.map → plugin-create-HFKS23JY.js.map} +1 -1
- package/dist/{post-upgrade-Y26S2ZQ7.js → post-upgrade-F4YPMTUT.js} +6 -6
- package/dist/{security-2BA265LN.js → security-O4XGN2CM.js} +2 -2
- package/dist/{setup-E6BNEYCS.js → setup-44WLBIOT.js} +209 -22
- package/dist/setup-44WLBIOT.js.map +1 -0
- package/dist/{speech-SG62JYIF.js → speech-GHTSWDAN.js} +2 -2
- package/dist/telegram-D7ASLVEB.js +7 -0
- package/dist/telegram-D7ASLVEB.js.map +1 -0
- package/dist/tunnel-ALJDPFDQ.js +10 -0
- package/dist/tunnel-ALJDPFDQ.js.map +1 -0
- package/dist/{tunnel-service-ZMO4THKE.js → tunnel-service-TBAHDXMF.js} +41 -547
- package/dist/tunnel-service-TBAHDXMF.js.map +1 -0
- package/package.json +1 -1
- package/dist/adapter-4U6MC5ZS.js +0 -13
- package/dist/api-server-5VNYFWJE.js +0 -7
- package/dist/chunk-3NAFXVQM.js.map +0 -1
- package/dist/chunk-4WXALZA3.js +0 -45
- package/dist/chunk-4WXALZA3.js.map +0 -1
- package/dist/chunk-5HKQCYOI.js.map +0 -1
- package/dist/chunk-5OCGO27U.js.map +0 -1
- package/dist/chunk-5ZNBNIK3.js.map +0 -1
- package/dist/chunk-7RKPIM3E.js.map +0 -1
- package/dist/chunk-APS6UEFU.js.map +0 -1
- package/dist/chunk-BTJHGSLM.js.map +0 -1
- package/dist/chunk-CFUJGWOP.js.map +0 -1
- package/dist/chunk-FCTC7KDT.js.map +0 -1
- package/dist/chunk-GEOXPGCO.js.map +0 -1
- package/dist/chunk-KDU3ZEWT.js.map +0 -1
- package/dist/chunk-MITTQMGZ.js.map +0 -1
- package/dist/chunk-MPGEHTGE.js.map +0 -1
- package/dist/chunk-PA6MNBG4.js.map +0 -1
- package/dist/chunk-QWVHCTCA.js.map +0 -1
- package/dist/chunk-TMVTSWVH.js.map +0 -1
- package/dist/chunk-UWH7KIAA.js.map +0 -1
- package/dist/chunk-V2YZWYXT.js.map +0 -1
- package/dist/chunk-W4LK6WJP.js.map +0 -1
- package/dist/config-KN6NKKPF.js +0 -20
- package/dist/config-editor-76RVZS4B.js +0 -10
- package/dist/context-NXXW62NJ.js +0 -9
- package/dist/core-plugins-OCHKGCIZ.js +0 -22
- package/dist/doctor-AV6AUO22.js +0 -9
- package/dist/install-cloudflared-JRJ4BSOM.js.map +0 -1
- package/dist/install-context-EHYV5WRY.js.map +0 -1
- package/dist/main-L2M4NTJY.js.map +0 -1
- package/dist/setup-E6BNEYCS.js.map +0 -1
- package/dist/telegram-EAVRDNFU.js +0 -7
- package/dist/tunnel-HWJ27WDH.js +0 -7
- package/dist/tunnel-service-ZMO4THKE.js.map +0 -1
- /package/dist/{adapter-4U6MC5ZS.js.map → adapter-ELG3VRZ3.js.map} +0 -0
- /package/dist/{agent-catalog-SZQQERV7.js.map → agent-catalog-UYD26QDK.js.map} +0 -0
- /package/dist/{api-client-XTLRRFPX.js.map → api-client-PEMHYL5U.js.map} +0 -0
- /package/dist/{api-server-5VNYFWJE.js.map → api-server-DATG2KBR.js.map} +0 -0
- /package/dist/{api-server-JLBDKCU4.js.map → api-server-L5Z7XACW.js.map} +0 -0
- /package/dist/{chunk-YIGBJFJL.js.map → chunk-7GXEMMEV.js.map} +0 -0
- /package/dist/{config-KN6NKKPF.js.map → config-X4UP7H6R.js.map} +0 -0
- /package/dist/{config-editor-76RVZS4B.js.map → config-editor-7BENRVG5.js.map} +0 -0
- /package/dist/{config-registry-ZXAIJNYB.js.map → config-registry-M3FFWEVM.js.map} +0 -0
- /package/dist/{context-NXXW62NJ.js.map → context-FVGCU5TI.js.map} +0 -0
- /package/dist/{core-plugins-OCHKGCIZ.js.map → core-plugins-JSY2I44L.js.map} +0 -0
- /package/dist/{daemon-XFEMMJSZ.js.map → daemon-UOSRDEXW.js.map} +0 -0
- /package/dist/{doctor-AV6AUO22.js.map → doctor-6DLACBR4.js.map} +0 -0
- /package/dist/{file-service-HHB3JQIO.js.map → file-service-FQQYME7M.js.map} +0 -0
- /package/dist/{install-jq-ISTGT263.js.map → install-jq-SN4IA5K4.js.map} +0 -0
- /package/dist/{security-2BA265LN.js.map → instance-context-FLCE7VZ4.js.map} +0 -0
- /package/dist/{speech-SG62JYIF.js.map → instance-registry-SW5FWKHO.js.map} +0 -0
- /package/dist/{post-upgrade-Y26S2ZQ7.js.map → post-upgrade-F4YPMTUT.js.map} +0 -0
- /package/dist/{telegram-EAVRDNFU.js.map → security-O4XGN2CM.js.map} +0 -0
- /package/dist/{tunnel-HWJ27WDH.js.map → speech-GHTSWDAN.js.map} +0 -0
|
@@ -1,531 +1,19 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
} from "./chunk-
|
|
2
|
+
TunnelRegistry
|
|
3
|
+
} from "./chunk-NHD5XDD2.js";
|
|
4
4
|
import {
|
|
5
5
|
createChildLogger
|
|
6
6
|
} from "./chunk-R6KZYF7D.js";
|
|
7
|
+
import "./chunk-ZSLHHQPQ.js";
|
|
7
8
|
|
|
8
9
|
// src/plugins/tunnel/tunnel-service.ts
|
|
9
10
|
import { serve } from "@hono/node-server";
|
|
10
11
|
|
|
11
|
-
// src/plugins/tunnel/tunnel-registry.ts
|
|
12
|
-
import fs2 from "fs";
|
|
13
|
-
import path2 from "path";
|
|
14
|
-
import os2 from "os";
|
|
15
|
-
|
|
16
|
-
// src/plugins/tunnel/providers/cloudflare.ts
|
|
17
|
-
import { spawn } from "child_process";
|
|
18
|
-
import fs from "fs";
|
|
19
|
-
import path from "path";
|
|
20
|
-
import os from "os";
|
|
21
|
-
var log = createChildLogger({ module: "cloudflare-tunnel" });
|
|
22
|
-
var CloudflareTunnelProvider = class {
|
|
23
|
-
child = null;
|
|
24
|
-
publicUrl = "";
|
|
25
|
-
options;
|
|
26
|
-
constructor(options = {}) {
|
|
27
|
-
this.options = options;
|
|
28
|
-
}
|
|
29
|
-
async start(localPort) {
|
|
30
|
-
let binaryPath = this.findBinary();
|
|
31
|
-
if (!binaryPath) {
|
|
32
|
-
log.warn("cloudflared not found locally, attempting auto-install as fallback...");
|
|
33
|
-
try {
|
|
34
|
-
const { ensureCloudflared } = await import("./install-cloudflared-JRJ4BSOM.js");
|
|
35
|
-
binaryPath = await ensureCloudflared();
|
|
36
|
-
} catch (err) {
|
|
37
|
-
throw new Error(`cloudflared is not installed and auto-install failed: ${err.message}`);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
const args = ["tunnel", "--url", `http://localhost:${localPort}`];
|
|
41
|
-
if (this.options.domain) {
|
|
42
|
-
args.push("--hostname", String(this.options.domain));
|
|
43
|
-
}
|
|
44
|
-
return new Promise((resolve2, reject) => {
|
|
45
|
-
const timeout = setTimeout(() => {
|
|
46
|
-
this.stop();
|
|
47
|
-
reject(new Error("Cloudflare tunnel timed out after 30s"));
|
|
48
|
-
}, 3e4);
|
|
49
|
-
try {
|
|
50
|
-
this.child = spawn(binaryPath, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
51
|
-
} catch {
|
|
52
|
-
clearTimeout(timeout);
|
|
53
|
-
reject(new Error(`Failed to start cloudflared at ${binaryPath}`));
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
const urlPattern = /https:\/\/[a-zA-Z0-9-]+\.trycloudflare\.com/;
|
|
57
|
-
const onData = (data) => {
|
|
58
|
-
const line = data.toString();
|
|
59
|
-
log.debug(line.trim());
|
|
60
|
-
const match = line.match(urlPattern);
|
|
61
|
-
if (match) {
|
|
62
|
-
clearTimeout(timeout);
|
|
63
|
-
this.publicUrl = match[0];
|
|
64
|
-
log.info({ url: this.publicUrl }, "Cloudflare tunnel ready");
|
|
65
|
-
resolve2(this.publicUrl);
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
this.child.stdout?.on("data", onData);
|
|
69
|
-
this.child.stderr?.on("data", onData);
|
|
70
|
-
this.child.on("error", (err) => {
|
|
71
|
-
clearTimeout(timeout);
|
|
72
|
-
reject(new Error(`cloudflared failed to start: ${err.message}`));
|
|
73
|
-
});
|
|
74
|
-
this.child.on("exit", (code) => {
|
|
75
|
-
if (!this.publicUrl) {
|
|
76
|
-
clearTimeout(timeout);
|
|
77
|
-
reject(new Error(`cloudflared exited with code ${code} before establishing tunnel`));
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
async stop() {
|
|
83
|
-
if (this.child) {
|
|
84
|
-
this.child.kill("SIGTERM");
|
|
85
|
-
this.child = null;
|
|
86
|
-
log.info("Cloudflare tunnel stopped");
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
getPublicUrl() {
|
|
90
|
-
return this.publicUrl;
|
|
91
|
-
}
|
|
92
|
-
findBinary() {
|
|
93
|
-
if (commandExists("cloudflared")) return "cloudflared";
|
|
94
|
-
const binPath = path.join(os.homedir(), ".openacp", "bin", "cloudflared");
|
|
95
|
-
if (fs.existsSync(binPath)) return binPath;
|
|
96
|
-
return null;
|
|
97
|
-
}
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
// src/plugins/tunnel/providers/ngrok.ts
|
|
101
|
-
import { spawn as spawn2 } from "child_process";
|
|
102
|
-
var log2 = createChildLogger({ module: "ngrok-tunnel" });
|
|
103
|
-
var NgrokTunnelProvider = class {
|
|
104
|
-
child = null;
|
|
105
|
-
publicUrl = "";
|
|
106
|
-
options;
|
|
107
|
-
constructor(options = {}) {
|
|
108
|
-
this.options = options;
|
|
109
|
-
}
|
|
110
|
-
async start(localPort) {
|
|
111
|
-
const args = ["http", String(localPort), "--log", "stdout", "--log-format", "json"];
|
|
112
|
-
if (this.options.authtoken) {
|
|
113
|
-
args.push("--authtoken", String(this.options.authtoken));
|
|
114
|
-
}
|
|
115
|
-
if (this.options.domain) {
|
|
116
|
-
args.push("--domain", String(this.options.domain));
|
|
117
|
-
}
|
|
118
|
-
if (this.options.region) {
|
|
119
|
-
args.push("--region", String(this.options.region));
|
|
120
|
-
}
|
|
121
|
-
return new Promise((resolve2, reject) => {
|
|
122
|
-
const timeout = setTimeout(() => {
|
|
123
|
-
this.stop();
|
|
124
|
-
reject(new Error("ngrok tunnel timed out after 30s. Is ngrok installed?"));
|
|
125
|
-
}, 3e4);
|
|
126
|
-
try {
|
|
127
|
-
this.child = spawn2("ngrok", args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
128
|
-
} catch {
|
|
129
|
-
clearTimeout(timeout);
|
|
130
|
-
reject(new Error(
|
|
131
|
-
"Failed to start ngrok. Install it from https://ngrok.com/download"
|
|
132
|
-
));
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
const urlPattern = /https:\/\/[a-zA-Z0-9-]+\.ngrok(-free)?\.app/;
|
|
136
|
-
const onData = (data) => {
|
|
137
|
-
const line = data.toString();
|
|
138
|
-
log2.debug(line.trim());
|
|
139
|
-
const match = line.match(urlPattern);
|
|
140
|
-
if (match) {
|
|
141
|
-
clearTimeout(timeout);
|
|
142
|
-
this.publicUrl = match[0];
|
|
143
|
-
log2.info({ url: this.publicUrl }, "ngrok tunnel ready");
|
|
144
|
-
resolve2(this.publicUrl);
|
|
145
|
-
}
|
|
146
|
-
};
|
|
147
|
-
this.child.stdout?.on("data", onData);
|
|
148
|
-
this.child.stderr?.on("data", onData);
|
|
149
|
-
this.child.on("error", (err) => {
|
|
150
|
-
clearTimeout(timeout);
|
|
151
|
-
reject(new Error(
|
|
152
|
-
`ngrok failed to start: ${err.message}. Install it from https://ngrok.com/download`
|
|
153
|
-
));
|
|
154
|
-
});
|
|
155
|
-
this.child.on("exit", (code) => {
|
|
156
|
-
if (!this.publicUrl) {
|
|
157
|
-
clearTimeout(timeout);
|
|
158
|
-
reject(new Error(`ngrok exited with code ${code} before establishing tunnel`));
|
|
159
|
-
}
|
|
160
|
-
});
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
async stop() {
|
|
164
|
-
if (this.child) {
|
|
165
|
-
this.child.kill("SIGTERM");
|
|
166
|
-
this.child = null;
|
|
167
|
-
log2.info("ngrok tunnel stopped");
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
getPublicUrl() {
|
|
171
|
-
return this.publicUrl;
|
|
172
|
-
}
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
// src/plugins/tunnel/providers/bore.ts
|
|
176
|
-
import { spawn as spawn3 } from "child_process";
|
|
177
|
-
var log3 = createChildLogger({ module: "bore-tunnel" });
|
|
178
|
-
var BoreTunnelProvider = class {
|
|
179
|
-
child = null;
|
|
180
|
-
publicUrl = "";
|
|
181
|
-
options;
|
|
182
|
-
constructor(options = {}) {
|
|
183
|
-
this.options = options;
|
|
184
|
-
}
|
|
185
|
-
async start(localPort) {
|
|
186
|
-
const server = String(this.options.server || "bore.pub");
|
|
187
|
-
const args = ["local", String(localPort), "--to", server];
|
|
188
|
-
if (this.options.port) {
|
|
189
|
-
args.push("--port", String(this.options.port));
|
|
190
|
-
}
|
|
191
|
-
if (this.options.secret) {
|
|
192
|
-
args.push("--secret", String(this.options.secret));
|
|
193
|
-
}
|
|
194
|
-
return new Promise((resolve2, reject) => {
|
|
195
|
-
const timeout = setTimeout(() => {
|
|
196
|
-
this.stop();
|
|
197
|
-
reject(new Error("Bore tunnel timed out after 30s. Is bore installed?"));
|
|
198
|
-
}, 3e4);
|
|
199
|
-
try {
|
|
200
|
-
this.child = spawn3("bore", args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
201
|
-
} catch {
|
|
202
|
-
clearTimeout(timeout);
|
|
203
|
-
reject(new Error(
|
|
204
|
-
"Failed to start bore. Install it from https://github.com/ekzhang/bore"
|
|
205
|
-
));
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
const urlPattern = /listening at ([^\s]+):(\d+)/;
|
|
209
|
-
const onData = (data) => {
|
|
210
|
-
const line = data.toString();
|
|
211
|
-
log3.debug(line.trim());
|
|
212
|
-
const match = line.match(urlPattern);
|
|
213
|
-
if (match) {
|
|
214
|
-
clearTimeout(timeout);
|
|
215
|
-
this.publicUrl = `http://${match[1]}:${match[2]}`;
|
|
216
|
-
log3.info({ url: this.publicUrl }, "Bore tunnel ready");
|
|
217
|
-
resolve2(this.publicUrl);
|
|
218
|
-
}
|
|
219
|
-
};
|
|
220
|
-
this.child.stdout?.on("data", onData);
|
|
221
|
-
this.child.stderr?.on("data", onData);
|
|
222
|
-
this.child.on("error", (err) => {
|
|
223
|
-
clearTimeout(timeout);
|
|
224
|
-
reject(new Error(
|
|
225
|
-
`bore failed to start: ${err.message}. Install it from https://github.com/ekzhang/bore`
|
|
226
|
-
));
|
|
227
|
-
});
|
|
228
|
-
this.child.on("exit", (code) => {
|
|
229
|
-
if (!this.publicUrl) {
|
|
230
|
-
clearTimeout(timeout);
|
|
231
|
-
reject(new Error(`bore exited with code ${code} before establishing tunnel`));
|
|
232
|
-
}
|
|
233
|
-
});
|
|
234
|
-
});
|
|
235
|
-
}
|
|
236
|
-
async stop() {
|
|
237
|
-
if (this.child) {
|
|
238
|
-
this.child.kill("SIGTERM");
|
|
239
|
-
this.child = null;
|
|
240
|
-
log3.info("Bore tunnel stopped");
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
getPublicUrl() {
|
|
244
|
-
return this.publicUrl;
|
|
245
|
-
}
|
|
246
|
-
};
|
|
247
|
-
|
|
248
|
-
// src/plugins/tunnel/providers/tailscale.ts
|
|
249
|
-
import { spawn as spawn4, execSync } from "child_process";
|
|
250
|
-
var log4 = createChildLogger({ module: "tailscale-tunnel" });
|
|
251
|
-
var TailscaleTunnelProvider = class {
|
|
252
|
-
child = null;
|
|
253
|
-
publicUrl = "";
|
|
254
|
-
options;
|
|
255
|
-
constructor(options = {}) {
|
|
256
|
-
this.options = options;
|
|
257
|
-
}
|
|
258
|
-
async start(localPort) {
|
|
259
|
-
let hostname = "";
|
|
260
|
-
try {
|
|
261
|
-
const statusJson = execSync("tailscale status --json", { encoding: "utf-8" });
|
|
262
|
-
const status = JSON.parse(statusJson);
|
|
263
|
-
hostname = String(status.Self.DNSName).replace(/\.$/, "");
|
|
264
|
-
log4.debug({ hostname }, "Resolved Tailscale hostname");
|
|
265
|
-
} catch (err) {
|
|
266
|
-
log4.warn("Failed to resolve Tailscale hostname via status --json");
|
|
267
|
-
}
|
|
268
|
-
const args = ["funnel", String(localPort)];
|
|
269
|
-
if (this.options.bg) {
|
|
270
|
-
args.push("--bg");
|
|
271
|
-
}
|
|
272
|
-
return new Promise((resolve2, reject) => {
|
|
273
|
-
const timeout = setTimeout(() => {
|
|
274
|
-
this.stop();
|
|
275
|
-
reject(new Error("Tailscale funnel timed out after 30s. Is tailscale installed?"));
|
|
276
|
-
}, 3e4);
|
|
277
|
-
try {
|
|
278
|
-
this.child = spawn4("tailscale", args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
279
|
-
} catch {
|
|
280
|
-
clearTimeout(timeout);
|
|
281
|
-
reject(new Error(
|
|
282
|
-
"Failed to start tailscale. Install it from https://tailscale.com/download"
|
|
283
|
-
));
|
|
284
|
-
return;
|
|
285
|
-
}
|
|
286
|
-
const urlPattern = /https:\/\/[^\s]+/;
|
|
287
|
-
const onData = (data) => {
|
|
288
|
-
const line = data.toString();
|
|
289
|
-
log4.debug(line.trim());
|
|
290
|
-
const match = line.match(urlPattern);
|
|
291
|
-
if (match) {
|
|
292
|
-
clearTimeout(timeout);
|
|
293
|
-
this.publicUrl = match[0];
|
|
294
|
-
log4.info({ url: this.publicUrl }, "Tailscale funnel ready");
|
|
295
|
-
resolve2(this.publicUrl);
|
|
296
|
-
}
|
|
297
|
-
};
|
|
298
|
-
this.child.stdout?.on("data", onData);
|
|
299
|
-
this.child.stderr?.on("data", onData);
|
|
300
|
-
this.child.on("error", (err) => {
|
|
301
|
-
clearTimeout(timeout);
|
|
302
|
-
reject(new Error(
|
|
303
|
-
`tailscale failed to start: ${err.message}. Install it from https://tailscale.com/download`
|
|
304
|
-
));
|
|
305
|
-
});
|
|
306
|
-
this.child.on("exit", (code) => {
|
|
307
|
-
if (!this.publicUrl) {
|
|
308
|
-
clearTimeout(timeout);
|
|
309
|
-
if (hostname) {
|
|
310
|
-
this.publicUrl = `https://${hostname}`;
|
|
311
|
-
log4.info({ url: this.publicUrl }, "Tailscale funnel ready (constructed from hostname)");
|
|
312
|
-
resolve2(this.publicUrl);
|
|
313
|
-
} else {
|
|
314
|
-
reject(new Error(`tailscale exited with code ${code} before establishing funnel`));
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
});
|
|
318
|
-
});
|
|
319
|
-
}
|
|
320
|
-
async stop() {
|
|
321
|
-
if (this.child) {
|
|
322
|
-
this.child.kill("SIGTERM");
|
|
323
|
-
this.child = null;
|
|
324
|
-
log4.info("Tailscale funnel stopped");
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
getPublicUrl() {
|
|
328
|
-
return this.publicUrl;
|
|
329
|
-
}
|
|
330
|
-
};
|
|
331
|
-
|
|
332
|
-
// src/plugins/tunnel/tunnel-registry.ts
|
|
333
|
-
var log5 = createChildLogger({ module: "tunnel-registry" });
|
|
334
|
-
var REGISTRY_PATH = path2.join(os2.homedir(), ".openacp", "tunnels.json");
|
|
335
|
-
var TunnelRegistry = class {
|
|
336
|
-
entries = /* @__PURE__ */ new Map();
|
|
337
|
-
saveTimeout = null;
|
|
338
|
-
maxUserTunnels;
|
|
339
|
-
providerOptions;
|
|
340
|
-
constructor(opts = {}) {
|
|
341
|
-
this.maxUserTunnels = opts.maxUserTunnels ?? 5;
|
|
342
|
-
this.providerOptions = opts.providerOptions ?? {};
|
|
343
|
-
}
|
|
344
|
-
async add(port, opts) {
|
|
345
|
-
if (this.entries.has(port)) {
|
|
346
|
-
const existing = this.entries.get(port);
|
|
347
|
-
if (existing.entry.status === "active" || existing.entry.status === "starting") {
|
|
348
|
-
throw new Error(`Port ${port} is already tunneled \u2192 ${existing.entry.publicUrl || "starting..."}`);
|
|
349
|
-
}
|
|
350
|
-
this.entries.delete(port);
|
|
351
|
-
}
|
|
352
|
-
if (opts.type === "user") {
|
|
353
|
-
const userCount = this.list(false).filter((e) => e.status === "active" || e.status === "starting").length;
|
|
354
|
-
if (userCount >= this.maxUserTunnels) {
|
|
355
|
-
throw new Error(`Max user tunnels (${this.maxUserTunnels}) reached. Stop a tunnel first.`);
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
const entry = {
|
|
359
|
-
port,
|
|
360
|
-
type: opts.type,
|
|
361
|
-
provider: opts.provider,
|
|
362
|
-
label: opts.label,
|
|
363
|
-
sessionId: opts.sessionId,
|
|
364
|
-
status: "starting",
|
|
365
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
366
|
-
};
|
|
367
|
-
const provider = this.createProvider(opts.provider);
|
|
368
|
-
const spawnPromise = provider.start(port).then((url) => {
|
|
369
|
-
entry.publicUrl = url;
|
|
370
|
-
entry.status = "active";
|
|
371
|
-
log5.info({ port, url, label: opts.label }, "Tunnel active");
|
|
372
|
-
this.scheduleSave();
|
|
373
|
-
return url;
|
|
374
|
-
}).catch((err) => {
|
|
375
|
-
entry.status = "failed";
|
|
376
|
-
log5.error({ port, err: err.message }, "Tunnel failed to start");
|
|
377
|
-
this.scheduleSave();
|
|
378
|
-
throw err;
|
|
379
|
-
});
|
|
380
|
-
this.entries.set(port, { entry, process: provider, spawnPromise });
|
|
381
|
-
this.scheduleSave();
|
|
382
|
-
await spawnPromise;
|
|
383
|
-
return entry;
|
|
384
|
-
}
|
|
385
|
-
async stop(port) {
|
|
386
|
-
const live = this.entries.get(port);
|
|
387
|
-
if (!live) return;
|
|
388
|
-
if (live.entry.type === "system") {
|
|
389
|
-
throw new Error("Cannot stop system tunnel");
|
|
390
|
-
}
|
|
391
|
-
if (live.spawnPromise) {
|
|
392
|
-
try {
|
|
393
|
-
await live.spawnPromise;
|
|
394
|
-
} catch {
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
if (live.process) {
|
|
398
|
-
await live.process.stop();
|
|
399
|
-
}
|
|
400
|
-
this.entries.delete(port);
|
|
401
|
-
this.scheduleSave();
|
|
402
|
-
log5.info({ port, label: live.entry.label }, "Tunnel stopped");
|
|
403
|
-
}
|
|
404
|
-
async stopBySession(sessionId) {
|
|
405
|
-
const stopped = [];
|
|
406
|
-
const toStop = this.getBySession(sessionId);
|
|
407
|
-
for (const entry of toStop) {
|
|
408
|
-
try {
|
|
409
|
-
await this.stop(entry.port);
|
|
410
|
-
stopped.push(entry);
|
|
411
|
-
} catch {
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
return stopped;
|
|
415
|
-
}
|
|
416
|
-
async stopAllUser() {
|
|
417
|
-
const userEntries = this.list(false);
|
|
418
|
-
for (const entry of userEntries) {
|
|
419
|
-
try {
|
|
420
|
-
await this.stop(entry.port);
|
|
421
|
-
} catch {
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
async shutdown() {
|
|
426
|
-
for (const [, live] of this.entries) {
|
|
427
|
-
if (live.spawnPromise) {
|
|
428
|
-
try {
|
|
429
|
-
await live.spawnPromise;
|
|
430
|
-
} catch {
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
if (live.process) {
|
|
434
|
-
await live.process.stop();
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
this.entries.clear();
|
|
438
|
-
this.scheduleSave();
|
|
439
|
-
}
|
|
440
|
-
list(includeSystem = false) {
|
|
441
|
-
const entries = Array.from(this.entries.values()).map((l) => l.entry);
|
|
442
|
-
if (includeSystem) return entries;
|
|
443
|
-
return entries.filter((e) => e.type === "user");
|
|
444
|
-
}
|
|
445
|
-
get(port) {
|
|
446
|
-
return this.entries.get(port)?.entry ?? null;
|
|
447
|
-
}
|
|
448
|
-
getBySession(sessionId) {
|
|
449
|
-
return this.list(false).filter((e) => e.sessionId === sessionId);
|
|
450
|
-
}
|
|
451
|
-
getSystemEntry() {
|
|
452
|
-
for (const live of this.entries.values()) {
|
|
453
|
-
if (live.entry.type === "system") return live.entry;
|
|
454
|
-
}
|
|
455
|
-
return null;
|
|
456
|
-
}
|
|
457
|
-
async restore() {
|
|
458
|
-
if (!fs2.existsSync(REGISTRY_PATH)) return;
|
|
459
|
-
try {
|
|
460
|
-
const raw = JSON.parse(fs2.readFileSync(REGISTRY_PATH, "utf-8"));
|
|
461
|
-
log5.info({ count: raw.length }, "Restoring tunnels");
|
|
462
|
-
const userEntries = raw.filter((e) => e.type === "user");
|
|
463
|
-
for (const persisted of userEntries) {
|
|
464
|
-
try {
|
|
465
|
-
await this.add(persisted.port, {
|
|
466
|
-
type: persisted.type,
|
|
467
|
-
provider: persisted.provider,
|
|
468
|
-
label: persisted.label,
|
|
469
|
-
sessionId: persisted.sessionId
|
|
470
|
-
});
|
|
471
|
-
} catch (err) {
|
|
472
|
-
log5.warn({ port: persisted.port, err: err.message }, "Failed to restore tunnel");
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
} catch (err) {
|
|
476
|
-
log5.warn({ err: err.message }, "Failed to read tunnels.json");
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
createProvider(name) {
|
|
480
|
-
switch (name) {
|
|
481
|
-
case "cloudflare":
|
|
482
|
-
return new CloudflareTunnelProvider(this.providerOptions);
|
|
483
|
-
case "ngrok":
|
|
484
|
-
return new NgrokTunnelProvider(this.providerOptions);
|
|
485
|
-
case "bore":
|
|
486
|
-
return new BoreTunnelProvider(this.providerOptions);
|
|
487
|
-
case "tailscale":
|
|
488
|
-
return new TailscaleTunnelProvider(this.providerOptions);
|
|
489
|
-
default:
|
|
490
|
-
log5.warn({ provider: name }, "Unknown provider, falling back to cloudflare");
|
|
491
|
-
return new CloudflareTunnelProvider(this.providerOptions);
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
scheduleSave() {
|
|
495
|
-
if (this.saveTimeout) clearTimeout(this.saveTimeout);
|
|
496
|
-
this.saveTimeout = setTimeout(() => this.save(), 2e3);
|
|
497
|
-
}
|
|
498
|
-
save() {
|
|
499
|
-
const data = Array.from(this.entries.values()).map((l) => ({
|
|
500
|
-
port: l.entry.port,
|
|
501
|
-
type: l.entry.type,
|
|
502
|
-
provider: l.entry.provider,
|
|
503
|
-
label: l.entry.label,
|
|
504
|
-
sessionId: l.entry.sessionId,
|
|
505
|
-
createdAt: l.entry.createdAt
|
|
506
|
-
}));
|
|
507
|
-
try {
|
|
508
|
-
const dir = path2.dirname(REGISTRY_PATH);
|
|
509
|
-
fs2.mkdirSync(dir, { recursive: true });
|
|
510
|
-
fs2.writeFileSync(REGISTRY_PATH, JSON.stringify(data, null, 2));
|
|
511
|
-
} catch (err) {
|
|
512
|
-
log5.error({ err: err.message }, "Failed to save tunnels.json");
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
flush() {
|
|
516
|
-
if (this.saveTimeout) {
|
|
517
|
-
clearTimeout(this.saveTimeout);
|
|
518
|
-
this.saveTimeout = null;
|
|
519
|
-
}
|
|
520
|
-
this.save();
|
|
521
|
-
}
|
|
522
|
-
};
|
|
523
|
-
|
|
524
12
|
// src/plugins/tunnel/viewer-store.ts
|
|
525
|
-
import * as
|
|
526
|
-
import * as
|
|
13
|
+
import * as fs from "fs";
|
|
14
|
+
import * as path from "path";
|
|
527
15
|
import { nanoid } from "nanoid";
|
|
528
|
-
var
|
|
16
|
+
var log = createChildLogger({ module: "viewer-store" });
|
|
529
17
|
var MAX_CONTENT_SIZE = 1e6;
|
|
530
18
|
var EXTENSION_LANGUAGE = {
|
|
531
19
|
".ts": "typescript",
|
|
@@ -573,11 +61,11 @@ var ViewerStore = class {
|
|
|
573
61
|
}
|
|
574
62
|
storeFile(sessionId, filePath, content, workingDirectory) {
|
|
575
63
|
if (!this.isPathAllowed(filePath, workingDirectory)) {
|
|
576
|
-
|
|
64
|
+
log.warn({ filePath, workingDirectory }, "Path outside workspace, rejecting");
|
|
577
65
|
return null;
|
|
578
66
|
}
|
|
579
67
|
if (content.length > MAX_CONTENT_SIZE) {
|
|
580
|
-
|
|
68
|
+
log.debug({ filePath, size: content.length }, "File too large for viewer");
|
|
581
69
|
return null;
|
|
582
70
|
}
|
|
583
71
|
const id = nanoid(12);
|
|
@@ -593,17 +81,17 @@ var ViewerStore = class {
|
|
|
593
81
|
createdAt: now,
|
|
594
82
|
expiresAt: now + this.ttlMs
|
|
595
83
|
});
|
|
596
|
-
|
|
84
|
+
log.debug({ id, filePath }, "Stored file for viewing");
|
|
597
85
|
return id;
|
|
598
86
|
}
|
|
599
87
|
storeDiff(sessionId, filePath, oldContent, newContent, workingDirectory) {
|
|
600
88
|
if (!this.isPathAllowed(filePath, workingDirectory)) {
|
|
601
|
-
|
|
89
|
+
log.warn({ filePath, workingDirectory }, "Path outside workspace, rejecting");
|
|
602
90
|
return null;
|
|
603
91
|
}
|
|
604
92
|
const combined = oldContent.length + newContent.length;
|
|
605
93
|
if (combined > MAX_CONTENT_SIZE) {
|
|
606
|
-
|
|
94
|
+
log.debug({ filePath, size: combined }, "Diff content too large for viewer");
|
|
607
95
|
return null;
|
|
608
96
|
}
|
|
609
97
|
const id = nanoid(12);
|
|
@@ -620,12 +108,12 @@ var ViewerStore = class {
|
|
|
620
108
|
createdAt: now,
|
|
621
109
|
expiresAt: now + this.ttlMs
|
|
622
110
|
});
|
|
623
|
-
|
|
111
|
+
log.debug({ id, filePath }, "Stored diff for viewing");
|
|
624
112
|
return id;
|
|
625
113
|
}
|
|
626
114
|
storeOutput(sessionId, label, output) {
|
|
627
115
|
if (output.length > MAX_CONTENT_SIZE) {
|
|
628
|
-
|
|
116
|
+
log.debug({ label, size: output.length }, "Output too large for viewer");
|
|
629
117
|
return null;
|
|
630
118
|
}
|
|
631
119
|
const id = nanoid(12);
|
|
@@ -641,7 +129,7 @@ var ViewerStore = class {
|
|
|
641
129
|
createdAt: now,
|
|
642
130
|
expiresAt: now + this.ttlMs
|
|
643
131
|
});
|
|
644
|
-
|
|
132
|
+
log.debug({ id, label }, "Stored output for viewing");
|
|
645
133
|
return id;
|
|
646
134
|
}
|
|
647
135
|
get(id) {
|
|
@@ -663,7 +151,7 @@ var ViewerStore = class {
|
|
|
663
151
|
}
|
|
664
152
|
}
|
|
665
153
|
if (removed > 0) {
|
|
666
|
-
|
|
154
|
+
log.debug({ removed, remaining: this.entries.size }, "Cleaned up expired viewer entries");
|
|
667
155
|
}
|
|
668
156
|
}
|
|
669
157
|
isPathAllowed(filePath, workingDirectory) {
|
|
@@ -671,24 +159,24 @@ var ViewerStore = class {
|
|
|
671
159
|
let resolved;
|
|
672
160
|
let workspace;
|
|
673
161
|
try {
|
|
674
|
-
resolved =
|
|
162
|
+
resolved = fs.realpathSync(path.resolve(workingDirectory, filePath));
|
|
675
163
|
} catch {
|
|
676
|
-
resolved =
|
|
164
|
+
resolved = path.resolve(workingDirectory, filePath);
|
|
677
165
|
}
|
|
678
166
|
try {
|
|
679
|
-
workspace =
|
|
167
|
+
workspace = fs.realpathSync(path.resolve(workingDirectory));
|
|
680
168
|
} catch {
|
|
681
|
-
workspace =
|
|
169
|
+
workspace = path.resolve(workingDirectory);
|
|
682
170
|
}
|
|
683
171
|
if (caseInsensitive) {
|
|
684
172
|
const rLower = resolved.toLowerCase();
|
|
685
173
|
const wLower = workspace.toLowerCase();
|
|
686
|
-
return rLower.startsWith(wLower +
|
|
174
|
+
return rLower.startsWith(wLower + path.sep) || rLower === wLower;
|
|
687
175
|
}
|
|
688
|
-
return resolved.startsWith(workspace +
|
|
176
|
+
return resolved.startsWith(workspace + path.sep) || resolved === workspace;
|
|
689
177
|
}
|
|
690
178
|
detectLanguage(filePath) {
|
|
691
|
-
const ext =
|
|
179
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
692
180
|
return EXTENSION_LANGUAGE[ext];
|
|
693
181
|
}
|
|
694
182
|
destroy() {
|
|
@@ -1142,7 +630,7 @@ function createTunnelServer(store, authToken) {
|
|
|
1142
630
|
}
|
|
1143
631
|
|
|
1144
632
|
// src/plugins/tunnel/tunnel-service.ts
|
|
1145
|
-
var
|
|
633
|
+
var log2 = createChildLogger({ module: "tunnel" });
|
|
1146
634
|
var TunnelService = class {
|
|
1147
635
|
registry;
|
|
1148
636
|
store;
|
|
@@ -1150,12 +638,13 @@ var TunnelService = class {
|
|
|
1150
638
|
config;
|
|
1151
639
|
systemPort = 0;
|
|
1152
640
|
startError;
|
|
1153
|
-
constructor(config) {
|
|
641
|
+
constructor(config, registryPath) {
|
|
1154
642
|
this.config = config;
|
|
1155
643
|
this.store = new ViewerStore(config.storeTtlMinutes);
|
|
1156
644
|
this.registry = new TunnelRegistry({
|
|
1157
645
|
maxUserTunnels: config.maxUserTunnels ?? 5,
|
|
1158
|
-
providerOptions: config.options
|
|
646
|
+
providerOptions: config.options,
|
|
647
|
+
registryPath
|
|
1159
648
|
});
|
|
1160
649
|
}
|
|
1161
650
|
async start() {
|
|
@@ -1166,23 +655,28 @@ var TunnelService = class {
|
|
|
1166
655
|
for (let i = 0; i < maxRetries; i++) {
|
|
1167
656
|
const port = this.config.port + i;
|
|
1168
657
|
const server = serve({ fetch: app.fetch, port });
|
|
1169
|
-
const
|
|
1170
|
-
server.on("listening", () => resolve2(true));
|
|
1171
|
-
server.on("error", () => resolve2(false));
|
|
658
|
+
const result = await new Promise((resolve2) => {
|
|
659
|
+
server.on("listening", () => resolve2({ ok: true }));
|
|
660
|
+
server.on("error", (err) => resolve2({ ok: false, code: err.code }));
|
|
1172
661
|
});
|
|
1173
|
-
if (ok) {
|
|
662
|
+
if (result.ok) {
|
|
1174
663
|
this.server = server;
|
|
1175
664
|
actualPort = port;
|
|
1176
665
|
if (i > 0) {
|
|
1177
|
-
|
|
666
|
+
log2.info({ configuredPort: this.config.port, actualPort }, "Configured port in use, using next available");
|
|
1178
667
|
}
|
|
1179
|
-
|
|
668
|
+
log2.info({ port: actualPort }, "Tunnel HTTP server started");
|
|
1180
669
|
break;
|
|
1181
670
|
}
|
|
1182
671
|
server.close();
|
|
672
|
+
if (result.code === "EACCES") {
|
|
673
|
+
log2.error({ port }, "Permission denied binding to port (try a port > 1024)");
|
|
674
|
+
break;
|
|
675
|
+
}
|
|
1183
676
|
}
|
|
1184
677
|
if (!this.server) {
|
|
1185
|
-
|
|
678
|
+
log2.error({ port: this.config.port }, "Failed to start tunnel HTTP server \u2014 no available port");
|
|
679
|
+
this.startError = `HTTP server failed to bind (tried ports ${this.config.port}-${this.config.port + maxRetries - 1})`;
|
|
1186
680
|
return `http://localhost:${this.config.port}`;
|
|
1187
681
|
}
|
|
1188
682
|
this.systemPort = actualPort;
|
|
@@ -1194,7 +688,7 @@ var TunnelService = class {
|
|
|
1194
688
|
});
|
|
1195
689
|
} catch (err) {
|
|
1196
690
|
this.startError = err.message;
|
|
1197
|
-
|
|
691
|
+
log2.warn({ err: this.startError }, "System tunnel failed, running on localhost");
|
|
1198
692
|
}
|
|
1199
693
|
await this.registry.restore();
|
|
1200
694
|
const systemEntry = this.registry.getSystemEntry();
|
|
@@ -1208,7 +702,7 @@ var TunnelService = class {
|
|
|
1208
702
|
this.server = null;
|
|
1209
703
|
}
|
|
1210
704
|
this.store.destroy();
|
|
1211
|
-
|
|
705
|
+
log2.info("Tunnel service stopped");
|
|
1212
706
|
}
|
|
1213
707
|
// --- User tunnel management ---
|
|
1214
708
|
async addTunnel(port, opts) {
|
|
@@ -1258,4 +752,4 @@ var TunnelService = class {
|
|
|
1258
752
|
export {
|
|
1259
753
|
TunnelService
|
|
1260
754
|
};
|
|
1261
|
-
//# sourceMappingURL=tunnel-service-
|
|
755
|
+
//# sourceMappingURL=tunnel-service-TBAHDXMF.js.map
|