@openacp/cli 2026.330.3 → 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.
Files changed (145) hide show
  1. package/README.md +17 -0
  2. package/dist/adapter-ELG3VRZ3.js +14 -0
  3. package/dist/{agent-catalog-SZQQERV7.js → agent-catalog-UYD26QDK.js} +3 -3
  4. package/dist/{api-client-XTLRRFPX.js → api-client-PEMHYL5U.js} +2 -2
  5. package/dist/{api-server-JLBDKCU4.js → api-server-DATG2KBR.js} +3 -3
  6. package/dist/api-server-L5Z7XACW.js +7 -0
  7. package/dist/chunk-23SRIVG4.js +50 -0
  8. package/dist/chunk-23SRIVG4.js.map +1 -0
  9. package/dist/{chunk-2HEFALTZ.js → chunk-7GXEMMEV.js} +15 -15
  10. package/dist/{chunk-QWVHCTCA.js → chunk-7U6IZIJP.js} +37 -23
  11. package/dist/chunk-7U6IZIJP.js.map +1 -0
  12. package/dist/{chunk-FCTC7KDT.js → chunk-7YIKTRSM.js} +14 -10
  13. package/dist/chunk-7YIKTRSM.js.map +1 -0
  14. package/dist/{chunk-MITTQMGZ.js → chunk-BYCJQPMN.js} +5 -5
  15. package/dist/chunk-BYCJQPMN.js.map +1 -0
  16. package/dist/{chunk-XBZIHNKV.js → chunk-EWVXSTQK.js} +183 -49
  17. package/dist/chunk-EWVXSTQK.js.map +1 -0
  18. package/dist/{chunk-UWH7KIAA.js → chunk-FPKQYCQS.js} +88 -13
  19. package/dist/chunk-FPKQYCQS.js.map +1 -0
  20. package/dist/{chunk-GEOXPGCO.js → chunk-K6UY5M75.js} +12 -9
  21. package/dist/chunk-K6UY5M75.js.map +1 -0
  22. package/dist/{chunk-KDU3ZEWT.js → chunk-KGAQW6F4.js} +12 -3
  23. package/dist/chunk-KGAQW6F4.js.map +1 -0
  24. package/dist/{chunk-UCIZM5SW.js → chunk-LRV56K2M.js} +205 -16
  25. package/dist/chunk-LRV56K2M.js.map +1 -0
  26. package/dist/{chunk-V2YZWYXT.js → chunk-MDJHCCFS.js} +18 -17
  27. package/dist/chunk-MDJHCCFS.js.map +1 -0
  28. package/dist/chunk-NHD5XDD2.js +686 -0
  29. package/dist/chunk-NHD5XDD2.js.map +1 -0
  30. package/dist/{chunk-APS6UEFU.js → chunk-NJX75BLK.js} +1 -1
  31. package/dist/chunk-NJX75BLK.js.map +1 -0
  32. package/dist/{chunk-5HKQCYOI.js → chunk-NOEAJNTK.js} +14 -3
  33. package/dist/chunk-NOEAJNTK.js.map +1 -0
  34. package/dist/chunk-ON7HB5O7.js +58 -0
  35. package/dist/chunk-ON7HB5O7.js.map +1 -0
  36. package/dist/{chunk-5OCGO27U.js → chunk-OSBZXY2W.js} +2 -1
  37. package/dist/chunk-OSBZXY2W.js.map +1 -0
  38. package/dist/{chunk-PA6MNBG4.js → chunk-P3HHJANC.js} +32 -13
  39. package/dist/chunk-P3HHJANC.js.map +1 -0
  40. package/dist/{chunk-BTJHGSLM.js → chunk-R2YLDQLI.js} +9 -10
  41. package/dist/chunk-R2YLDQLI.js.map +1 -0
  42. package/dist/{chunk-237WYH6H.js → chunk-SSLVNCEA.js} +3 -2
  43. package/dist/chunk-SSLVNCEA.js.map +1 -0
  44. package/dist/{chunk-MPGEHTGE.js → chunk-TGP34LQN.js} +9 -7
  45. package/dist/chunk-TGP34LQN.js.map +1 -0
  46. package/dist/{chunk-TMVTSWVH.js → chunk-VUSCVRJL.js} +2 -1
  47. package/dist/chunk-VUSCVRJL.js.map +1 -0
  48. package/dist/chunk-XRJUS6FE.js +53 -0
  49. package/dist/chunk-XRJUS6FE.js.map +1 -0
  50. package/dist/{chunk-W4LK6WJP.js → chunk-YZCKSNRN.js} +24 -17
  51. package/dist/chunk-YZCKSNRN.js.map +1 -0
  52. package/dist/{chunk-3NAFXVQM.js → chunk-ZIRH6QWW.js} +7 -5
  53. package/dist/chunk-ZIRH6QWW.js.map +1 -0
  54. package/dist/cli.d.ts +11 -0
  55. package/dist/cli.js +334 -140
  56. package/dist/cli.js.map +1 -1
  57. package/dist/config-X4UP7H6R.js +13 -0
  58. package/dist/config-editor-7BENRVG5.js +11 -0
  59. package/dist/{config-registry-ZXAIJNYB.js → config-registry-M3FFWEVM.js} +3 -2
  60. package/dist/context-FVGCU5TI.js +9 -0
  61. package/dist/core-plugins-JSY2I44L.js +25 -0
  62. package/dist/{daemon-XFEMMJSZ.js → daemon-UOSRDEXW.js} +8 -3
  63. package/dist/doctor-6DLACBR4.js +10 -0
  64. package/dist/{file-service-HHB3JQIO.js → file-service-FQQYME7M.js} +2 -2
  65. package/dist/index.d.ts +259 -30
  66. package/dist/index.js +44 -33
  67. package/dist/index.js.map +1 -1
  68. package/dist/{install-cloudflared-JRJ4BSOM.js → install-cloudflared-LNS5L5FR.js} +5 -4
  69. package/dist/install-cloudflared-LNS5L5FR.js.map +1 -0
  70. package/dist/{install-context-EHYV5WRY.js → install-context-KZO5FR4D.js} +4 -3
  71. package/dist/install-context-KZO5FR4D.js.map +1 -0
  72. package/dist/{install-jq-ISTGT263.js → install-jq-SN4IA5K4.js} +3 -3
  73. package/dist/instance-context-FLCE7VZ4.js +13 -0
  74. package/dist/instance-registry-SW5FWKHO.js +7 -0
  75. package/dist/{main-VEJCG5PY.js → main-D7M2AKRM.js} +91 -48
  76. package/dist/main-D7M2AKRM.js.map +1 -0
  77. package/dist/{plugin-create-EHL76ZZG.js → plugin-create-HFKS23JY.js} +4 -2
  78. package/dist/{plugin-create-EHL76ZZG.js.map → plugin-create-HFKS23JY.js.map} +1 -1
  79. package/dist/{post-upgrade-Y26S2ZQ7.js → post-upgrade-F4YPMTUT.js} +6 -6
  80. package/dist/{security-2BA265LN.js → security-O4XGN2CM.js} +2 -2
  81. package/dist/{setup-DISPNDEK.js → setup-44WLBIOT.js} +209 -22
  82. package/dist/setup-44WLBIOT.js.map +1 -0
  83. package/dist/{speech-SG62JYIF.js → speech-GHTSWDAN.js} +2 -2
  84. package/dist/telegram-D7ASLVEB.js +7 -0
  85. package/dist/telegram-D7ASLVEB.js.map +1 -0
  86. package/dist/tunnel-ALJDPFDQ.js +10 -0
  87. package/dist/tunnel-ALJDPFDQ.js.map +1 -0
  88. package/dist/{tunnel-service-ZMO4THKE.js → tunnel-service-TBAHDXMF.js} +41 -547
  89. package/dist/tunnel-service-TBAHDXMF.js.map +1 -0
  90. package/package.json +1 -1
  91. package/dist/adapter-AWSI4GML.js +0 -13
  92. package/dist/api-server-5VNYFWJE.js +0 -7
  93. package/dist/chunk-237WYH6H.js.map +0 -1
  94. package/dist/chunk-3NAFXVQM.js.map +0 -1
  95. package/dist/chunk-4WXALZA3.js +0 -45
  96. package/dist/chunk-4WXALZA3.js.map +0 -1
  97. package/dist/chunk-5HKQCYOI.js.map +0 -1
  98. package/dist/chunk-5OCGO27U.js.map +0 -1
  99. package/dist/chunk-APS6UEFU.js.map +0 -1
  100. package/dist/chunk-BTJHGSLM.js.map +0 -1
  101. package/dist/chunk-FCTC7KDT.js.map +0 -1
  102. package/dist/chunk-GEOXPGCO.js.map +0 -1
  103. package/dist/chunk-KDU3ZEWT.js.map +0 -1
  104. package/dist/chunk-MITTQMGZ.js.map +0 -1
  105. package/dist/chunk-MPGEHTGE.js.map +0 -1
  106. package/dist/chunk-PA6MNBG4.js.map +0 -1
  107. package/dist/chunk-QWVHCTCA.js.map +0 -1
  108. package/dist/chunk-TMVTSWVH.js.map +0 -1
  109. package/dist/chunk-UCIZM5SW.js.map +0 -1
  110. package/dist/chunk-UWH7KIAA.js.map +0 -1
  111. package/dist/chunk-V2YZWYXT.js.map +0 -1
  112. package/dist/chunk-W4LK6WJP.js.map +0 -1
  113. package/dist/chunk-XBZIHNKV.js.map +0 -1
  114. package/dist/config-KN6NKKPF.js +0 -20
  115. package/dist/config-editor-76RVZS4B.js +0 -10
  116. package/dist/context-NXXW62NJ.js +0 -9
  117. package/dist/core-plugins-BPZY7SEB.js +0 -22
  118. package/dist/doctor-AV6AUO22.js +0 -9
  119. package/dist/install-cloudflared-JRJ4BSOM.js.map +0 -1
  120. package/dist/install-context-EHYV5WRY.js.map +0 -1
  121. package/dist/main-VEJCG5PY.js.map +0 -1
  122. package/dist/setup-DISPNDEK.js.map +0 -1
  123. package/dist/telegram-L3YM6SQJ.js +0 -7
  124. package/dist/tunnel-HWJ27WDH.js +0 -7
  125. package/dist/tunnel-service-ZMO4THKE.js.map +0 -1
  126. /package/dist/{adapter-AWSI4GML.js.map → adapter-ELG3VRZ3.js.map} +0 -0
  127. /package/dist/{agent-catalog-SZQQERV7.js.map → agent-catalog-UYD26QDK.js.map} +0 -0
  128. /package/dist/{api-client-XTLRRFPX.js.map → api-client-PEMHYL5U.js.map} +0 -0
  129. /package/dist/{api-server-5VNYFWJE.js.map → api-server-DATG2KBR.js.map} +0 -0
  130. /package/dist/{api-server-JLBDKCU4.js.map → api-server-L5Z7XACW.js.map} +0 -0
  131. /package/dist/{chunk-2HEFALTZ.js.map → chunk-7GXEMMEV.js.map} +0 -0
  132. /package/dist/{config-KN6NKKPF.js.map → config-X4UP7H6R.js.map} +0 -0
  133. /package/dist/{config-editor-76RVZS4B.js.map → config-editor-7BENRVG5.js.map} +0 -0
  134. /package/dist/{config-registry-ZXAIJNYB.js.map → config-registry-M3FFWEVM.js.map} +0 -0
  135. /package/dist/{context-NXXW62NJ.js.map → context-FVGCU5TI.js.map} +0 -0
  136. /package/dist/{core-plugins-BPZY7SEB.js.map → core-plugins-JSY2I44L.js.map} +0 -0
  137. /package/dist/{daemon-XFEMMJSZ.js.map → daemon-UOSRDEXW.js.map} +0 -0
  138. /package/dist/{doctor-AV6AUO22.js.map → doctor-6DLACBR4.js.map} +0 -0
  139. /package/dist/{file-service-HHB3JQIO.js.map → file-service-FQQYME7M.js.map} +0 -0
  140. /package/dist/{install-jq-ISTGT263.js.map → install-jq-SN4IA5K4.js.map} +0 -0
  141. /package/dist/{security-2BA265LN.js.map → instance-context-FLCE7VZ4.js.map} +0 -0
  142. /package/dist/{speech-SG62JYIF.js.map → instance-registry-SW5FWKHO.js.map} +0 -0
  143. /package/dist/{post-upgrade-Y26S2ZQ7.js.map → post-upgrade-F4YPMTUT.js.map} +0 -0
  144. /package/dist/{telegram-L3YM6SQJ.js.map → security-O4XGN2CM.js.map} +0 -0
  145. /package/dist/{tunnel-HWJ27WDH.js.map → speech-GHTSWDAN.js.map} +0 -0
@@ -1,531 +1,19 @@
1
1
  import {
2
- commandExists
3
- } from "./chunk-ZSLHHQPQ.js";
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 fs3 from "fs";
526
- import * as path3 from "path";
13
+ import * as fs from "fs";
14
+ import * as path from "path";
527
15
  import { nanoid } from "nanoid";
528
- var log6 = createChildLogger({ module: "viewer-store" });
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
- log6.warn({ filePath, workingDirectory }, "Path outside workspace, rejecting");
64
+ log.warn({ filePath, workingDirectory }, "Path outside workspace, rejecting");
577
65
  return null;
578
66
  }
579
67
  if (content.length > MAX_CONTENT_SIZE) {
580
- log6.debug({ filePath, size: content.length }, "File too large for viewer");
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
- log6.debug({ id, filePath }, "Stored file for viewing");
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
- log6.warn({ filePath, workingDirectory }, "Path outside workspace, rejecting");
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
- log6.debug({ filePath, size: combined }, "Diff content too large for viewer");
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
- log6.debug({ id, filePath }, "Stored diff for viewing");
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
- log6.debug({ label, size: output.length }, "Output too large for viewer");
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
- log6.debug({ id, label }, "Stored output for viewing");
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
- log6.debug({ removed, remaining: this.entries.size }, "Cleaned up expired viewer entries");
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 = fs3.realpathSync(path3.resolve(workingDirectory, filePath));
162
+ resolved = fs.realpathSync(path.resolve(workingDirectory, filePath));
675
163
  } catch {
676
- resolved = path3.resolve(workingDirectory, filePath);
164
+ resolved = path.resolve(workingDirectory, filePath);
677
165
  }
678
166
  try {
679
- workspace = fs3.realpathSync(path3.resolve(workingDirectory));
167
+ workspace = fs.realpathSync(path.resolve(workingDirectory));
680
168
  } catch {
681
- workspace = path3.resolve(workingDirectory);
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 + path3.sep) || rLower === wLower;
174
+ return rLower.startsWith(wLower + path.sep) || rLower === wLower;
687
175
  }
688
- return resolved.startsWith(workspace + path3.sep) || resolved === workspace;
176
+ return resolved.startsWith(workspace + path.sep) || resolved === workspace;
689
177
  }
690
178
  detectLanguage(filePath) {
691
- const ext = path3.extname(filePath).toLowerCase();
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 log7 = createChildLogger({ module: "tunnel" });
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 ok = await new Promise((resolve2) => {
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
- log7.info({ configuredPort: this.config.port, actualPort }, "Configured port in use, using next available");
666
+ log2.info({ configuredPort: this.config.port, actualPort }, "Configured port in use, using next available");
1178
667
  }
1179
- log7.info({ port: actualPort }, "Tunnel HTTP server started");
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
- log7.warn({ port: this.config.port }, "Could not find available port for tunnel HTTP server");
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
- log7.warn({ err: this.startError }, "System tunnel failed, running on localhost");
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
- log7.info("Tunnel service stopped");
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-ZMO4THKE.js.map
755
+ //# sourceMappingURL=tunnel-service-TBAHDXMF.js.map