@kortix/agent-tunnel 0.13.56 → 0.13.57

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/dist/agent-cli.js CHANGED
@@ -19,13 +19,15 @@ var __toESM = (mod, isNodeMode, target) => {
19
19
  return cached;
20
20
  }
21
21
  target = mod != null ? __create(__getProtoOf(mod)) : {};
22
- const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
23
- for (let key of __getOwnPropNames(mod))
24
- if (!__hasOwnProp.call(to, key))
25
- __defProp(to, key, {
26
- get: __accessProp.bind(mod, key),
27
- enumerable: true
28
- });
22
+ const to = isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
23
+ if (mod && typeof mod === "object" || typeof mod === "function") {
24
+ for (let key of __getOwnPropNames(mod))
25
+ if (!__hasOwnProp.call(to, key))
26
+ __defProp(to, key, {
27
+ get: __accessProp.bind(mod, key),
28
+ enumerable: true
29
+ });
30
+ }
29
31
  if (canCache)
30
32
  cache.set(mod, to);
31
33
  return to;
@@ -44,11 +46,21 @@ var __export = (target, all) => {
44
46
  set: __exportSetter.bind(all, name)
45
47
  });
46
48
  };
47
- var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
49
+ var __esm = (fn, res, err) => () => {
50
+ if (fn)
51
+ try {
52
+ res = fn(fn = 0);
53
+ } catch (e) {
54
+ err = [e];
55
+ }
56
+ if (err)
57
+ throw err[0];
58
+ return res;
59
+ };
48
60
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
49
61
 
50
62
  // ../../node_modules/.pnpm/ws@8.21.3/node_modules/ws/lib/constants.js
51
- var require_constants = __commonJS((exports, module) => {
63
+ var require_constants = __commonJS(function(exports, module) {
52
64
  var BINARY_TYPES = ["nodebuffer", "arraybuffer", "fragments"];
53
65
  var hasBlob = typeof Blob !== "undefined";
54
66
  if (hasBlob)
@@ -68,7 +80,7 @@ var require_constants = __commonJS((exports, module) => {
68
80
  });
69
81
 
70
82
  // ../../node_modules/.pnpm/ws@8.21.3/node_modules/ws/lib/buffer-util.js
71
- var require_buffer_util = __commonJS((exports, module) => {
83
+ var require_buffer_util = __commonJS(function(exports, module) {
72
84
  var { EMPTY_BUFFER } = require_constants();
73
85
  var FastBuffer = Buffer[Symbol.species];
74
86
  function concat(list, totalLength) {
@@ -129,11 +141,11 @@ var require_buffer_util = __commonJS((exports, module) => {
129
141
  if (!process.env.WS_NO_BUFFER_UTIL) {
130
142
  try {
131
143
  const bufferUtil = (()=>{throw new Error("Cannot require module "+"bufferutil");})();
132
- module.exports.mask = function(source, mask, output, offset, length) {
144
+ module.exports.mask = function(source, mask2, output, offset, length) {
133
145
  if (length < 48)
134
- _mask(source, mask, output, offset, length);
146
+ _mask(source, mask2, output, offset, length);
135
147
  else
136
- bufferUtil.mask(source, mask, output, offset, length);
148
+ bufferUtil.mask(source, mask2, output, offset, length);
137
149
  };
138
150
  module.exports.unmask = function(buffer, mask) {
139
151
  if (buffer.length < 32)
@@ -146,7 +158,7 @@ var require_buffer_util = __commonJS((exports, module) => {
146
158
  });
147
159
 
148
160
  // ../../node_modules/.pnpm/ws@8.21.3/node_modules/ws/lib/limiter.js
149
- var require_limiter = __commonJS((exports, module) => {
161
+ var require_limiter = __commonJS(function(exports, module) {
150
162
  var kDone = Symbol("kDone");
151
163
  var kRun = Symbol("kRun");
152
164
 
@@ -178,7 +190,7 @@ var require_limiter = __commonJS((exports, module) => {
178
190
  });
179
191
 
180
192
  // ../../node_modules/.pnpm/ws@8.21.3/node_modules/ws/lib/permessage-deflate.js
181
- var require_permessage_deflate = __commonJS((exports, module) => {
193
+ var require_permessage_deflate = __commonJS(function(exports, module) {
182
194
  var zlib = __require("zlib");
183
195
  var bufferUtil = require_buffer_util();
184
196
  var Limiter = require_limiter();
@@ -366,7 +378,7 @@ var require_permessage_deflate = __commonJS((exports, module) => {
366
378
  callback(err);
367
379
  return;
368
380
  }
369
- const data2 = bufferUtil.concat(this._inflate[kBuffers], this._inflate[kTotalLength]);
381
+ const data = bufferUtil.concat(this._inflate[kBuffers], this._inflate[kTotalLength]);
370
382
  if (this._inflate._readableState.endEmitted) {
371
383
  this._inflate.close();
372
384
  this._inflate = null;
@@ -377,7 +389,7 @@ var require_permessage_deflate = __commonJS((exports, module) => {
377
389
  this._inflate.reset();
378
390
  }
379
391
  }
380
- callback(null, data2);
392
+ callback(null, data);
381
393
  });
382
394
  }
383
395
  _compress(data, fin, callback) {
@@ -399,9 +411,9 @@ var require_permessage_deflate = __commonJS((exports, module) => {
399
411
  if (!this._deflate) {
400
412
  return;
401
413
  }
402
- let data2 = bufferUtil.concat(this._deflate[kBuffers], this._deflate[kTotalLength]);
414
+ let data = bufferUtil.concat(this._deflate[kBuffers], this._deflate[kTotalLength]);
403
415
  if (fin) {
404
- data2 = new FastBuffer(data2.buffer, data2.byteOffset, data2.length - 4);
416
+ data = new FastBuffer(data.buffer, data.byteOffset, data.length - 4);
405
417
  }
406
418
  this._deflate[kCallback] = null;
407
419
  this._deflate[kTotalLength] = 0;
@@ -409,7 +421,7 @@ var require_permessage_deflate = __commonJS((exports, module) => {
409
421
  if (fin && this.params[`${endpoint}_no_context_takeover`]) {
410
422
  this._deflate.reset();
411
423
  }
412
- callback(null, data2);
424
+ callback(null, data);
413
425
  });
414
426
  }
415
427
  }
@@ -442,7 +454,7 @@ var require_permessage_deflate = __commonJS((exports, module) => {
442
454
  });
443
455
 
444
456
  // ../../node_modules/.pnpm/ws@8.21.3/node_modules/ws/lib/validation.js
445
- var require_validation = __commonJS((exports, module) => {
457
+ var require_validation = __commonJS(function(exports, module) {
446
458
  var { isUtf8 } = __require("buffer");
447
459
  var { hasBlob } = require_constants();
448
460
  var tokenChars = [
@@ -629,7 +641,7 @@ var require_validation = __commonJS((exports, module) => {
629
641
  });
630
642
 
631
643
  // ../../node_modules/.pnpm/ws@8.21.3/node_modules/ws/lib/receiver.js
632
- var require_receiver = __commonJS((exports, module) => {
644
+ var require_receiver = __commonJS(function(exports, module) {
633
645
  var { Writable } = __require("stream");
634
646
  var PerMessageDeflate = require_permessage_deflate();
635
647
  var {
@@ -1023,7 +1035,7 @@ var require_receiver = __commonJS((exports, module) => {
1023
1035
  });
1024
1036
 
1025
1037
  // ../../node_modules/.pnpm/ws@8.21.3/node_modules/ws/lib/sender.js
1026
- var require_sender = __commonJS((exports, module) => {
1038
+ var require_sender = __commonJS(function(exports, module) {
1027
1039
  var { Duplex } = __require("stream");
1028
1040
  var { randomFillSync } = __require("crypto");
1029
1041
  var {
@@ -1382,7 +1394,7 @@ var require_sender = __commonJS((exports, module) => {
1382
1394
  });
1383
1395
 
1384
1396
  // ../../node_modules/.pnpm/ws@8.21.3/node_modules/ws/lib/event-target.js
1385
- var require_event_target = __commonJS((exports, module) => {
1397
+ var require_event_target = __commonJS(function(exports, module) {
1386
1398
  var { kForOnEventAttribute, kListener } = require_constants();
1387
1399
  var kCode = Symbol("kCode");
1388
1400
  var kData = Symbol("kData");
@@ -1533,7 +1545,7 @@ var require_event_target = __commonJS((exports, module) => {
1533
1545
  });
1534
1546
 
1535
1547
  // ../../node_modules/.pnpm/ws@8.21.3/node_modules/ws/lib/extension.js
1536
- var require_extension = __commonJS((exports, module) => {
1548
+ var require_extension = __commonJS(function(exports, module) {
1537
1549
  var { tokenChars } = require_validation();
1538
1550
  function push(dest, name, elem) {
1539
1551
  if (dest[name] === undefined)
@@ -1698,7 +1710,7 @@ var require_extension = __commonJS((exports, module) => {
1698
1710
  });
1699
1711
 
1700
1712
  // ../../node_modules/.pnpm/ws@8.21.3/node_modules/ws/lib/websocket.js
1701
- var require_websocket = __commonJS((exports, module) => {
1713
+ var require_websocket = __commonJS(function(exports, module) {
1702
1714
  var EventEmitter = __require("events");
1703
1715
  var https = __require("https");
1704
1716
  var http = __require("http");
@@ -2121,7 +2133,7 @@ var require_websocket = __commonJS((exports, module) => {
2121
2133
  }
2122
2134
  const defaultPort = isSecure ? 443 : 80;
2123
2135
  const key = randomBytes(16).toString("base64");
2124
- const request = isSecure ? https.request : http.request;
2136
+ const request3 = isSecure ? https.request : http.request;
2125
2137
  const protocolSet = new Set;
2126
2138
  let perMessageDeflate;
2127
2139
  opts.createConnection = opts.createConnection || (isSecure ? tlsConnect : netConnect);
@@ -2180,8 +2192,8 @@ var require_websocket = __commonJS((exports, module) => {
2180
2192
  const headers = options && options.headers;
2181
2193
  options = { ...options, headers: {} };
2182
2194
  if (headers) {
2183
- for (const [key2, value] of Object.entries(headers)) {
2184
- options.headers[key2.toLowerCase()] = value;
2195
+ for (const [key, value] of Object.entries(headers)) {
2196
+ options.headers[key.toLowerCase()] = value;
2185
2197
  }
2186
2198
  }
2187
2199
  } else if (websocket.listenerCount("redirect") === 0) {
@@ -2197,12 +2209,12 @@ var require_websocket = __commonJS((exports, module) => {
2197
2209
  if (opts.auth && !options.headers.authorization) {
2198
2210
  options.headers.authorization = "Basic " + Buffer.from(opts.auth).toString("base64");
2199
2211
  }
2200
- req = websocket._req = request(opts);
2212
+ req = websocket._req = request3(opts);
2201
2213
  if (websocket._redirects) {
2202
2214
  websocket.emit("redirect", websocket.url, req);
2203
2215
  }
2204
2216
  } else {
2205
- req = websocket._req = request(opts);
2217
+ req = websocket._req = request3(opts);
2206
2218
  }
2207
2219
  if (opts.timeout) {
2208
2220
  req.on("timeout", () => {
@@ -2470,7 +2482,7 @@ var require_websocket = __commonJS((exports, module) => {
2470
2482
  });
2471
2483
 
2472
2484
  // ../../node_modules/.pnpm/ws@8.21.3/node_modules/ws/lib/stream.js
2473
- var require_stream = __commonJS((exports, module) => {
2485
+ var require_stream = __commonJS(function(exports, module) {
2474
2486
  var WebSocket2 = require_websocket();
2475
2487
  var { Duplex } = __require("stream");
2476
2488
  function emitClose(stream) {
@@ -2520,9 +2532,9 @@ var require_stream = __commonJS((exports, module) => {
2520
2532
  return;
2521
2533
  }
2522
2534
  let called = false;
2523
- ws.once("error", function error(err2) {
2535
+ ws.once("error", function error(err) {
2524
2536
  called = true;
2525
- callback(err2);
2537
+ callback(err);
2526
2538
  });
2527
2539
  ws.once("close", function close() {
2528
2540
  if (!called)
@@ -2573,7 +2585,7 @@ var require_stream = __commonJS((exports, module) => {
2573
2585
  });
2574
2586
 
2575
2587
  // ../../node_modules/.pnpm/ws@8.21.3/node_modules/ws/lib/subprotocol.js
2576
- var require_subprotocol = __commonJS((exports, module) => {
2588
+ var require_subprotocol = __commonJS(function(exports, module) {
2577
2589
  var { tokenChars } = require_validation();
2578
2590
  function parse(header) {
2579
2591
  const protocols = new Set;
@@ -2594,11 +2606,11 @@ var require_subprotocol = __commonJS((exports, module) => {
2594
2606
  }
2595
2607
  if (end === -1)
2596
2608
  end = i;
2597
- const protocol2 = header.slice(start, end);
2598
- if (protocols.has(protocol2)) {
2599
- throw new SyntaxError(`The "${protocol2}" subprotocol is duplicated`);
2609
+ const protocol = header.slice(start, end);
2610
+ if (protocols.has(protocol)) {
2611
+ throw new SyntaxError(`The "${protocol}" subprotocol is duplicated`);
2600
2612
  }
2601
- protocols.add(protocol2);
2613
+ protocols.add(protocol);
2602
2614
  start = end = -1;
2603
2615
  } else {
2604
2616
  throw new SyntaxError(`Unexpected character at index ${i}`);
@@ -2618,7 +2630,7 @@ var require_subprotocol = __commonJS((exports, module) => {
2618
2630
  });
2619
2631
 
2620
2632
  // ../../node_modules/.pnpm/ws@8.21.3/node_modules/ws/lib/websocket-server.js
2621
- var require_websocket_server = __commonJS((exports, module) => {
2633
+ var require_websocket_server = __commonJS(function(exports, module) {
2622
2634
  var EventEmitter = __require("events");
2623
2635
  var http = __require("http");
2624
2636
  var { Duplex } = __require("stream");
@@ -2930,15 +2942,8 @@ var require_websocket_server = __commonJS((exports, module) => {
2930
2942
  // ../../node_modules/.pnpm/ws@8.21.3/node_modules/ws/wrapper.mjs
2931
2943
  var exports_wrapper = {};
2932
2944
  __export(exports_wrapper, {
2933
- subprotocol: () => import_subprotocol.default,
2934
- extension: () => import_extension.default,
2935
- default: () => wrapper_default,
2936
- createWebSocketStream: () => import_stream.default,
2937
- WebSocketServer: () => import_websocket_server.default,
2938
2945
  WebSocket: () => import_websocket.default,
2939
- Sender: () => import_sender.default,
2940
- Receiver: () => import_receiver.default,
2941
- PerMessageDeflate: () => import_permessage_deflate.default
2946
+ default: () => wrapper_default
2942
2947
  });
2943
2948
  var import_stream, import_extension, import_permessage_deflate, import_receiver, import_sender, import_subprotocol, import_websocket, import_websocket_server, wrapper_default;
2944
2949
  var init_wrapper = __esm(() => {
@@ -2957,7 +2962,7 @@ var init_wrapper = __esm(() => {
2957
2962
  if (typeof globalThis.WebSocket === "undefined") {
2958
2963
  try {
2959
2964
  const ws = await Promise.resolve().then(() => (init_wrapper(), exports_wrapper));
2960
- const WebSocketImpl = ws.WebSocket ?? ws.default;
2965
+ const WebSocketImpl = ws.WebSocket ?? wrapper_default;
2961
2966
  globalThis.WebSocket = WebSocketImpl;
2962
2967
  } catch {
2963
2968
  console.error('[agent-tunnel] WebSocket is not available. Install the "ws" package or use Node.js 22+.');
@@ -3677,10 +3682,10 @@ function wakeDesktopApp(home = agentTunnelHome(), now = Date.now()) {
3677
3682
  return "no-app";
3678
3683
  }
3679
3684
  }
3680
- function keepAwakeCommand(platform2, pid) {
3681
- if (platform2 === "darwin")
3685
+ function keepAwakeCommand(platform, pid) {
3686
+ if (platform === "darwin")
3682
3687
  return { command: "caffeinate", args: ["-s", "-w", String(pid)] };
3683
- if (platform2 === "linux") {
3688
+ if (platform === "linux") {
3684
3689
  return {
3685
3690
  command: "systemd-inhibit",
3686
3691
  args: ["--what=sleep", "--who=Kortix", "--why=Keep this computer reachable", "--mode=block", "tail", `--pid=${pid}`, "-f", "/dev/null"]
@@ -3947,7 +3952,7 @@ function visibleLength(value) {
3947
3952
  function clearScreen() {
3948
3953
  process.stdout.write("\x1B[2J\x1B[3J\x1B[H");
3949
3954
  }
3950
- var sleep = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
3955
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
3951
3956
  var glyph = {
3952
3957
  on: `${c.green}●${c.reset}`,
3953
3958
  off: `${c.gray}○${c.reset}`,
@@ -4485,7 +4490,7 @@ class TunnelAgent {
4485
4490
  } catch {}
4486
4491
  const deadline = Date.now() + hold;
4487
4492
  while (Date.now() < deadline) {
4488
- await new Promise((resolve2) => setTimeout(resolve2, Math.min(100, hold / 4)));
4493
+ await new Promise((resolve) => setTimeout(resolve, Math.min(100, hold / 4)));
4489
4494
  const decision = decideAccess(readAccess(this.home));
4490
4495
  if (decision === "ask")
4491
4496
  continue;
@@ -4527,7 +4532,7 @@ class TunnelAgent {
4527
4532
  const hold = this.options.accessHoldMs ?? ACCESS_HOLD_MS;
4528
4533
  const deadline = Date.now() + hold;
4529
4534
  while (Date.now() < deadline) {
4530
- await new Promise((resolve2) => setTimeout(resolve2, Math.min(100, hold / 4)));
4535
+ await new Promise((resolve) => setTimeout(resolve, Math.min(100, hold / 4)));
4531
4536
  if (!accessWidens(readAccess(this.home), target, this.now())) {
4532
4537
  clearAccessRequest(asked.id, this.home);
4533
4538
  return accessView(this.home, platform3(), this.now());
@@ -4853,7 +4858,7 @@ function hostBundleId() {
4853
4858
  }
4854
4859
  }
4855
4860
  function execFile(cmd, args, timeoutMs = 30000, env = driverEnvironment()) {
4856
- return new Promise((resolve2, reject) => {
4861
+ return new Promise((resolve, reject) => {
4857
4862
  const proc = spawn4(cmd, args, {
4858
4863
  stdio: ["ignore", "pipe", "pipe"],
4859
4864
  env
@@ -4898,7 +4903,7 @@ function execFile(cmd, args, timeoutMs = 30000, env = driverEnvironment()) {
4898
4903
  const detail = stderr.trim() || stdout.trim();
4899
4904
  reject(new Error(`${basename2(cmd)} failed (${code})${detail ? `: ${detail}` : ""}`));
4900
4905
  } else {
4901
- resolve2({ stdout, stderr });
4906
+ resolve({ stdout, stderr });
4902
4907
  }
4903
4908
  });
4904
4909
  proc.on("error", (err) => {
@@ -4920,7 +4925,7 @@ function isDaemonProxyFallback(message) {
4920
4925
  return message.includes("daemon proxy") && message.includes("Resource temporarily unavailable");
4921
4926
  }
4922
4927
  async function sleep2(ms) {
4923
- await new Promise((resolve2) => setTimeout(resolve2, ms));
4928
+ await new Promise((resolve) => setTimeout(resolve, ms));
4924
4929
  }
4925
4930
  function sanitizeArgs(args) {
4926
4931
  const sanitized = {};
@@ -5546,7 +5551,7 @@ function createShellCapability(config) {
5546
5551
  safeEnv[key] = process.env[key];
5547
5552
  }
5548
5553
  }
5549
- return new Promise((resolve3, reject) => {
5554
+ return new Promise((resolve, reject) => {
5550
5555
  const proc = spawn5(executable, args, {
5551
5556
  cwd,
5552
5557
  shell: false,
@@ -5589,7 +5594,7 @@ function createShellCapability(config) {
5589
5594
  reject(new Error(`Command failed to start: ${err.message}`));
5590
5595
  });
5591
5596
  proc.on("close", (code, signal) => {
5592
- resolve3({
5597
+ resolve({
5593
5598
  exitCode: code,
5594
5599
  signal,
5595
5600
  stdout,
@@ -5678,17 +5683,17 @@ function clearSavedCredentials() {
5678
5683
  // src/agent/credential-probe.ts
5679
5684
  var DEFAULT_PROBE_TIMEOUT_MS = 1e4;
5680
5685
  function probeCredentials(config, options = {}) {
5681
- return new Promise((resolve3) => {
5686
+ return new Promise((resolve) => {
5682
5687
  let socket;
5683
5688
  try {
5684
5689
  socket = openRelaySocket(new URL(buildTunnelWsUrl(config)).href);
5685
5690
  } catch {
5686
- resolve3("unreachable");
5691
+ resolve("unreachable");
5687
5692
  return;
5688
5693
  }
5689
5694
  const settle = (result) => {
5690
5695
  clearTimeout(timer);
5691
- resolve3(result);
5696
+ resolve(result);
5692
5697
  try {
5693
5698
  socket.close(1000, "probe complete");
5694
5699
  } catch {}
@@ -6262,8 +6267,8 @@ import { statSync as statSync3 } from "fs";
6262
6267
  function runtimeFingerprint(files) {
6263
6268
  return files.map((file) => {
6264
6269
  try {
6265
- const stat2 = statSync3(file);
6266
- return `${stat2.ino}:${stat2.mtimeMs}:${stat2.size}`;
6270
+ const stat = statSync3(file);
6271
+ return `${stat.ino}:${stat.mtimeMs}:${stat.size}`;
6267
6272
  } catch {
6268
6273
  return "missing";
6269
6274
  }
@@ -6405,7 +6410,6 @@ function startAgent(config, options = {}) {
6405
6410
  persistent: options.service === true,
6406
6411
  reloadConfig: () => loadConfig({ apiUrl: config.apiUrl })
6407
6412
  });
6408
- agent.connect();
6409
6413
  const shutdown = () => {
6410
6414
  if (!options.service)
6411
6415
  console.log(`
@@ -6415,6 +6419,7 @@ ${c.dim} Shutting down…${c.reset}`);
6415
6419
  };
6416
6420
  process.on("SIGTERM", shutdown);
6417
6421
  process.on("SIGINT", shutdown);
6422
+ agent.connect();
6418
6423
  }
6419
6424
  function chooseBackgroundMode(flags) {
6420
6425
  if (anyFlag(flags, BACKGROUND_FLAGS))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kortix/agent-tunnel",
3
- "version": "0.13.56",
3
+ "version": "0.13.57",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/kortix-ai/suna.git",
package/src/agent/cli.ts CHANGED
@@ -149,8 +149,8 @@ function startAgent(config: TunnelConfig, options: { service?: boolean } = {}):
149
149
  reloadConfig: () => loadConfig({ apiUrl: config.apiUrl }),
150
150
  },
151
151
  );
152
- agent.connect();
153
-
152
+ // Before connect(): connect() writes state.json, and a SIGTERM that lands
153
+ // before a handler exists kills the process with the state left `connecting`.
154
154
  const shutdown = () => {
155
155
  if (!options.service) console.log(`\n${c.dim} Shutting down…${c.reset}`);
156
156
  agent?.disconnect();
@@ -158,6 +158,7 @@ function startAgent(config: TunnelConfig, options: { service?: boolean } = {}):
158
158
  };
159
159
  process.on('SIGTERM', shutdown);
160
160
  process.on('SIGINT', shutdown);
161
+ agent.connect();
161
162
  }
162
163
 
163
164
  // ── pairing ──────────────────────────────────────────────────────────────────
@@ -9,15 +9,15 @@ export function createTunnelRouter(
9
9
  ): Hono {
10
10
  const router = new Hono();
11
11
 
12
- if (onAuthorizeHTTP) {
13
- router.use('*', async (c, next) => {
14
- const authorized = await onAuthorizeHTTP(c.req.raw);
15
- if (!authorized) {
16
- return c.json({ error: 'Unauthorized' }, 403);
17
- }
18
- await next();
19
- });
20
- }
12
+ // Fail closed: these routes list agents and relay RPC to them (shell, files,
13
+ // desktop). With no authorizer, nobody is authorized.
14
+ router.use('*', async (c, next) => {
15
+ const authorized = onAuthorizeHTTP ? await onAuthorizeHTTP(c.req.raw) : false;
16
+ if (!authorized) {
17
+ return c.json({ error: 'Unauthorized' }, 403);
18
+ }
19
+ await next();
20
+ });
21
21
 
22
22
  router.get('/connections', (c) => {
23
23
  const agents = relay.getConnectedAgents();
@@ -31,3 +31,20 @@ describe('standalone tunnel WebSocket boundary', () => {
31
31
  expect(server.relay.getConnectedCount()).toBe(0);
32
32
  });
33
33
  });
34
+
35
+ describe('standalone tunnel HTTP routes', () => {
36
+ // Pentest: with no onAuthorizeHTTP the routes listed agents and relayed RPC to anyone.
37
+ test('fail closed when no HTTP authorizer is configured', async () => {
38
+ server = startTunnelServer({ port: 0 });
39
+ const base = `http://127.0.0.1:${server.port}`;
40
+ expect((await fetch(`${base}/connections`)).status).toBe(403);
41
+ expect((await fetch(`${base}/rpc/00000000-0000-4000-8000-000000000001`, { method: 'POST', body: '{}' })).status).toBe(403);
42
+ });
43
+
44
+ test('serve a caller the authorizer accepts', async () => {
45
+ server = startTunnelServer({ port: 0, onAuthorizeHTTP: async (req) => req.headers.get('authorization') === 'Bearer ok' });
46
+ const base = `http://127.0.0.1:${server.port}`;
47
+ expect((await fetch(`${base}/connections`, { headers: { authorization: 'Bearer ok' } })).status).toBe(200);
48
+ expect((await fetch(`${base}/connections`)).status).toBe(403);
49
+ });
50
+ });
@@ -200,7 +200,7 @@ export interface TunnelServerConfig {
200
200
  ) => Promise<boolean>;
201
201
  /**
202
202
  * Called before handling HTTP requests to relay routes (/connections, /rpc).
203
- * Return false to deny. If not provided, routes are open.
203
+ * Return false to deny. If not provided, every request is denied (403).
204
204
  */
205
205
  onAuthorizeHTTP?: (req: Request) => Promise<boolean>;
206
206
  }