@spinabot/brigade 1.7.0 → 1.9.0

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 (257) hide show
  1. package/README.md +19 -0
  2. package/dist/agents/agent-loop.d.ts +13 -0
  3. package/dist/agents/agent-loop.d.ts.map +1 -1
  4. package/dist/agents/agent-loop.js +5 -0
  5. package/dist/agents/agent-loop.js.map +1 -1
  6. package/dist/agents/channels/access-control/group-tool-policy.d.ts +73 -0
  7. package/dist/agents/channels/access-control/group-tool-policy.d.ts.map +1 -0
  8. package/dist/agents/channels/access-control/group-tool-policy.js +193 -0
  9. package/dist/agents/channels/access-control/group-tool-policy.js.map +1 -0
  10. package/dist/agents/channels/access-control/index.d.ts +1 -0
  11. package/dist/agents/channels/access-control/index.d.ts.map +1 -1
  12. package/dist/agents/channels/access-control/index.js +1 -0
  13. package/dist/agents/channels/access-control/index.js.map +1 -1
  14. package/dist/agents/channels/bluebubbles/account-config.d.ts +253 -0
  15. package/dist/agents/channels/bluebubbles/account-config.d.ts.map +1 -0
  16. package/dist/agents/channels/bluebubbles/account-config.js +486 -0
  17. package/dist/agents/channels/bluebubbles/account-config.js.map +1 -0
  18. package/dist/agents/channels/bluebubbles/account-registry.d.ts +33 -0
  19. package/dist/agents/channels/bluebubbles/account-registry.d.ts.map +1 -0
  20. package/dist/agents/channels/bluebubbles/account-registry.js +46 -0
  21. package/dist/agents/channels/bluebubbles/account-registry.js.map +1 -0
  22. package/dist/agents/channels/bluebubbles/adapter.d.ts +45 -0
  23. package/dist/agents/channels/bluebubbles/adapter.d.ts.map +1 -0
  24. package/dist/agents/channels/bluebubbles/adapter.js +366 -0
  25. package/dist/agents/channels/bluebubbles/adapter.js.map +1 -0
  26. package/dist/agents/channels/bluebubbles/catchup-cursor.d.ts +52 -0
  27. package/dist/agents/channels/bluebubbles/catchup-cursor.d.ts.map +1 -0
  28. package/dist/agents/channels/bluebubbles/catchup-cursor.js +118 -0
  29. package/dist/agents/channels/bluebubbles/catchup-cursor.js.map +1 -0
  30. package/dist/agents/channels/bluebubbles/catchup.d.ts +114 -0
  31. package/dist/agents/channels/bluebubbles/catchup.d.ts.map +1 -0
  32. package/dist/agents/channels/bluebubbles/catchup.js +220 -0
  33. package/dist/agents/channels/bluebubbles/catchup.js.map +1 -0
  34. package/dist/agents/channels/bluebubbles/chat.d.ts +75 -0
  35. package/dist/agents/channels/bluebubbles/chat.d.ts.map +1 -0
  36. package/dist/agents/channels/bluebubbles/chat.js +182 -0
  37. package/dist/agents/channels/bluebubbles/chat.js.map +1 -0
  38. package/dist/agents/channels/bluebubbles/connection.d.ts +95 -0
  39. package/dist/agents/channels/bluebubbles/connection.d.ts.map +1 -0
  40. package/dist/agents/channels/bluebubbles/connection.js +413 -0
  41. package/dist/agents/channels/bluebubbles/connection.js.map +1 -0
  42. package/dist/agents/channels/bluebubbles/contact-names.d.ts +79 -0
  43. package/dist/agents/channels/bluebubbles/contact-names.d.ts.map +1 -0
  44. package/dist/agents/channels/bluebubbles/contact-names.js +188 -0
  45. package/dist/agents/channels/bluebubbles/contact-names.js.map +1 -0
  46. package/dist/agents/channels/bluebubbles/debounce.d.ts +78 -0
  47. package/dist/agents/channels/bluebubbles/debounce.d.ts.map +1 -0
  48. package/dist/agents/channels/bluebubbles/debounce.js +173 -0
  49. package/dist/agents/channels/bluebubbles/debounce.js.map +1 -0
  50. package/dist/agents/channels/bluebubbles/dedupe.d.ts +25 -0
  51. package/dist/agents/channels/bluebubbles/dedupe.d.ts.map +1 -0
  52. package/dist/agents/channels/bluebubbles/dedupe.js +35 -0
  53. package/dist/agents/channels/bluebubbles/dedupe.js.map +1 -0
  54. package/dist/agents/channels/bluebubbles/effects.d.ts +17 -0
  55. package/dist/agents/channels/bluebubbles/effects.d.ts.map +1 -0
  56. package/dist/agents/channels/bluebubbles/effects.js +57 -0
  57. package/dist/agents/channels/bluebubbles/effects.js.map +1 -0
  58. package/dist/agents/channels/bluebubbles/history.d.ts +56 -0
  59. package/dist/agents/channels/bluebubbles/history.d.ts.map +1 -0
  60. package/dist/agents/channels/bluebubbles/history.js +140 -0
  61. package/dist/agents/channels/bluebubbles/history.js.map +1 -0
  62. package/dist/agents/channels/bluebubbles/index.d.ts +18 -0
  63. package/dist/agents/channels/bluebubbles/index.d.ts.map +1 -0
  64. package/dist/agents/channels/bluebubbles/index.js +18 -0
  65. package/dist/agents/channels/bluebubbles/index.js.map +1 -0
  66. package/dist/agents/channels/bluebubbles/media.d.ts +104 -0
  67. package/dist/agents/channels/bluebubbles/media.d.ts.map +1 -0
  68. package/dist/agents/channels/bluebubbles/media.js +222 -0
  69. package/dist/agents/channels/bluebubbles/media.js.map +1 -0
  70. package/dist/agents/channels/bluebubbles/messaging.d.ts +13 -0
  71. package/dist/agents/channels/bluebubbles/messaging.d.ts.map +1 -0
  72. package/dist/agents/channels/bluebubbles/messaging.js +36 -0
  73. package/dist/agents/channels/bluebubbles/messaging.js.map +1 -0
  74. package/dist/agents/channels/bluebubbles/module.d.ts +24 -0
  75. package/dist/agents/channels/bluebubbles/module.d.ts.map +1 -0
  76. package/dist/agents/channels/bluebubbles/module.js +52 -0
  77. package/dist/agents/channels/bluebubbles/module.js.map +1 -0
  78. package/dist/agents/channels/bluebubbles/normalize.d.ts +111 -0
  79. package/dist/agents/channels/bluebubbles/normalize.d.ts.map +1 -0
  80. package/dist/agents/channels/bluebubbles/normalize.js +239 -0
  81. package/dist/agents/channels/bluebubbles/normalize.js.map +1 -0
  82. package/dist/agents/channels/bluebubbles/plugin.d.ts +46 -0
  83. package/dist/agents/channels/bluebubbles/plugin.d.ts.map +1 -0
  84. package/dist/agents/channels/bluebubbles/plugin.js +242 -0
  85. package/dist/agents/channels/bluebubbles/plugin.js.map +1 -0
  86. package/dist/agents/channels/bluebubbles/probe.d.ts +79 -0
  87. package/dist/agents/channels/bluebubbles/probe.d.ts.map +1 -0
  88. package/dist/agents/channels/bluebubbles/probe.js +138 -0
  89. package/dist/agents/channels/bluebubbles/probe.js.map +1 -0
  90. package/dist/agents/channels/bluebubbles/reactions.d.ts +46 -0
  91. package/dist/agents/channels/bluebubbles/reactions.d.ts.map +1 -0
  92. package/dist/agents/channels/bluebubbles/reactions.js +207 -0
  93. package/dist/agents/channels/bluebubbles/reactions.js.map +1 -0
  94. package/dist/agents/channels/bluebubbles/send.d.ts +132 -0
  95. package/dist/agents/channels/bluebubbles/send.d.ts.map +1 -0
  96. package/dist/agents/channels/bluebubbles/send.js +327 -0
  97. package/dist/agents/channels/bluebubbles/send.js.map +1 -0
  98. package/dist/agents/channels/bluebubbles/status-issues.d.ts +57 -0
  99. package/dist/agents/channels/bluebubbles/status-issues.d.ts.map +1 -0
  100. package/dist/agents/channels/bluebubbles/status-issues.js +93 -0
  101. package/dist/agents/channels/bluebubbles/status-issues.js.map +1 -0
  102. package/dist/agents/channels/bluebubbles/types.d.ts +69 -0
  103. package/dist/agents/channels/bluebubbles/types.d.ts.map +1 -0
  104. package/dist/agents/channels/bluebubbles/types.js +118 -0
  105. package/dist/agents/channels/bluebubbles/types.js.map +1 -0
  106. package/dist/agents/channels/bluebubbles/webhook.d.ts +97 -0
  107. package/dist/agents/channels/bluebubbles/webhook.d.ts.map +1 -0
  108. package/dist/agents/channels/bluebubbles/webhook.js +249 -0
  109. package/dist/agents/channels/bluebubbles/webhook.js.map +1 -0
  110. package/dist/agents/channels/bundled-channel-metas.d.ts +13 -0
  111. package/dist/agents/channels/bundled-channel-metas.d.ts.map +1 -1
  112. package/dist/agents/channels/bundled-channel-metas.js +33 -0
  113. package/dist/agents/channels/bundled-channel-metas.js.map +1 -1
  114. package/dist/agents/channels/imessage/account-config.d.ts +178 -0
  115. package/dist/agents/channels/imessage/account-config.d.ts.map +1 -0
  116. package/dist/agents/channels/imessage/account-config.js +371 -0
  117. package/dist/agents/channels/imessage/account-config.js.map +1 -0
  118. package/dist/agents/channels/imessage/adapter.d.ts +36 -0
  119. package/dist/agents/channels/imessage/adapter.d.ts.map +1 -0
  120. package/dist/agents/channels/imessage/adapter.js +286 -0
  121. package/dist/agents/channels/imessage/adapter.js.map +1 -0
  122. package/dist/agents/channels/imessage/client.d.ts +99 -0
  123. package/dist/agents/channels/imessage/client.d.ts.map +1 -0
  124. package/dist/agents/channels/imessage/client.js +231 -0
  125. package/dist/agents/channels/imessage/client.js.map +1 -0
  126. package/dist/agents/channels/imessage/connection.d.ts +101 -0
  127. package/dist/agents/channels/imessage/connection.d.ts.map +1 -0
  128. package/dist/agents/channels/imessage/connection.js +367 -0
  129. package/dist/agents/channels/imessage/connection.js.map +1 -0
  130. package/dist/agents/channels/imessage/format.d.ts +45 -0
  131. package/dist/agents/channels/imessage/format.d.ts.map +1 -0
  132. package/dist/agents/channels/imessage/format.js +170 -0
  133. package/dist/agents/channels/imessage/format.js.map +1 -0
  134. package/dist/agents/channels/imessage/history.d.ts +49 -0
  135. package/dist/agents/channels/imessage/history.d.ts.map +1 -0
  136. package/dist/agents/channels/imessage/history.js +74 -0
  137. package/dist/agents/channels/imessage/history.js.map +1 -0
  138. package/dist/agents/channels/imessage/index.d.ts +25 -0
  139. package/dist/agents/channels/imessage/index.d.ts.map +1 -0
  140. package/dist/agents/channels/imessage/index.js +25 -0
  141. package/dist/agents/channels/imessage/index.js.map +1 -0
  142. package/dist/agents/channels/imessage/media.d.ts +92 -0
  143. package/dist/agents/channels/imessage/media.d.ts.map +1 -0
  144. package/dist/agents/channels/imessage/media.js +196 -0
  145. package/dist/agents/channels/imessage/media.js.map +1 -0
  146. package/dist/agents/channels/imessage/messaging.d.ts +14 -0
  147. package/dist/agents/channels/imessage/messaging.d.ts.map +1 -0
  148. package/dist/agents/channels/imessage/messaging.js +37 -0
  149. package/dist/agents/channels/imessage/messaging.js.map +1 -0
  150. package/dist/agents/channels/imessage/module.d.ts +16 -0
  151. package/dist/agents/channels/imessage/module.d.ts.map +1 -0
  152. package/dist/agents/channels/imessage/module.js +23 -0
  153. package/dist/agents/channels/imessage/module.js.map +1 -0
  154. package/dist/agents/channels/imessage/monitor.d.ts +207 -0
  155. package/dist/agents/channels/imessage/monitor.d.ts.map +1 -0
  156. package/dist/agents/channels/imessage/monitor.js +504 -0
  157. package/dist/agents/channels/imessage/monitor.js.map +1 -0
  158. package/dist/agents/channels/imessage/plugin.d.ts +53 -0
  159. package/dist/agents/channels/imessage/plugin.d.ts.map +1 -0
  160. package/dist/agents/channels/imessage/plugin.js +215 -0
  161. package/dist/agents/channels/imessage/plugin.js.map +1 -0
  162. package/dist/agents/channels/imessage/probe.d.ts +68 -0
  163. package/dist/agents/channels/imessage/probe.d.ts.map +1 -0
  164. package/dist/agents/channels/imessage/probe.js +134 -0
  165. package/dist/agents/channels/imessage/probe.js.map +1 -0
  166. package/dist/agents/channels/imessage/remote-attachments.d.ts +61 -0
  167. package/dist/agents/channels/imessage/remote-attachments.d.ts.map +1 -0
  168. package/dist/agents/channels/imessage/remote-attachments.js +131 -0
  169. package/dist/agents/channels/imessage/remote-attachments.js.map +1 -0
  170. package/dist/agents/channels/imessage/send.d.ts +61 -0
  171. package/dist/agents/channels/imessage/send.d.ts.map +1 -0
  172. package/dist/agents/channels/imessage/send.js +108 -0
  173. package/dist/agents/channels/imessage/send.js.map +1 -0
  174. package/dist/agents/channels/imessage/targets.d.ts +91 -0
  175. package/dist/agents/channels/imessage/targets.d.ts.map +1 -0
  176. package/dist/agents/channels/imessage/targets.js +245 -0
  177. package/dist/agents/channels/imessage/targets.js.map +1 -0
  178. package/dist/agents/channels/imessage/watch-error.d.ts +23 -0
  179. package/dist/agents/channels/imessage/watch-error.d.ts.map +1 -0
  180. package/dist/agents/channels/imessage/watch-error.js +69 -0
  181. package/dist/agents/channels/imessage/watch-error.js.map +1 -0
  182. package/dist/agents/channels/inbound-pipeline.d.ts +11 -0
  183. package/dist/agents/channels/inbound-pipeline.d.ts.map +1 -1
  184. package/dist/agents/channels/inbound-pipeline.js +20 -1
  185. package/dist/agents/channels/inbound-pipeline.js.map +1 -1
  186. package/dist/agents/channels/manager.d.ts +9 -0
  187. package/dist/agents/channels/manager.d.ts.map +1 -1
  188. package/dist/agents/channels/manager.js.map +1 -1
  189. package/dist/agents/channels/sdk.d.ts +4 -0
  190. package/dist/agents/channels/sdk.d.ts.map +1 -1
  191. package/dist/agents/channels/sdk.js +4 -0
  192. package/dist/agents/channels/sdk.js.map +1 -1
  193. package/dist/agents/extensions/modules/index.d.ts.map +1 -1
  194. package/dist/agents/extensions/modules/index.js +12 -0
  195. package/dist/agents/extensions/modules/index.js.map +1 -1
  196. package/dist/agents/session-wiring.d.ts +31 -0
  197. package/dist/agents/session-wiring.d.ts.map +1 -1
  198. package/dist/agents/session-wiring.js +45 -2
  199. package/dist/agents/session-wiring.js.map +1 -1
  200. package/dist/agents/tools/bluebubbles-action-tool.d.ts +66 -0
  201. package/dist/agents/tools/bluebubbles-action-tool.d.ts.map +1 -0
  202. package/dist/agents/tools/bluebubbles-action-tool.js +234 -0
  203. package/dist/agents/tools/bluebubbles-action-tool.js.map +1 -0
  204. package/dist/agents/tools/registry.d.ts.map +1 -1
  205. package/dist/agents/tools/registry.js +18 -0
  206. package/dist/agents/tools/registry.js.map +1 -1
  207. package/dist/buildstamp.json +1 -1
  208. package/dist/cli/commands/expose.d.ts +40 -0
  209. package/dist/cli/commands/expose.d.ts.map +1 -0
  210. package/dist/cli/commands/expose.js +200 -0
  211. package/dist/cli/commands/expose.js.map +1 -0
  212. package/dist/cli/program/build-program.d.ts.map +1 -1
  213. package/dist/cli/program/build-program.js +61 -0
  214. package/dist/cli/program/build-program.js.map +1 -1
  215. package/dist/config/io.d.ts +41 -0
  216. package/dist/config/io.d.ts.map +1 -1
  217. package/dist/config/io.js.map +1 -1
  218. package/dist/core/server.d.ts.map +1 -1
  219. package/dist/core/server.js +48 -2
  220. package/dist/core/server.js.map +1 -1
  221. package/dist/core/tunnel/auth-proxy.d.ts +55 -0
  222. package/dist/core/tunnel/auth-proxy.d.ts.map +1 -0
  223. package/dist/core/tunnel/auth-proxy.js +179 -0
  224. package/dist/core/tunnel/auth-proxy.js.map +1 -0
  225. package/dist/core/tunnel/manager.d.ts +42 -0
  226. package/dist/core/tunnel/manager.d.ts.map +1 -0
  227. package/dist/core/tunnel/manager.js +102 -0
  228. package/dist/core/tunnel/manager.js.map +1 -0
  229. package/dist/core/tunnel/providers/bore.d.ts +18 -0
  230. package/dist/core/tunnel/providers/bore.d.ts.map +1 -0
  231. package/dist/core/tunnel/providers/bore.js +117 -0
  232. package/dist/core/tunnel/providers/bore.js.map +1 -0
  233. package/dist/core/tunnel/providers/cloudflared.d.ts +24 -0
  234. package/dist/core/tunnel/providers/cloudflared.d.ts.map +1 -0
  235. package/dist/core/tunnel/providers/cloudflared.js +179 -0
  236. package/dist/core/tunnel/providers/cloudflared.js.map +1 -0
  237. package/dist/core/tunnel/providers/custom.d.ts +21 -0
  238. package/dist/core/tunnel/providers/custom.d.ts.map +1 -0
  239. package/dist/core/tunnel/providers/custom.js +124 -0
  240. package/dist/core/tunnel/providers/custom.js.map +1 -0
  241. package/dist/core/tunnel/registry.d.ts +15 -0
  242. package/dist/core/tunnel/registry.d.ts.map +1 -0
  243. package/dist/core/tunnel/registry.js +26 -0
  244. package/dist/core/tunnel/registry.js.map +1 -0
  245. package/dist/core/tunnel/state.d.ts +39 -0
  246. package/dist/core/tunnel/state.d.ts.map +1 -0
  247. package/dist/core/tunnel/state.js +57 -0
  248. package/dist/core/tunnel/state.js.map +1 -0
  249. package/dist/core/tunnel/types.d.ts +61 -0
  250. package/dist/core/tunnel/types.d.ts.map +1 -0
  251. package/dist/core/tunnel/types.js +20 -0
  252. package/dist/core/tunnel/types.js.map +1 -0
  253. package/dist/infra/net/fetch-guard.d.ts +24 -2
  254. package/dist/infra/net/fetch-guard.d.ts.map +1 -1
  255. package/dist/infra/net/fetch-guard.js +78 -31
  256. package/dist/infra/net/fetch-guard.js.map +1 -1
  257. package/package.json +5 -1
@@ -0,0 +1,179 @@
1
+ /**
2
+ * Cloudflare provider — anonymous TryCloudflare quick tunnel.
3
+ *
4
+ * The default `brigade expose` backend. No account, no signup: cloudflared
5
+ * dials Cloudflare's edge and we get a random `https://<x>.trycloudflare.com`
6
+ * URL that terminates TLS at the edge and proxies straight to our local
7
+ * auth-proxy. WebSockets work by default (the gateway is WS-first).
8
+ *
9
+ * The `cloudflared` binary (Apache-2.0) is NOT a hard npm dependency — that
10
+ * would add a ~30 MB download to every Brigade install. Instead we resolve it
11
+ * lazily at expose time:
12
+ * 1. `$BRIGADE_CLOUDFLARED_BIN`
13
+ * 2. `cloudflared` on PATH (system install)
14
+ * 3. a Brigade-managed copy in the OS cache dir
15
+ * 4. download the official release into the cache dir (once)
16
+ *
17
+ * Caveat (documented in the research): TryCloudflare quick tunnels don't
18
+ * support SSE and cap in-flight requests; that's fine for a personal WS
19
+ * gateway. For a stable URL the operator can point a self-host provider at
20
+ * their own relay instead.
21
+ */
22
+ import { spawn, spawnSync } from "node:child_process";
23
+ import * as fs from "node:fs";
24
+ import * as fsAsync from "node:fs/promises";
25
+ import * as path from "node:path";
26
+ import { resolveOsCacheDir } from "../../../config/paths.js";
27
+ const QUICK_URL_RE = /https:\/\/[a-z0-9-]+\.trycloudflare\.com/i;
28
+ const URL_WAIT_MS = 30_000;
29
+ function cloudflaredAssetName() {
30
+ const arch = process.arch === "x64"
31
+ ? "amd64"
32
+ : process.arch === "arm64"
33
+ ? "arm64"
34
+ : process.arch === "ia32"
35
+ ? "386"
36
+ : process.arch === "arm"
37
+ ? "arm"
38
+ : "amd64";
39
+ if (process.platform === "win32")
40
+ return `cloudflared-windows-${arch === "arm64" ? "amd64" : arch}.exe`;
41
+ if (process.platform === "darwin")
42
+ return `cloudflared-darwin-${arch}.tgz`;
43
+ return `cloudflared-linux-${arch}`;
44
+ }
45
+ function managedBinPath() {
46
+ const name = process.platform === "win32" ? "cloudflared.exe" : "cloudflared";
47
+ return path.join(resolveOsCacheDir(), "cloudflared", name);
48
+ }
49
+ /** First binary that exists across env → PATH → managed copy. */
50
+ function findExistingBinary() {
51
+ const envBin = process.env.BRIGADE_CLOUDFLARED_BIN?.trim();
52
+ if (envBin && fs.existsSync(envBin))
53
+ return envBin;
54
+ const probe = process.platform === "win32" ? "where" : "which";
55
+ try {
56
+ const res = spawnSync(probe, ["cloudflared"], { encoding: "utf8" });
57
+ if (res.status === 0) {
58
+ const first = res.stdout.split(/\r?\n/).map((l) => l.trim()).find(Boolean);
59
+ if (first && fs.existsSync(first))
60
+ return first;
61
+ }
62
+ }
63
+ catch {
64
+ // probe tool missing — fall through to managed copy
65
+ }
66
+ const managed = managedBinPath();
67
+ if (fs.existsSync(managed))
68
+ return managed;
69
+ return undefined;
70
+ }
71
+ /** Download the official cloudflared release into the cache dir. */
72
+ async function downloadBinary(onLog) {
73
+ const asset = cloudflaredAssetName();
74
+ const url = `https://github.com/cloudflare/cloudflared/releases/latest/download/${asset}`;
75
+ const dest = managedBinPath();
76
+ await fsAsync.mkdir(path.dirname(dest), { recursive: true });
77
+ onLog?.(`downloading cloudflared (${asset})…`);
78
+ const res = await fetch(url, { redirect: "follow" });
79
+ if (!res.ok || !res.body) {
80
+ throw new Error(`cloudflared download failed (HTTP ${res.status}) from ${url}`);
81
+ }
82
+ const buf = Buffer.from(await res.arrayBuffer());
83
+ if (asset.endsWith(".tgz")) {
84
+ // macOS ships a gzipped tarball containing the `cloudflared` binary.
85
+ const tmpTgz = `${dest}.tgz`;
86
+ await fsAsync.writeFile(tmpTgz, buf);
87
+ const tar = await import("tar");
88
+ await tar.x({ file: tmpTgz, cwd: path.dirname(dest) });
89
+ await fsAsync.rm(tmpTgz, { force: true });
90
+ // The tarball extracts a file literally named `cloudflared`.
91
+ const extracted = path.join(path.dirname(dest), "cloudflared");
92
+ if (extracted !== dest && fs.existsSync(extracted)) {
93
+ await fsAsync.rename(extracted, dest);
94
+ }
95
+ }
96
+ else {
97
+ await fsAsync.writeFile(dest, buf);
98
+ }
99
+ if (process.platform !== "win32")
100
+ await fsAsync.chmod(dest, 0o755);
101
+ onLog?.(`cloudflared ready at ${dest}`);
102
+ return dest;
103
+ }
104
+ async function resolveBinary(onLog) {
105
+ return findExistingBinary() ?? (await downloadBinary(onLog));
106
+ }
107
+ export const cloudflareProvider = {
108
+ name: "cloudflare",
109
+ label: "Cloudflare (anonymous TryCloudflare quick tunnel)",
110
+ async isAvailable() {
111
+ // Always available — the binary is auto-downloaded on first use. We only
112
+ // surface a soft note when there's no pre-existing copy.
113
+ return { ok: true };
114
+ },
115
+ async start(opts) {
116
+ const bin = await resolveBinary(opts.onLog);
117
+ const target = `http://${opts.localHost}:${opts.localPort}`;
118
+ const args = ["tunnel", "--no-autoupdate", "--url", target];
119
+ const child = spawn(bin, args, { stdio: ["ignore", "pipe", "pipe"] });
120
+ const url = await new Promise((resolve, reject) => {
121
+ let settled = false;
122
+ const timer = setTimeout(() => {
123
+ if (settled)
124
+ return;
125
+ settled = true;
126
+ try {
127
+ child.kill();
128
+ }
129
+ catch { /* ignore */ }
130
+ reject(new Error(`cloudflared did not produce a public URL within ${URL_WAIT_MS / 1000}s`));
131
+ }, URL_WAIT_MS);
132
+ const scan = (chunk) => {
133
+ const text = chunk.toString();
134
+ for (const line of text.split(/\r?\n/)) {
135
+ if (line.trim())
136
+ opts.onLog?.(line.trim());
137
+ }
138
+ const m = text.match(QUICK_URL_RE);
139
+ if (m && !settled) {
140
+ settled = true;
141
+ clearTimeout(timer);
142
+ resolve(m[0]);
143
+ }
144
+ };
145
+ // cloudflared logs the URL to stderr; scan both to be safe.
146
+ child.stdout?.on("data", scan);
147
+ child.stderr?.on("data", scan);
148
+ child.on("error", (err) => {
149
+ if (settled)
150
+ return;
151
+ settled = true;
152
+ clearTimeout(timer);
153
+ reject(err);
154
+ });
155
+ child.on("exit", (code) => {
156
+ if (settled)
157
+ return;
158
+ settled = true;
159
+ clearTimeout(timer);
160
+ reject(new Error(`cloudflared exited (code ${code ?? "?"}) before publishing a URL`));
161
+ });
162
+ });
163
+ let stopped = false;
164
+ return {
165
+ provider: "cloudflare",
166
+ url,
167
+ async stop() {
168
+ if (stopped)
169
+ return;
170
+ stopped = true;
171
+ try {
172
+ child.kill();
173
+ }
174
+ catch { /* ignore */ }
175
+ },
176
+ };
177
+ },
178
+ };
179
+ //# sourceMappingURL=cloudflared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloudflared.js","sourceRoot":"","sources":["../../../../src/core/tunnel/providers/cloudflared.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * custom provider — run any tunnel command and scrape its public URL.
3
+ *
4
+ * The "bring your own OSS tunnel" escape hatch: frp, sish, chisel, zrok,
5
+ * localhost.run / ssh -R, pinggy, … anything that prints a public URL to
6
+ * stdout/stderr. The operator supplies a command template via `--command`
7
+ * (or `cfg.gateway.tunnel.command`); `{port}` is replaced with the local
8
+ * auth-proxy port.
9
+ *
10
+ * Examples:
11
+ * --command "ssh -R 80:localhost:{port} nokey@localhost.run"
12
+ * --command "sish-client ... -R {port}"
13
+ * --command "frpc http --local-port {port} --server-addr my.relay --sd brigade"
14
+ *
15
+ * We parse the first `http(s)://…` URL the command emits. If a custom tool
16
+ * uses a different scheme/format, set `--relay` to override the printed URL
17
+ * outright (we then skip URL detection and just keep the process alive).
18
+ */
19
+ import type { TunnelProvider } from "../types.js";
20
+ export declare const customProvider: TunnelProvider;
21
+ //# sourceMappingURL=custom.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom.d.ts","sourceRoot":"","sources":["../../../../src/core/tunnel/providers/custom.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,OAAO,KAAK,EAAoC,cAAc,EAAsB,MAAM,aAAa,CAAC;AAcxG,eAAO,MAAM,cAAc,EAAE,cAmF5B,CAAC"}
@@ -0,0 +1,124 @@
1
+ /**
2
+ * custom provider — run any tunnel command and scrape its public URL.
3
+ *
4
+ * The "bring your own OSS tunnel" escape hatch: frp, sish, chisel, zrok,
5
+ * localhost.run / ssh -R, pinggy, … anything that prints a public URL to
6
+ * stdout/stderr. The operator supplies a command template via `--command`
7
+ * (or `cfg.gateway.tunnel.command`); `{port}` is replaced with the local
8
+ * auth-proxy port.
9
+ *
10
+ * Examples:
11
+ * --command "ssh -R 80:localhost:{port} nokey@localhost.run"
12
+ * --command "sish-client ... -R {port}"
13
+ * --command "frpc http --local-port {port} --server-addr my.relay --sd brigade"
14
+ *
15
+ * We parse the first `http(s)://…` URL the command emits. If a custom tool
16
+ * uses a different scheme/format, set `--relay` to override the printed URL
17
+ * outright (we then skip URL detection and just keep the process alive).
18
+ */
19
+ import { spawn } from "node:child_process";
20
+ const ANY_URL_RE = /\bhttps?:\/\/[^\s'"]+/i;
21
+ const URL_WAIT_MS = 30_000;
22
+ /** Split a command string into argv, honouring simple double-quotes. */
23
+ function tokenize(cmd) {
24
+ const out = [];
25
+ const re = /"([^"]*)"|(\S+)/g;
26
+ let m;
27
+ while ((m = re.exec(cmd)) !== null)
28
+ out.push(m[1] ?? m[2] ?? "");
29
+ return out;
30
+ }
31
+ export const customProvider = {
32
+ name: "custom",
33
+ label: "custom (user-supplied tunnel command)",
34
+ async isAvailable(opts) {
35
+ if (opts?.command && opts.command.trim())
36
+ return { ok: true };
37
+ return {
38
+ ok: false,
39
+ reason: "the custom provider needs a command — pass --command \"…{port}…\" or set cfg.gateway.tunnel.command.",
40
+ };
41
+ },
42
+ async start(opts) {
43
+ const template = opts.command?.trim();
44
+ if (!template)
45
+ throw new Error("custom provider requires --command");
46
+ const rendered = template.replaceAll("{port}", String(opts.localPort));
47
+ const argv = tokenize(rendered);
48
+ if (argv.length === 0)
49
+ throw new Error("custom provider command is empty");
50
+ const child = spawn(argv[0], argv.slice(1), {
51
+ stdio: ["ignore", "pipe", "pipe"],
52
+ shell: false,
53
+ });
54
+ // If the operator pre-declares the public URL via --relay, trust it and
55
+ // skip detection (covers tools that don't print a parseable URL).
56
+ const forced = opts.relay?.trim();
57
+ const url = await new Promise((resolve, reject) => {
58
+ let settled = false;
59
+ const done = (u) => {
60
+ if (settled)
61
+ return;
62
+ settled = true;
63
+ clearTimeout(timer);
64
+ resolve(u);
65
+ };
66
+ const timer = setTimeout(() => {
67
+ if (settled)
68
+ return;
69
+ settled = true;
70
+ try {
71
+ child.kill();
72
+ }
73
+ catch { /* ignore */ }
74
+ reject(new Error(`custom tunnel did not emit a URL within ${URL_WAIT_MS / 1000}s (use --relay to set it explicitly)`));
75
+ }, URL_WAIT_MS);
76
+ const scan = (chunk) => {
77
+ const text = chunk.toString();
78
+ for (const line of text.split(/\r?\n/)) {
79
+ if (line.trim())
80
+ opts.onLog?.(line.trim());
81
+ }
82
+ if (!forced) {
83
+ const m = text.match(ANY_URL_RE);
84
+ if (m)
85
+ done(m[0]);
86
+ }
87
+ };
88
+ child.stdout?.on("data", scan);
89
+ child.stderr?.on("data", scan);
90
+ child.on("error", (err) => {
91
+ if (settled)
92
+ return;
93
+ settled = true;
94
+ clearTimeout(timer);
95
+ reject(err);
96
+ });
97
+ child.on("exit", (code) => {
98
+ if (settled)
99
+ return;
100
+ settled = true;
101
+ clearTimeout(timer);
102
+ reject(new Error(`custom tunnel exited (code ${code ?? "?"}) before emitting a URL`));
103
+ });
104
+ // Forced URL: resolve as soon as the process is spawned (give it a tick).
105
+ if (forced)
106
+ setTimeout(() => done(forced), 250);
107
+ });
108
+ let stopped = false;
109
+ return {
110
+ provider: "custom",
111
+ url,
112
+ async stop() {
113
+ if (stopped)
114
+ return;
115
+ stopped = true;
116
+ try {
117
+ child.kill();
118
+ }
119
+ catch { /* ignore */ }
120
+ },
121
+ };
122
+ },
123
+ };
124
+ //# sourceMappingURL=custom.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom.js","sourceRoot":"","sources":["../../../../src/core/tunnel/providers/custom.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Tunnel provider registry — maps a provider name to its implementation.
3
+ *
4
+ * `cloudflare` is the default (zero-config, anonymous, auto-managed binary).
5
+ * Add a provider by importing it here and listing it in `PROVIDERS`; the CLI's
6
+ * `--provider` flag and `cfg.gateway.tunnel.provider` both resolve through this.
7
+ */
8
+ import type { TunnelProvider } from "./types.js";
9
+ /** The default provider used when none is configured. */
10
+ export declare const DEFAULT_PROVIDER = "cloudflare";
11
+ /** All known provider names, in display order. */
12
+ export declare function listProviderNames(): string[];
13
+ /** Resolve a provider by name. Throws with a helpful message on unknown names. */
14
+ export declare function getProvider(name: string): TunnelProvider;
15
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/core/tunnel/registry.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,yDAAyD;AACzD,eAAO,MAAM,gBAAgB,eAAe,CAAC;AAI7C,kDAAkD;AAClD,wBAAgB,iBAAiB,IAAI,MAAM,EAAE,CAE5C;AAED,kFAAkF;AAClF,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAQxD"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Tunnel provider registry — maps a provider name to its implementation.
3
+ *
4
+ * `cloudflare` is the default (zero-config, anonymous, auto-managed binary).
5
+ * Add a provider by importing it here and listing it in `PROVIDERS`; the CLI's
6
+ * `--provider` flag and `cfg.gateway.tunnel.provider` both resolve through this.
7
+ */
8
+ import { boreProvider } from "./providers/bore.js";
9
+ import { cloudflareProvider } from "./providers/cloudflared.js";
10
+ import { customProvider } from "./providers/custom.js";
11
+ /** The default provider used when none is configured. */
12
+ export const DEFAULT_PROVIDER = "cloudflare";
13
+ const PROVIDERS = [cloudflareProvider, boreProvider, customProvider];
14
+ /** All known provider names, in display order. */
15
+ export function listProviderNames() {
16
+ return PROVIDERS.map((p) => p.name);
17
+ }
18
+ /** Resolve a provider by name. Throws with a helpful message on unknown names. */
19
+ export function getProvider(name) {
20
+ const found = PROVIDERS.find((p) => p.name === name);
21
+ if (!found) {
22
+ throw new Error(`unknown tunnel provider "${name}". Known providers: ${listProviderNames().join(", ")}.`);
23
+ }
24
+ return found;
25
+ }
26
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/core/tunnel/registry.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAGvD,yDAAyD;AACzD,MAAM,CAAC,MAAM,gBAAgB,GAAG,YAAY,CAAC;AAE7C,MAAM,SAAS,GAA8B,CAAC,kBAAkB,EAAE,YAAY,EAAE,cAAc,CAAC,CAAC;AAEhG,kDAAkD;AAClD,MAAM,UAAU,iBAAiB;IAC/B,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AACtC,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IACrD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CACb,4BAA4B,IAAI,uBAAuB,iBAAiB,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CACzF,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Tunnel runtime state — lets `brigade expose status` / `stop` (separate
3
+ * short-lived processes) find a tunnel started by a long-running
4
+ * `brigade expose`.
5
+ *
6
+ * Stored in the OS cache dir (NOT under ~/.brigade) because it is ephemeral
7
+ * machine-local coordination — the same reasoning `resolveOsCacheDir` uses for
8
+ * the gateway lock in convex mode, and it keeps `~/.brigade` clean under the
9
+ * strict-zero guard. The file is unlinked on clean shutdown; a stale file is
10
+ * reconciled by the pid liveness check in `readTunnelState`.
11
+ */
12
+ export interface TunnelState {
13
+ /** Public URL without the token. */
14
+ url: string;
15
+ /** Public URL with `?token=` appended (when token-gated). */
16
+ urlWithToken: string;
17
+ /** Provider name. */
18
+ provider: string;
19
+ /** PID of the owning `brigade expose` process. */
20
+ pid: number;
21
+ /** Local auth-proxy port. */
22
+ proxyPort: number;
23
+ /** Gateway port being exposed. */
24
+ gatewayPort: number;
25
+ /** Whether a token gate is active. */
26
+ secured: boolean;
27
+ /** Epoch ms when the tunnel came up. */
28
+ startedAt: number;
29
+ }
30
+ /** Atomically persist tunnel state (tempfile + rename). */
31
+ export declare function writeTunnelState(state: TunnelState): Promise<void>;
32
+ /**
33
+ * Read tunnel state. Returns `undefined` when missing/unparseable. Does NOT
34
+ * verify liveness — callers that care use `isProcessAlive(state.pid)`.
35
+ */
36
+ export declare function readTunnelState(): TunnelState | undefined;
37
+ /** Remove the state file. Silent on missing file. */
38
+ export declare function clearTunnelState(): Promise<void>;
39
+ //# sourceMappingURL=state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../src/core/tunnel/state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAQH,MAAM,WAAW,WAAW;IAC1B,oCAAoC;IACpC,GAAG,EAAE,MAAM,CAAC;IACZ,6DAA6D;IAC7D,YAAY,EAAE,MAAM,CAAC;IACrB,qBAAqB;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,kDAAkD;IAClD,GAAG,EAAE,MAAM,CAAC;IACZ,6BAA6B;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,kCAAkC;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,sCAAsC;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,wCAAwC;IACxC,SAAS,EAAE,MAAM,CAAC;CACnB;AAMD,2DAA2D;AAC3D,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAMxE;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,WAAW,GAAG,SAAS,CAgBzD;AAED,qDAAqD;AACrD,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAMtD"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Tunnel runtime state — lets `brigade expose status` / `stop` (separate
3
+ * short-lived processes) find a tunnel started by a long-running
4
+ * `brigade expose`.
5
+ *
6
+ * Stored in the OS cache dir (NOT under ~/.brigade) because it is ephemeral
7
+ * machine-local coordination — the same reasoning `resolveOsCacheDir` uses for
8
+ * the gateway lock in convex mode, and it keeps `~/.brigade` clean under the
9
+ * strict-zero guard. The file is unlinked on clean shutdown; a stale file is
10
+ * reconciled by the pid liveness check in `readTunnelState`.
11
+ */
12
+ import * as fs from "node:fs";
13
+ import * as fsAsync from "node:fs/promises";
14
+ import * as path from "node:path";
15
+ import { resolveOsCacheDir } from "../../config/paths.js";
16
+ function tunnelStatePath() {
17
+ return path.join(resolveOsCacheDir(), "gateway-tunnel.json");
18
+ }
19
+ /** Atomically persist tunnel state (tempfile + rename). */
20
+ export async function writeTunnelState(state) {
21
+ const file = tunnelStatePath();
22
+ await fsAsync.mkdir(path.dirname(file), { recursive: true });
23
+ const tmp = `${file}.tmp`;
24
+ await fsAsync.writeFile(tmp, JSON.stringify(state, null, 2), "utf8");
25
+ await fsAsync.rename(tmp, file);
26
+ }
27
+ /**
28
+ * Read tunnel state. Returns `undefined` when missing/unparseable. Does NOT
29
+ * verify liveness — callers that care use `isProcessAlive(state.pid)`.
30
+ */
31
+ export function readTunnelState() {
32
+ try {
33
+ const raw = fs.readFileSync(tunnelStatePath(), "utf8");
34
+ const parsed = JSON.parse(raw);
35
+ if (typeof parsed.url === "string" &&
36
+ typeof parsed.provider === "string" &&
37
+ typeof parsed.pid === "number" &&
38
+ typeof parsed.proxyPort === "number") {
39
+ return parsed;
40
+ }
41
+ return undefined;
42
+ }
43
+ catch {
44
+ return undefined;
45
+ }
46
+ }
47
+ /** Remove the state file. Silent on missing file. */
48
+ export async function clearTunnelState() {
49
+ try {
50
+ await fsAsync.unlink(tunnelStatePath());
51
+ }
52
+ catch (err) {
53
+ if (err.code !== "ENOENT")
54
+ throw err;
55
+ }
56
+ }
57
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../../../src/core/tunnel/state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,OAAO,MAAM,kBAAkB,CAAC;AAC5C,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAqB1D,SAAS,eAAe;IACtB,OAAO,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,qBAAqB,CAAC,CAAC;AAC/D,CAAC;AAED,2DAA2D;AAC3D,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,KAAkB;IACvD,MAAM,IAAI,GAAG,eAAe,EAAE,CAAC;IAC/B,MAAM,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7D,MAAM,GAAG,GAAG,GAAG,IAAI,MAAM,CAAC;IAC1B,MAAM,OAAO,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;IACrE,MAAM,OAAO,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;AAClC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe;IAC7B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,eAAe,EAAE,EAAE,MAAM,CAAC,CAAC;QACvD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAyB,CAAC;QACvD,IACE,OAAO,MAAM,CAAC,GAAG,KAAK,QAAQ;YAC9B,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ;YACnC,OAAO,MAAM,CAAC,GAAG,KAAK,QAAQ;YAC9B,OAAO,MAAM,CAAC,SAAS,KAAK,QAAQ,EACpC,CAAC;YACD,OAAO,MAAqB,CAAC;QAC/B,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,qDAAqD;AACrD,MAAM,CAAC,KAAK,UAAU,gBAAgB;IACpC,IAAI,CAAC;QACH,MAAM,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC,CAAC;IAC1C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;YAAE,MAAM,GAAG,CAAC;IAClE,CAAC;AACH,CAAC"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Tunnel provider contract — the seam behind `brigade expose`.
3
+ *
4
+ * A provider's only job is to take a LOCAL port (where Brigade's
5
+ * token-checking auth-proxy is listening) and publish it on the internet,
6
+ * returning the public URL. Providers never touch the gateway directly: the
7
+ * auth-proxy already sits between the public URL and the (unauthenticated,
8
+ * loopback-only) gateway, so a provider just forwards bytes.
9
+ *
10
+ * Built-in providers live in `./providers/`:
11
+ * - `cloudflare` — anonymous TryCloudflare quick tunnel (auto-managed binary)
12
+ * - `bore` — the OSS `bore` client (self-hostable relay)
13
+ * - `custom` — a user-supplied command template (`{port}` placeholder)
14
+ *
15
+ * Authoring a new provider = implement `TunnelProvider` and register it in
16
+ * `./registry.ts`. Everything else (auth-proxy, token gate, state file,
17
+ * lifecycle, CLI) is provider-agnostic.
18
+ */
19
+ /** Options handed to a provider's `start()`. */
20
+ export interface TunnelStartOptions {
21
+ /** Loopback host the auth-proxy is bound to (always `127.0.0.1`). */
22
+ localHost: string;
23
+ /** Port the auth-proxy is listening on — the tunnel points HERE, never at
24
+ * the raw gateway port. */
25
+ localPort: number;
26
+ /** Self-hosted relay address (`bore` / `custom`). Provider-defined default
27
+ * when omitted. */
28
+ relay?: string;
29
+ /** `custom` provider command template; `{port}` → `localPort`. */
30
+ command?: string;
31
+ /** Sink for provider log lines (binary stdout/stderr, status). */
32
+ onLog?: (line: string) => void;
33
+ }
34
+ /** A running tunnel. `stop()` must be idempotent. */
35
+ export interface TunnelHandle {
36
+ /** Provider name that produced this handle. */
37
+ provider: string;
38
+ /** Public URL (scheme included), e.g. `https://lively-fox-42.trycloudflare.com`
39
+ * or `http://bore.pub:41734`. Does NOT include the auth token. */
40
+ url: string;
41
+ /** Tear down the tunnel process/connection. Safe to call more than once. */
42
+ stop(): Promise<void>;
43
+ }
44
+ /** A tunnel backend. */
45
+ export interface TunnelProvider {
46
+ /** Stable id used in config + the `--provider` flag. */
47
+ name: string;
48
+ /** Human label for status output. */
49
+ label: string;
50
+ /** Whether this provider can run right now on this machine (binary present,
51
+ * command supplied, …). Cheap; called before `start()`. */
52
+ isAvailable(opts?: TunnelStartOptions): Promise<TunnelAvailability>;
53
+ /** Establish the tunnel. Rejects if the public URL can't be obtained. */
54
+ start(opts: TunnelStartOptions): Promise<TunnelHandle>;
55
+ }
56
+ export interface TunnelAvailability {
57
+ ok: boolean;
58
+ /** Operator-facing reason when `ok === false` (how to fix it). */
59
+ reason?: string;
60
+ }
61
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/core/tunnel/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,gDAAgD;AAChD,MAAM,WAAW,kBAAkB;IACjC,qEAAqE;IACrE,SAAS,EAAE,MAAM,CAAC;IAClB;gCAC4B;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB;wBACoB;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kEAAkE;IAClE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kEAAkE;IAClE,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAChC;AAED,qDAAqD;AACrD,MAAM,WAAW,YAAY;IAC3B,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,CAAC;IACjB;uEACmE;IACnE,GAAG,EAAE,MAAM,CAAC;IACZ,4EAA4E;IAC5E,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAED,wBAAwB;AACxB,MAAM,WAAW,cAAc;IAC7B,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAC;IACb,qCAAqC;IACrC,KAAK,EAAE,MAAM,CAAC;IACd;gEAC4D;IAC5D,WAAW,CAAC,IAAI,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACpE,yEAAyE;IACzE,KAAK,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;CACxD;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,OAAO,CAAC;IACZ,kEAAkE;IAClE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Tunnel provider contract — the seam behind `brigade expose`.
3
+ *
4
+ * A provider's only job is to take a LOCAL port (where Brigade's
5
+ * token-checking auth-proxy is listening) and publish it on the internet,
6
+ * returning the public URL. Providers never touch the gateway directly: the
7
+ * auth-proxy already sits between the public URL and the (unauthenticated,
8
+ * loopback-only) gateway, so a provider just forwards bytes.
9
+ *
10
+ * Built-in providers live in `./providers/`:
11
+ * - `cloudflare` — anonymous TryCloudflare quick tunnel (auto-managed binary)
12
+ * - `bore` — the OSS `bore` client (self-hostable relay)
13
+ * - `custom` — a user-supplied command template (`{port}` placeholder)
14
+ *
15
+ * Authoring a new provider = implement `TunnelProvider` and register it in
16
+ * `./registry.ts`. Everything else (auth-proxy, token gate, state file,
17
+ * lifecycle, CLI) is provider-agnostic.
18
+ */
19
+ export {};
20
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/core/tunnel/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG"}
@@ -19,6 +19,20 @@
19
19
  */
20
20
  /** Default redirect cap — matches the upstream reference. */
21
21
  export declare const DEFAULT_MAX_REDIRECTS = 3;
22
+ /** The injectable fetch primitive — production uses the global `fetch`. */
23
+ export type FetchLike = typeof fetch;
24
+ /**
25
+ * SSRF classification options. `allowPrivateNetwork` is a DANGEROUS opt-in for
26
+ * channels that legitimately talk to a LAN / private-IP host (e.g. a BlueBubbles
27
+ * macOS server on `192.168.x.x`). It relaxes the private/loopback/link-local IP
28
+ * checks — but NEVER the cloud-metadata block: `169.254.169.254`, the
29
+ * `metadata.*` hostnames, and the IPv6 metadata literal stay forbidden, so a
30
+ * redirect-to-metadata SSRF is still blocked even with private access on.
31
+ */
32
+ export interface SsrfClassifyOptions {
33
+ /** Allow RFC1918 / loopback / link-local / ULA targets (cloud-metadata stays blocked). */
34
+ allowPrivateNetwork?: boolean;
35
+ }
22
36
  /** Custom error thrown when an SSRF gate rejects a URL or resolved IP. */
23
37
  export declare class SsrfBlockedError extends Error {
24
38
  readonly url: string;
@@ -35,7 +49,7 @@ export declare class RedirectError extends Error {
35
49
  * obvious cases (literal `localhost`, IP-in-hostname, forbidden suffix)
36
50
  * so we don't even spend a DNS round-trip.
37
51
  */
38
- export declare function classifyHostnameSync(hostname: string): string | null;
52
+ export declare function classifyHostnameSync(hostname: string, opts?: SsrfClassifyOptions): string | null;
39
53
  /**
40
54
  * Full SSRF check — sync hostname classify + DNS resolution + re-classify
41
55
  * every resolved A/AAAA record. A hostname like `attacker.com` passing
@@ -46,13 +60,21 @@ export declare function classifyHostnameSync(hostname: string): string | null;
46
60
  * URL is safe, returns an `SsrfBlockedError`-shaped reason string when
47
61
  * not. Caller throws.
48
62
  */
49
- export declare function classifyUrlForSsrf(rawUrl: string): Promise<string | null>;
63
+ export declare function classifyUrlForSsrf(rawUrl: string, opts?: SsrfClassifyOptions): Promise<string | null>;
50
64
  export interface GuardedFetchOptions {
51
65
  method?: string;
52
66
  headers?: Record<string, string>;
53
67
  body?: RequestInit["body"];
54
68
  maxRedirects?: number;
55
69
  timeoutMs?: number;
70
+ /**
71
+ * DANGEROUS opt-in: allow private/LAN/loopback targets (cloud-metadata stays
72
+ * blocked on every hop). For channels that talk to an operator's own host
73
+ * (e.g. a BlueBubbles macOS server on `192.168.x.x`).
74
+ */
75
+ allowPrivateNetwork?: boolean;
76
+ /** Inject the network primitive (TEST SEAM). Defaults to the global `fetch`. */
77
+ fetchImpl?: FetchLike;
56
78
  /** Signal from the caller (e.g. agent turn cancel). Combined with the timeout signal. */
57
79
  signal?: AbortSignal;
58
80
  /** When set, retry transient failures (429 / 5xx / network) with exp-backoff. */