arena-bridge 0.6.1 → 0.6.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.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,13 @@ All notable changes to this project are documented here.
4
4
  The format follows [Keep a Changelog](https://keepachangelog.com/) and the project uses
5
5
  [Semantic Versioning](https://semver.org/).
6
6
 
7
+ ## 0.6.2
8
+
9
+ ### Added
10
+ - `arena-bridge tunnel --bin ngrok` runs an ngrok tunnel instead of cloudflared; with an
11
+ authtoken and a reserved domain (`--authtoken`, `--domain`) the public URL stays the same
12
+ across restarts, so the agent prompt does not have to be copied again.
13
+
7
14
  ## 0.6.1
8
15
 
9
16
  ### Fixed
package/README.md CHANGED
@@ -54,7 +54,9 @@ arena-bridge setup --root ~/projects --root ~/Documents
54
54
  `setup` печатает готовый промпт и публичный URL. Дальше — просто говорите агенту задачу.
55
55
 
56
56
  Нужен туннель? Поставьте `cloudflared` (`brew install cloudflared` или бинарник в `/usr/local/bin`).
57
- Подойдёт и любой другой: `ngrok http 8787`, `tailscale funnel --bg 8787`.
57
+ Можно и через ngrok — `arena-bridge tunnel --bin ngrok`; с сохранённым токеном и зарезервированным
58
+ доменом публичный адрес остаётся постоянным, и промпт агенту не нужно копировать заново:
59
+ `arena-bridge tunnel --bin ngrok --authtoken <token> --domain <name>.ngrok-free.dev`.
58
60
 
59
61
  ### В фоне, чтобы не открывать терминал
60
62
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arena-bridge",
3
- "version": "0.6.1",
3
+ "version": "0.6.2",
4
4
  "description": "Give a cloud AI agent (Arena.ai Agent Mode, Claude, Cursor, ...) controlled access to your own machine over HTTP/MCP: files, shell, Chrome and the macOS desktop, with an approval gate, trust windows and an audit log.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli.mjs CHANGED
@@ -92,7 +92,9 @@ Common options:
92
92
  --hub URL hub base URL (for "connect" / init --mode hub)
93
93
  --public-url URL public base URL the agent should use
94
94
  --protocol P tunnel transport: http2 (default, works on locked-down networks) or quic
95
- --bin PATH tunnel binary to run (default: cloudflared)
95
+ --bin PATH tunnel binary to run (default: cloudflared; "ngrok" is supported too)
96
+ --authtoken TOKEN ngrok authtoken (keeps the same URL on every restart)
97
+ --domain NAME ngrok reserved domain, e.g. my-machine.ngrok-free.app
96
98
  --no-approval disable the human approval gate (dangerous)
97
99
  --no-shell disable shell execution entirely
98
100
 
@@ -197,7 +199,13 @@ async function cmdTunnel(args) {
197
199
  }
198
200
 
199
201
  console.log(`starting ${bin} (protocol=${protocol})…`);
200
- const { child, urlPromise } = startTunnel(cfg, { port, protocol, bin });
202
+ const { child, urlPromise } = startTunnel(cfg, {
203
+ port,
204
+ protocol,
205
+ bin,
206
+ authtoken: args.flags.authtoken,
207
+ domain: args.flags.domain,
208
+ });
201
209
 
202
210
  let publicUrl;
203
211
  try {
package/src/direct.mjs CHANGED
@@ -29,7 +29,7 @@ export function createDirectServer(cfg, engine) {
29
29
  ok: true,
30
30
  mode: 'direct',
31
31
  device: cfg.device,
32
- version: '0.6.1',
32
+ version: '0.6.2',
33
33
  uptime_sec: Math.round(process.uptime()),
34
34
  });
35
35
  }
package/src/hub.mjs CHANGED
@@ -90,7 +90,7 @@ export function createHubServer(cfg) {
90
90
  return sendJson(res, 200, {
91
91
  ok: true,
92
92
  mode: 'hub',
93
- version: '0.6.1',
93
+ version: '0.6.2',
94
94
  devices: [...devices.keys()],
95
95
  });
96
96
  }
package/src/mcp.mjs CHANGED
@@ -48,7 +48,7 @@ export async function handleMcp(req, res, ctx) {
48
48
  mcpResponse(id, {
49
49
  protocolVersion: PROTOCOL_VERSION,
50
50
  capabilities: { tools: { listChanged: false } },
51
- serverInfo: { name: 'arena-bridge', version: '0.6.1' },
51
+ serverInfo: { name: 'arena-bridge', version: '0.6.2' },
52
52
  instructions:
53
53
  'Tools operate the user\'s own machine. Dangerous operations may return ' +
54
54
  'PROTECTED and require a local human approval.',
package/src/tunnel.mjs CHANGED
@@ -2,15 +2,27 @@ import { spawn } from 'node:child_process';
2
2
  import net from 'node:net';
3
3
 
4
4
  const URL_RE = /https:\/\/[a-z0-9-]+\.trycloudflare\.com/i;
5
+ const NGROK_URL_RE = /https:\/\/[a-z0-9-]+\.ngrok(?:-free)?\.(?:app|io|dev)/i;
5
6
 
6
7
  /**
7
- * Starts a cloudflared quick tunnel and resolves with the public URL it prints.
8
- * QUIC (UDP/7844) is blocked on many corporate/ISP networks, so we default to
9
- * http2 transport, which works over plain TCP/443.
8
+ * Starts a tunnel and resolves with the public URL it prints.
9
+ * cloudflared: QUIC (UDP/7844) is blocked on many networks, so we default to http2 over TCP/443.
10
+ * ngrok: an authtoken and an optional reserved domain give the same URL on every restart.
10
11
  */
11
- export function startTunnel(cfg, { port, protocol = 'http2', bin = 'cloudflared' } = {}) {
12
+ export function startTunnel(cfg, { port, protocol = 'http2', bin = 'cloudflared', authtoken, domain } = {}) {
12
13
  const target = `http://127.0.0.1:${port}`;
13
- const args = ['tunnel', '--url', target, '--protocol', protocol];
14
+ const isNgrok = /ngrok/i.test(bin);
15
+ const args = isNgrok
16
+ ? [
17
+ 'http',
18
+ target,
19
+ ...(authtoken ? ['--authtoken', String(authtoken)] : []),
20
+ ...(domain ? ['--domain', String(domain)] : []),
21
+ '--log',
22
+ 'stdout',
23
+ ]
24
+ : ['tunnel', '--url', target, '--protocol', protocol];
25
+ const urlPattern = isNgrok ? NGROK_URL_RE : URL_RE;
14
26
 
15
27
  const child = spawn(bin, args, { stdio: ['ignore', 'pipe', 'pipe'] });
16
28
 
@@ -27,7 +39,7 @@ export function startTunnel(cfg, { port, protocol = 'http2', bin = 'cloudflared'
27
39
  const onChunk = (chunk) => {
28
40
  const text = chunk.toString();
29
41
  buffer += text;
30
- const match = URL_RE.exec(buffer);
42
+ const match = urlPattern.exec(buffer);
31
43
  if (match && !settled) {
32
44
  settled = true;
33
45
  resolveUrl(match[0]);