@openacp/cli 2026.331.1 → 2026.331.2

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.
Files changed (181) hide show
  1. package/README.md +2 -1
  2. package/dist/cli.js +24932 -270
  3. package/dist/cli.js.map +1 -1
  4. package/dist/data/registry-snapshot.json +1 -1
  5. package/dist/index.js +17626 -409
  6. package/dist/index.js.map +1 -1
  7. package/package.json +2 -2
  8. package/dist/adapter-ELG3VRZ3.js +0 -14
  9. package/dist/adapter-ELG3VRZ3.js.map +0 -1
  10. package/dist/agent-catalog-UYD26QDK.js +0 -10
  11. package/dist/agent-catalog-UYD26QDK.js.map +0 -1
  12. package/dist/agent-dependencies-ED2ZTUHG.js +0 -23
  13. package/dist/agent-dependencies-ED2ZTUHG.js.map +0 -1
  14. package/dist/agent-registry-YOGP656W.js +0 -8
  15. package/dist/agent-registry-YOGP656W.js.map +0 -1
  16. package/dist/agent-store-5UHZH2XI.js +0 -8
  17. package/dist/agent-store-5UHZH2XI.js.map +0 -1
  18. package/dist/api-client-PEMHYL5U.js +0 -13
  19. package/dist/api-client-PEMHYL5U.js.map +0 -1
  20. package/dist/api-server-DATG2KBR.js +0 -10
  21. package/dist/api-server-DATG2KBR.js.map +0 -1
  22. package/dist/api-server-L5Z7XACW.js +0 -7
  23. package/dist/api-server-L5Z7XACW.js.map +0 -1
  24. package/dist/autostart-CUPZMKKC.js +0 -22
  25. package/dist/autostart-CUPZMKKC.js.map +0 -1
  26. package/dist/chunk-23SRIVG4.js +0 -50
  27. package/dist/chunk-23SRIVG4.js.map +0 -1
  28. package/dist/chunk-2KT6TROD.js +0 -129
  29. package/dist/chunk-2KT6TROD.js.map +0 -1
  30. package/dist/chunk-2R5XM3ES.js +0 -154
  31. package/dist/chunk-2R5XM3ES.js.map +0 -1
  32. package/dist/chunk-3EWTPOF7.js +0 -51
  33. package/dist/chunk-3EWTPOF7.js.map +0 -1
  34. package/dist/chunk-566W6INH.js +0 -83
  35. package/dist/chunk-566W6INH.js.map +0 -1
  36. package/dist/chunk-5WGVYX3C.js +0 -55
  37. package/dist/chunk-5WGVYX3C.js.map +0 -1
  38. package/dist/chunk-7GXEMMEV.js +0 -44
  39. package/dist/chunk-7GXEMMEV.js.map +0 -1
  40. package/dist/chunk-7U6IZIJP.js +0 -186
  41. package/dist/chunk-7U6IZIJP.js.map +0 -1
  42. package/dist/chunk-7YIKTRSM.js +0 -105
  43. package/dist/chunk-7YIKTRSM.js.map +0 -1
  44. package/dist/chunk-7ZCQF6QM.js +0 -27
  45. package/dist/chunk-7ZCQF6QM.js.map +0 -1
  46. package/dist/chunk-AFKX424Q.js +0 -92
  47. package/dist/chunk-AFKX424Q.js.map +0 -1
  48. package/dist/chunk-BYCJQPMN.js +0 -543
  49. package/dist/chunk-BYCJQPMN.js.map +0 -1
  50. package/dist/chunk-CDAUYTVP.js +0 -41
  51. package/dist/chunk-CDAUYTVP.js.map +0 -1
  52. package/dist/chunk-EWVXSTQK.js +0 -6544
  53. package/dist/chunk-EWVXSTQK.js.map +0 -1
  54. package/dist/chunk-FNRSWA2K.js +0 -1
  55. package/dist/chunk-FNRSWA2K.js.map +0 -1
  56. package/dist/chunk-FPKQYCQS.js +0 -776
  57. package/dist/chunk-FPKQYCQS.js.map +0 -1
  58. package/dist/chunk-IZ5UEZF7.js +0 -138
  59. package/dist/chunk-IZ5UEZF7.js.map +0 -1
  60. package/dist/chunk-K6UY5M75.js +0 -653
  61. package/dist/chunk-K6UY5M75.js.map +0 -1
  62. package/dist/chunk-KGAQW6F4.js +0 -106
  63. package/dist/chunk-KGAQW6F4.js.map +0 -1
  64. package/dist/chunk-LGFWH3AE.js +0 -26
  65. package/dist/chunk-LGFWH3AE.js.map +0 -1
  66. package/dist/chunk-LRV56K2M.js +0 -4106
  67. package/dist/chunk-LRV56K2M.js.map +0 -1
  68. package/dist/chunk-MDJHCCFS.js +0 -485
  69. package/dist/chunk-MDJHCCFS.js.map +0 -1
  70. package/dist/chunk-MLF4W5R6.js +0 -101
  71. package/dist/chunk-MLF4W5R6.js.map +0 -1
  72. package/dist/chunk-NHD5XDD2.js +0 -686
  73. package/dist/chunk-NHD5XDD2.js.map +0 -1
  74. package/dist/chunk-NJX75BLK.js +0 -259
  75. package/dist/chunk-NJX75BLK.js.map +0 -1
  76. package/dist/chunk-NOEAJNTK.js +0 -156
  77. package/dist/chunk-NOEAJNTK.js.map +0 -1
  78. package/dist/chunk-ON7HB5O7.js +0 -58
  79. package/dist/chunk-ON7HB5O7.js.map +0 -1
  80. package/dist/chunk-OSBZXY2W.js +0 -126
  81. package/dist/chunk-OSBZXY2W.js.map +0 -1
  82. package/dist/chunk-OYSAN7UX.js +0 -15
  83. package/dist/chunk-OYSAN7UX.js.map +0 -1
  84. package/dist/chunk-P3HHJANC.js +0 -209
  85. package/dist/chunk-P3HHJANC.js.map +0 -1
  86. package/dist/chunk-R2YLDQLI.js +0 -1115
  87. package/dist/chunk-R2YLDQLI.js.map +0 -1
  88. package/dist/chunk-R6KZYF7D.js +0 -231
  89. package/dist/chunk-R6KZYF7D.js.map +0 -1
  90. package/dist/chunk-S64CB6J3.js +0 -98
  91. package/dist/chunk-S64CB6J3.js.map +0 -1
  92. package/dist/chunk-SSLVNCEA.js +0 -236
  93. package/dist/chunk-SSLVNCEA.js.map +0 -1
  94. package/dist/chunk-TGP34LQN.js +0 -681
  95. package/dist/chunk-TGP34LQN.js.map +0 -1
  96. package/dist/chunk-VUSCVRJL.js +0 -229
  97. package/dist/chunk-VUSCVRJL.js.map +0 -1
  98. package/dist/chunk-W26AUH5B.js +0 -61
  99. package/dist/chunk-W26AUH5B.js.map +0 -1
  100. package/dist/chunk-WQCJTU2C.js +0 -84
  101. package/dist/chunk-WQCJTU2C.js.map +0 -1
  102. package/dist/chunk-XRJUS6FE.js +0 -53
  103. package/dist/chunk-XRJUS6FE.js.map +0 -1
  104. package/dist/chunk-YZCKSNRN.js +0 -453
  105. package/dist/chunk-YZCKSNRN.js.map +0 -1
  106. package/dist/chunk-ZIRH6QWW.js +0 -69
  107. package/dist/chunk-ZIRH6QWW.js.map +0 -1
  108. package/dist/chunk-ZSLHHQPQ.js +0 -282
  109. package/dist/chunk-ZSLHHQPQ.js.map +0 -1
  110. package/dist/config-X4UP7H6R.js +0 -13
  111. package/dist/config-X4UP7H6R.js.map +0 -1
  112. package/dist/config-editor-7BENRVG5.js +0 -11
  113. package/dist/config-editor-7BENRVG5.js.map +0 -1
  114. package/dist/config-registry-M3FFWEVM.js +0 -18
  115. package/dist/config-registry-M3FFWEVM.js.map +0 -1
  116. package/dist/context-FVGCU5TI.js +0 -9
  117. package/dist/context-FVGCU5TI.js.map +0 -1
  118. package/dist/core-plugins-JSY2I44L.js +0 -25
  119. package/dist/core-plugins-JSY2I44L.js.map +0 -1
  120. package/dist/daemon-UOSRDEXW.js +0 -34
  121. package/dist/daemon-UOSRDEXW.js.map +0 -1
  122. package/dist/dev-loader-7P3HZCIA.js +0 -37
  123. package/dist/dev-loader-7P3HZCIA.js.map +0 -1
  124. package/dist/doctor-6DLACBR4.js +0 -10
  125. package/dist/doctor-6DLACBR4.js.map +0 -1
  126. package/dist/file-service-FQQYME7M.js +0 -8
  127. package/dist/file-service-FQQYME7M.js.map +0 -1
  128. package/dist/install-cloudflared-LNS5L5FR.js +0 -33
  129. package/dist/install-cloudflared-LNS5L5FR.js.map +0 -1
  130. package/dist/install-context-KZO5FR4D.js +0 -78
  131. package/dist/install-context-KZO5FR4D.js.map +0 -1
  132. package/dist/install-jq-SN4IA5K4.js +0 -31
  133. package/dist/install-jq-SN4IA5K4.js.map +0 -1
  134. package/dist/instance-context-FLCE7VZ4.js +0 -13
  135. package/dist/instance-context-FLCE7VZ4.js.map +0 -1
  136. package/dist/instance-registry-SW5FWKHO.js +0 -7
  137. package/dist/instance-registry-SW5FWKHO.js.map +0 -1
  138. package/dist/integrate-JIEZYDOR.js +0 -371
  139. package/dist/integrate-JIEZYDOR.js.map +0 -1
  140. package/dist/log-YZ243M5G.js +0 -25
  141. package/dist/log-YZ243M5G.js.map +0 -1
  142. package/dist/main-D7M2AKRM.js +0 -697
  143. package/dist/main-D7M2AKRM.js.map +0 -1
  144. package/dist/menu-ALFN37IR.js +0 -15
  145. package/dist/menu-ALFN37IR.js.map +0 -1
  146. package/dist/notifications-MO23S7S3.js +0 -8
  147. package/dist/notifications-MO23S7S3.js.map +0 -1
  148. package/dist/plugin-create-HFKS23JY.js +0 -968
  149. package/dist/plugin-create-HFKS23JY.js.map +0 -1
  150. package/dist/plugin-installer-VSTYZSXC.js +0 -9
  151. package/dist/plugin-installer-VSTYZSXC.js.map +0 -1
  152. package/dist/plugin-registry-6J3YSFHF.js +0 -7
  153. package/dist/plugin-registry-6J3YSFHF.js.map +0 -1
  154. package/dist/plugin-search-MGKAL5JM.js +0 -39
  155. package/dist/plugin-search-MGKAL5JM.js.map +0 -1
  156. package/dist/post-upgrade-F4YPMTUT.js +0 -79
  157. package/dist/post-upgrade-F4YPMTUT.js.map +0 -1
  158. package/dist/read-text-file-DJBTITIB.js +0 -7
  159. package/dist/read-text-file-DJBTITIB.js.map +0 -1
  160. package/dist/registry-client-GTBWLXYU.js +0 -7
  161. package/dist/registry-client-GTBWLXYU.js.map +0 -1
  162. package/dist/security-O4XGN2CM.js +0 -8
  163. package/dist/security-O4XGN2CM.js.map +0 -1
  164. package/dist/settings-manager-B4UN2LAC.js +0 -7
  165. package/dist/settings-manager-B4UN2LAC.js.map +0 -1
  166. package/dist/setup-44WLBIOT.js +0 -989
  167. package/dist/setup-44WLBIOT.js.map +0 -1
  168. package/dist/speech-GHTSWDAN.js +0 -9
  169. package/dist/speech-GHTSWDAN.js.map +0 -1
  170. package/dist/suggest-RST5VOHB.js +0 -36
  171. package/dist/suggest-RST5VOHB.js.map +0 -1
  172. package/dist/telegram-D7ASLVEB.js +0 -7
  173. package/dist/telegram-D7ASLVEB.js.map +0 -1
  174. package/dist/tunnel-ALJDPFDQ.js +0 -10
  175. package/dist/tunnel-ALJDPFDQ.js.map +0 -1
  176. package/dist/tunnel-service-TBAHDXMF.js +0 -755
  177. package/dist/tunnel-service-TBAHDXMF.js.map +0 -1
  178. package/dist/validators-GITLOFXC.js +0 -11
  179. package/dist/validators-GITLOFXC.js.map +0 -1
  180. package/dist/version-AXXV6IV2.js +0 -15
  181. package/dist/version-AXXV6IV2.js.map +0 -1
@@ -1,686 +0,0 @@
1
- import {
2
- createChildLogger
3
- } from "./chunk-R6KZYF7D.js";
4
- import {
5
- commandExists
6
- } from "./chunk-ZSLHHQPQ.js";
7
-
8
- // src/plugins/tunnel/tunnel-registry.ts
9
- import fs2 from "fs";
10
- import path2 from "path";
11
- import os2 from "os";
12
-
13
- // src/plugins/tunnel/providers/cloudflare.ts
14
- import { spawn } from "child_process";
15
- import fs from "fs";
16
- import path from "path";
17
- import os from "os";
18
- var log = createChildLogger({ module: "cloudflare-tunnel" });
19
- var SIGKILL_TIMEOUT_MS = 5e3;
20
- var CloudflareTunnelProvider = class {
21
- child = null;
22
- publicUrl = "";
23
- options;
24
- binDir;
25
- exitCallback = null;
26
- constructor(options = {}, binDir) {
27
- this.options = options;
28
- this.binDir = binDir ?? path.join(os.homedir(), ".openacp", "bin");
29
- }
30
- onExit(callback) {
31
- this.exitCallback = callback;
32
- }
33
- async start(localPort) {
34
- let binaryPath = this.findBinary();
35
- if (!binaryPath) {
36
- log.warn("cloudflared not found locally, attempting auto-install as fallback...");
37
- try {
38
- const { ensureCloudflared } = await import("./install-cloudflared-LNS5L5FR.js");
39
- binaryPath = await ensureCloudflared();
40
- } catch (err) {
41
- throw new Error(`cloudflared is not installed and auto-install failed: ${err.message}`);
42
- }
43
- }
44
- const args = ["tunnel", "--url", `http://localhost:${localPort}`];
45
- if (this.options.domain) {
46
- args.push("--hostname", String(this.options.domain));
47
- }
48
- return new Promise((resolve, reject) => {
49
- let settled = false;
50
- const settle = (fn) => {
51
- if (!settled) {
52
- settled = true;
53
- fn();
54
- }
55
- };
56
- const timeout = setTimeout(() => {
57
- this.stop();
58
- settle(() => reject(new Error("Cloudflare tunnel timed out after 30s")));
59
- }, 3e4);
60
- try {
61
- this.child = spawn(binaryPath, args, { stdio: ["ignore", "pipe", "pipe"] });
62
- } catch {
63
- clearTimeout(timeout);
64
- settle(() => reject(new Error(`Failed to start cloudflared at ${binaryPath}`)));
65
- return;
66
- }
67
- const urlPattern = /https:\/\/[a-zA-Z0-9-]+\.trycloudflare\.com/;
68
- const onData = (data) => {
69
- const line = data.toString();
70
- log.debug(line.trim());
71
- const match = line.match(urlPattern);
72
- if (match) {
73
- clearTimeout(timeout);
74
- this.publicUrl = match[0];
75
- log.info({ url: this.publicUrl }, "Cloudflare tunnel ready");
76
- settle(() => resolve(this.publicUrl));
77
- }
78
- };
79
- this.child.stdout?.on("data", onData);
80
- this.child.stderr?.on("data", onData);
81
- this.child.on("error", (err) => {
82
- clearTimeout(timeout);
83
- settle(() => reject(new Error(`cloudflared failed to start: ${err.message}`)));
84
- });
85
- this.child.on("exit", (code) => {
86
- if (!this.publicUrl) {
87
- clearTimeout(timeout);
88
- settle(() => reject(new Error(`cloudflared exited with code ${code} before establishing tunnel`)));
89
- } else {
90
- log.error({ code }, "cloudflared exited unexpectedly after establishment");
91
- this.child = null;
92
- this.exitCallback?.(code);
93
- }
94
- });
95
- });
96
- }
97
- async stop() {
98
- const child = this.child;
99
- if (!child) return;
100
- this.child = null;
101
- child.kill("SIGTERM");
102
- const exited = await Promise.race([
103
- new Promise((resolve) => child.on("exit", () => resolve(true))),
104
- new Promise((resolve) => setTimeout(() => resolve(false), SIGKILL_TIMEOUT_MS))
105
- ]);
106
- if (!exited) {
107
- log.warn("cloudflared did not exit after SIGTERM, sending SIGKILL");
108
- child.kill("SIGKILL");
109
- }
110
- log.info("Cloudflare tunnel stopped");
111
- }
112
- getPublicUrl() {
113
- return this.publicUrl;
114
- }
115
- findBinary() {
116
- if (commandExists("cloudflared")) return "cloudflared";
117
- const binPath = path.join(this.binDir, "cloudflared");
118
- if (fs.existsSync(binPath)) return binPath;
119
- return null;
120
- }
121
- };
122
-
123
- // src/plugins/tunnel/providers/ngrok.ts
124
- import { spawn as spawn2 } from "child_process";
125
- var log2 = createChildLogger({ module: "ngrok-tunnel" });
126
- var SIGKILL_TIMEOUT_MS2 = 5e3;
127
- var NgrokTunnelProvider = class {
128
- child = null;
129
- publicUrl = "";
130
- options;
131
- exitCallback = null;
132
- constructor(options = {}) {
133
- this.options = options;
134
- }
135
- onExit(callback) {
136
- this.exitCallback = callback;
137
- }
138
- async start(localPort) {
139
- const args = ["http", String(localPort), "--log", "stdout", "--log-format", "json"];
140
- if (this.options.authtoken) {
141
- args.push("--authtoken", String(this.options.authtoken));
142
- }
143
- if (this.options.domain) {
144
- args.push("--domain", String(this.options.domain));
145
- }
146
- if (this.options.region) {
147
- args.push("--region", String(this.options.region));
148
- }
149
- return new Promise((resolve, reject) => {
150
- let settled = false;
151
- const settle = (fn) => {
152
- if (!settled) {
153
- settled = true;
154
- fn();
155
- }
156
- };
157
- const timeout = setTimeout(() => {
158
- this.stop();
159
- settle(() => reject(new Error("ngrok tunnel timed out after 30s. Is ngrok installed?")));
160
- }, 3e4);
161
- try {
162
- this.child = spawn2("ngrok", args, { stdio: ["ignore", "pipe", "pipe"] });
163
- } catch {
164
- clearTimeout(timeout);
165
- settle(() => reject(new Error(
166
- "Failed to start ngrok. Install it from https://ngrok.com/download"
167
- )));
168
- return;
169
- }
170
- const urlPattern = /https:\/\/[a-zA-Z0-9-]+\.(?:ngrok(?:-free)?\.app|ngrok\.io)/;
171
- const onData = (data) => {
172
- const line = data.toString();
173
- log2.debug(line.trim());
174
- const match = line.match(urlPattern);
175
- if (match) {
176
- clearTimeout(timeout);
177
- this.publicUrl = match[0];
178
- log2.info({ url: this.publicUrl }, "ngrok tunnel ready");
179
- settle(() => resolve(this.publicUrl));
180
- }
181
- };
182
- this.child.stdout?.on("data", onData);
183
- this.child.stderr?.on("data", onData);
184
- this.child.on("error", (err) => {
185
- clearTimeout(timeout);
186
- settle(() => reject(new Error(
187
- `ngrok failed to start: ${err.message}. Install it from https://ngrok.com/download`
188
- )));
189
- });
190
- this.child.on("exit", (code) => {
191
- if (!this.publicUrl) {
192
- clearTimeout(timeout);
193
- settle(() => reject(new Error(`ngrok exited with code ${code} before establishing tunnel`)));
194
- } else {
195
- log2.error({ code }, "ngrok exited unexpectedly after establishment");
196
- this.child = null;
197
- this.exitCallback?.(code);
198
- }
199
- });
200
- });
201
- }
202
- async stop() {
203
- const child = this.child;
204
- if (!child) return;
205
- this.child = null;
206
- child.kill("SIGTERM");
207
- const exited = await Promise.race([
208
- new Promise((resolve) => child.on("exit", () => resolve(true))),
209
- new Promise((resolve) => setTimeout(() => resolve(false), SIGKILL_TIMEOUT_MS2))
210
- ]);
211
- if (!exited) {
212
- log2.warn("ngrok did not exit after SIGTERM, sending SIGKILL");
213
- child.kill("SIGKILL");
214
- }
215
- log2.info("ngrok tunnel stopped");
216
- }
217
- getPublicUrl() {
218
- return this.publicUrl;
219
- }
220
- };
221
-
222
- // src/plugins/tunnel/providers/bore.ts
223
- import { spawn as spawn3 } from "child_process";
224
- var log3 = createChildLogger({ module: "bore-tunnel" });
225
- var SIGKILL_TIMEOUT_MS3 = 5e3;
226
- var BoreTunnelProvider = class {
227
- child = null;
228
- publicUrl = "";
229
- options;
230
- exitCallback = null;
231
- constructor(options = {}) {
232
- this.options = options;
233
- }
234
- onExit(callback) {
235
- this.exitCallback = callback;
236
- }
237
- async start(localPort) {
238
- const server = String(this.options.server || "bore.pub");
239
- const args = ["local", String(localPort), "--to", server];
240
- if (this.options.port) {
241
- args.push("--port", String(this.options.port));
242
- }
243
- if (this.options.secret) {
244
- args.push("--secret", String(this.options.secret));
245
- }
246
- return new Promise((resolve, reject) => {
247
- let settled = false;
248
- const settle = (fn) => {
249
- if (!settled) {
250
- settled = true;
251
- fn();
252
- }
253
- };
254
- const timeout = setTimeout(() => {
255
- this.stop();
256
- settle(() => reject(new Error("Bore tunnel timed out after 30s. Is bore installed?")));
257
- }, 3e4);
258
- try {
259
- this.child = spawn3("bore", args, { stdio: ["ignore", "pipe", "pipe"] });
260
- } catch {
261
- clearTimeout(timeout);
262
- settle(() => reject(new Error(
263
- "Failed to start bore. Install it from https://github.com/ekzhang/bore"
264
- )));
265
- return;
266
- }
267
- const urlPattern = /listening at ([^\s]+):(\d+)/;
268
- const onData = (data) => {
269
- const line = data.toString();
270
- log3.debug(line.trim());
271
- const match = line.match(urlPattern);
272
- if (match) {
273
- clearTimeout(timeout);
274
- this.publicUrl = `http://${match[1]}:${match[2]}`;
275
- log3.info({ url: this.publicUrl }, "Bore tunnel ready");
276
- settle(() => resolve(this.publicUrl));
277
- }
278
- };
279
- this.child.stdout?.on("data", onData);
280
- this.child.stderr?.on("data", onData);
281
- this.child.on("error", (err) => {
282
- clearTimeout(timeout);
283
- settle(() => reject(new Error(
284
- `bore failed to start: ${err.message}. Install it from https://github.com/ekzhang/bore`
285
- )));
286
- });
287
- this.child.on("exit", (code) => {
288
- if (!this.publicUrl) {
289
- clearTimeout(timeout);
290
- settle(() => reject(new Error(`bore exited with code ${code} before establishing tunnel`)));
291
- } else {
292
- log3.error({ code }, "bore exited unexpectedly after establishment");
293
- this.child = null;
294
- this.exitCallback?.(code);
295
- }
296
- });
297
- });
298
- }
299
- async stop() {
300
- const child = this.child;
301
- if (!child) return;
302
- this.child = null;
303
- child.kill("SIGTERM");
304
- const exited = await Promise.race([
305
- new Promise((resolve) => child.on("exit", () => resolve(true))),
306
- new Promise((resolve) => setTimeout(() => resolve(false), SIGKILL_TIMEOUT_MS3))
307
- ]);
308
- if (!exited) {
309
- log3.warn("bore did not exit after SIGTERM, sending SIGKILL");
310
- child.kill("SIGKILL");
311
- }
312
- log3.info("Bore tunnel stopped");
313
- }
314
- getPublicUrl() {
315
- return this.publicUrl;
316
- }
317
- };
318
-
319
- // src/plugins/tunnel/providers/tailscale.ts
320
- import { spawn as spawn4, execSync } from "child_process";
321
- var log4 = createChildLogger({ module: "tailscale-tunnel" });
322
- var SIGKILL_TIMEOUT_MS4 = 5e3;
323
- var TailscaleTunnelProvider = class {
324
- child = null;
325
- publicUrl = "";
326
- options;
327
- exitCallback = null;
328
- constructor(options = {}) {
329
- this.options = options;
330
- }
331
- onExit(callback) {
332
- this.exitCallback = callback;
333
- }
334
- async start(localPort) {
335
- let hostname = "";
336
- try {
337
- const statusJson = execSync("tailscale status --json", { encoding: "utf-8", timeout: 1e4 });
338
- const status = JSON.parse(statusJson);
339
- hostname = String(status.Self.DNSName).replace(/\.$/, "");
340
- log4.debug({ hostname }, "Resolved Tailscale hostname");
341
- } catch (err) {
342
- log4.warn("Failed to resolve Tailscale hostname via status --json");
343
- }
344
- const args = ["funnel", String(localPort)];
345
- if (this.options.bg) {
346
- args.push("--bg");
347
- }
348
- return new Promise((resolve, reject) => {
349
- let settled = false;
350
- const settle = (fn) => {
351
- if (!settled) {
352
- settled = true;
353
- fn();
354
- }
355
- };
356
- const timeout = setTimeout(() => {
357
- this.stop();
358
- settle(() => reject(new Error("Tailscale funnel timed out after 30s. Is tailscale installed?")));
359
- }, 3e4);
360
- try {
361
- this.child = spawn4("tailscale", args, { stdio: ["ignore", "pipe", "pipe"] });
362
- } catch {
363
- clearTimeout(timeout);
364
- settle(() => reject(new Error(
365
- "Failed to start tailscale. Install it from https://tailscale.com/download"
366
- )));
367
- return;
368
- }
369
- const urlPattern = /https:\/\/[a-zA-Z0-9-]+\.[a-zA-Z0-9-]+\.ts\.net/;
370
- const onData = (data) => {
371
- const line = data.toString();
372
- log4.debug(line.trim());
373
- const match = line.match(urlPattern);
374
- if (match) {
375
- clearTimeout(timeout);
376
- this.publicUrl = match[0];
377
- log4.info({ url: this.publicUrl }, "Tailscale funnel ready");
378
- settle(() => resolve(this.publicUrl));
379
- }
380
- };
381
- this.child.stdout?.on("data", onData);
382
- this.child.stderr?.on("data", onData);
383
- this.child.on("error", (err) => {
384
- clearTimeout(timeout);
385
- settle(() => reject(new Error(
386
- `tailscale failed to start: ${err.message}. Install it from https://tailscale.com/download`
387
- )));
388
- });
389
- this.child.on("exit", (code) => {
390
- if (!this.publicUrl) {
391
- clearTimeout(timeout);
392
- if (hostname) {
393
- this.publicUrl = `https://${hostname}:${localPort}`;
394
- this.child = null;
395
- log4.info({ url: this.publicUrl }, "Tailscale funnel ready (constructed from hostname)");
396
- settle(() => resolve(this.publicUrl));
397
- } else {
398
- settle(() => reject(new Error(`tailscale exited with code ${code} before establishing funnel`)));
399
- }
400
- } else {
401
- log4.error({ code }, "tailscale exited unexpectedly after establishment");
402
- this.child = null;
403
- this.exitCallback?.(code);
404
- }
405
- });
406
- });
407
- }
408
- async stop() {
409
- const child = this.child;
410
- if (!child) return;
411
- this.child = null;
412
- child.kill("SIGTERM");
413
- const exited = await Promise.race([
414
- new Promise((resolve) => child.on("exit", () => resolve(true))),
415
- new Promise((resolve) => setTimeout(() => resolve(false), SIGKILL_TIMEOUT_MS4))
416
- ]);
417
- if (!exited) {
418
- log4.warn("tailscale did not exit after SIGTERM, sending SIGKILL");
419
- child.kill("SIGKILL");
420
- }
421
- log4.info("Tailscale funnel stopped");
422
- }
423
- getPublicUrl() {
424
- return this.publicUrl;
425
- }
426
- };
427
-
428
- // src/plugins/tunnel/tunnel-registry.ts
429
- var log5 = createChildLogger({ module: "tunnel-registry" });
430
- var MAX_RETRIES = 5;
431
- var BASE_RETRY_DELAY_MS = 2e3;
432
- var TunnelRegistry = class {
433
- entries = /* @__PURE__ */ new Map();
434
- saveTimeout = null;
435
- maxUserTunnels;
436
- providerOptions;
437
- registryPath;
438
- shuttingDown = false;
439
- constructor(opts = {}) {
440
- this.maxUserTunnels = opts.maxUserTunnels ?? 5;
441
- this.providerOptions = opts.providerOptions ?? {};
442
- this.registryPath = opts.registryPath ?? path2.join(os2.homedir(), ".openacp", "tunnels.json");
443
- }
444
- async add(port, opts) {
445
- if (this.entries.has(port)) {
446
- const existing = this.entries.get(port);
447
- if (existing.entry.status === "active" || existing.entry.status === "starting") {
448
- throw new Error(`Port ${port} is already tunneled \u2192 ${existing.entry.publicUrl || "starting..."}`);
449
- }
450
- if (existing.retryTimer) clearTimeout(existing.retryTimer);
451
- this.entries.delete(port);
452
- }
453
- if (opts.type === "user") {
454
- const userCount = this.list(false).filter((e) => e.status === "active" || e.status === "starting").length;
455
- if (userCount >= this.maxUserTunnels) {
456
- throw new Error(`Max user tunnels (${this.maxUserTunnels}) reached. Stop a tunnel first.`);
457
- }
458
- }
459
- const entry = {
460
- port,
461
- type: opts.type,
462
- provider: opts.provider,
463
- label: opts.label,
464
- sessionId: opts.sessionId,
465
- status: "starting",
466
- retryCount: 0,
467
- createdAt: (/* @__PURE__ */ new Date()).toISOString()
468
- };
469
- const provider = this.createProvider(opts.provider);
470
- provider.onExit((code) => {
471
- if (this.shuttingDown) return;
472
- const live = this.entries.get(port);
473
- if (!live) return;
474
- live.entry.status = "failed";
475
- live.process = null;
476
- this.scheduleSave();
477
- if (live.entry.retryCount < MAX_RETRIES) {
478
- const delay = BASE_RETRY_DELAY_MS * Math.pow(2, live.entry.retryCount);
479
- log5.warn(
480
- { port, code, retry: live.entry.retryCount + 1, maxRetries: MAX_RETRIES, delayMs: delay },
481
- "Tunnel crashed, scheduling retry"
482
- );
483
- live.retryTimer = setTimeout(() => this.retry(port, opts), delay);
484
- } else {
485
- log5.error({ port, code }, `Tunnel crashed and exhausted all ${MAX_RETRIES} retries`);
486
- }
487
- });
488
- const spawnPromise = provider.start(port).then((url) => {
489
- entry.publicUrl = url;
490
- entry.status = "active";
491
- log5.info({ port, url, label: opts.label }, "Tunnel active");
492
- this.scheduleSave();
493
- return url;
494
- }).catch((err) => {
495
- entry.status = "failed";
496
- log5.error({ port, err: err.message }, "Tunnel failed to start");
497
- this.scheduleSave();
498
- throw err;
499
- });
500
- this.entries.set(port, { entry, process: provider, spawnPromise, retryTimer: null });
501
- this.scheduleSave();
502
- await spawnPromise;
503
- return entry;
504
- }
505
- async retry(port, opts) {
506
- if (this.shuttingDown) return;
507
- const live = this.entries.get(port);
508
- if (!live) return;
509
- const retryCount = live.entry.retryCount + 1;
510
- log5.info({ port, retry: retryCount, maxRetries: MAX_RETRIES }, "Retrying tunnel");
511
- if (live.retryTimer) clearTimeout(live.retryTimer);
512
- this.entries.delete(port);
513
- try {
514
- const entry = await this.add(port, opts);
515
- entry.retryCount = retryCount;
516
- } catch (err) {
517
- log5.error({ port, err: err.message, retry: retryCount }, "Tunnel retry failed");
518
- const failedEntry = {
519
- port,
520
- type: opts.type,
521
- provider: opts.provider,
522
- label: opts.label,
523
- sessionId: opts.sessionId,
524
- status: "failed",
525
- retryCount,
526
- createdAt: live.entry.createdAt
527
- };
528
- if (retryCount < MAX_RETRIES) {
529
- const delay = BASE_RETRY_DELAY_MS * Math.pow(2, retryCount);
530
- const retryTimer = setTimeout(() => this.retry(port, opts), delay);
531
- this.entries.set(port, { entry: failedEntry, process: null, spawnPromise: null, retryTimer });
532
- log5.warn({ port, retry: retryCount + 1, delayMs: delay }, "Scheduling next retry");
533
- } else {
534
- this.entries.set(port, { entry: failedEntry, process: null, spawnPromise: null, retryTimer: null });
535
- log5.error({ port }, `Tunnel exhausted all ${MAX_RETRIES} retries`);
536
- }
537
- this.scheduleSave();
538
- }
539
- }
540
- async stop(port) {
541
- const live = this.entries.get(port);
542
- if (!live) return;
543
- if (live.entry.type === "system") {
544
- throw new Error("Cannot stop system tunnel");
545
- }
546
- if (live.retryTimer) clearTimeout(live.retryTimer);
547
- if (live.spawnPromise) {
548
- try {
549
- await live.spawnPromise;
550
- } catch {
551
- }
552
- }
553
- if (live.process) {
554
- await live.process.stop();
555
- }
556
- this.entries.delete(port);
557
- this.scheduleSave();
558
- log5.info({ port, label: live.entry.label }, "Tunnel stopped");
559
- }
560
- async stopBySession(sessionId) {
561
- const stopped = [];
562
- const toStop = this.getBySession(sessionId);
563
- for (const entry of toStop) {
564
- try {
565
- await this.stop(entry.port);
566
- stopped.push(entry);
567
- } catch {
568
- }
569
- }
570
- return stopped;
571
- }
572
- async stopAllUser() {
573
- const userEntries = this.list(false);
574
- for (const entry of userEntries) {
575
- try {
576
- await this.stop(entry.port);
577
- } catch {
578
- }
579
- }
580
- }
581
- async shutdown() {
582
- this.shuttingDown = true;
583
- for (const [, live] of this.entries) {
584
- if (live.retryTimer) clearTimeout(live.retryTimer);
585
- if (live.spawnPromise) {
586
- try {
587
- await live.spawnPromise;
588
- } catch {
589
- }
590
- }
591
- if (live.process) {
592
- await live.process.stop();
593
- }
594
- }
595
- this.entries.clear();
596
- this.scheduleSave();
597
- }
598
- list(includeSystem = false) {
599
- const entries = Array.from(this.entries.values()).map((l) => l.entry);
600
- if (includeSystem) return entries;
601
- return entries.filter((e) => e.type === "user");
602
- }
603
- get(port) {
604
- return this.entries.get(port)?.entry ?? null;
605
- }
606
- getBySession(sessionId) {
607
- return this.list(false).filter((e) => e.sessionId === sessionId);
608
- }
609
- getSystemEntry() {
610
- for (const live of this.entries.values()) {
611
- if (live.entry.type === "system") return live.entry;
612
- }
613
- return null;
614
- }
615
- async restore() {
616
- if (!fs2.existsSync(this.registryPath)) return;
617
- try {
618
- const raw = JSON.parse(fs2.readFileSync(this.registryPath, "utf-8"));
619
- log5.info({ count: raw.length }, "Restoring tunnels");
620
- const userEntries = raw.filter((e) => e.type === "user");
621
- for (const persisted of userEntries) {
622
- try {
623
- await this.add(persisted.port, {
624
- type: persisted.type,
625
- provider: persisted.provider,
626
- label: persisted.label,
627
- sessionId: persisted.sessionId
628
- });
629
- } catch (err) {
630
- log5.warn({ port: persisted.port, err: err.message }, "Failed to restore tunnel");
631
- }
632
- }
633
- } catch (err) {
634
- log5.warn({ err: err.message }, "Failed to read tunnels.json");
635
- }
636
- }
637
- createProvider(name) {
638
- switch (name) {
639
- case "cloudflare":
640
- return new CloudflareTunnelProvider(this.providerOptions);
641
- case "ngrok":
642
- return new NgrokTunnelProvider(this.providerOptions);
643
- case "bore":
644
- return new BoreTunnelProvider(this.providerOptions);
645
- case "tailscale":
646
- return new TailscaleTunnelProvider(this.providerOptions);
647
- default:
648
- log5.warn({ provider: name }, "Unknown provider, falling back to cloudflare");
649
- return new CloudflareTunnelProvider(this.providerOptions);
650
- }
651
- }
652
- scheduleSave() {
653
- if (this.saveTimeout) clearTimeout(this.saveTimeout);
654
- this.saveTimeout = setTimeout(() => this.save(), 2e3);
655
- }
656
- save() {
657
- const data = Array.from(this.entries.values()).map((l) => ({
658
- port: l.entry.port,
659
- type: l.entry.type,
660
- provider: l.entry.provider,
661
- label: l.entry.label,
662
- sessionId: l.entry.sessionId,
663
- createdAt: l.entry.createdAt
664
- }));
665
- try {
666
- const dir = path2.dirname(this.registryPath);
667
- fs2.mkdirSync(dir, { recursive: true });
668
- fs2.writeFileSync(this.registryPath, JSON.stringify(data, null, 2));
669
- } catch (err) {
670
- log5.error({ err: err.message }, "Failed to save tunnels.json");
671
- }
672
- }
673
- flush() {
674
- if (this.saveTimeout) {
675
- clearTimeout(this.saveTimeout);
676
- this.saveTimeout = null;
677
- }
678
- this.save();
679
- }
680
- };
681
-
682
- export {
683
- MAX_RETRIES,
684
- TunnelRegistry
685
- };
686
- //# sourceMappingURL=chunk-NHD5XDD2.js.map