@skylandnpm/octopus-proxy 0.1.51 → 0.1.52

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.
@@ -3321,8 +3321,8 @@ var require_utils = __commonJS({
3321
3321
  var HOST_DELIMS = { "@": "%40", "/": "%2F", "?": "%3F", "#": "%23", ":": "%3A" };
3322
3322
  var HOST_DELIM_RE = /[@/?#:]/g;
3323
3323
  var HOST_DELIM_NO_COLON_RE = /[@/?#]/g;
3324
- function reescapeHostDelimiters(host, isIP) {
3325
- const re = isIP ? HOST_DELIM_NO_COLON_RE : HOST_DELIM_RE;
3324
+ function reescapeHostDelimiters(host, isIP2) {
3325
+ const re = isIP2 ? HOST_DELIM_NO_COLON_RE : HOST_DELIM_RE;
3326
3326
  re.lastIndex = 0;
3327
3327
  return host.replace(re, (ch) => HOST_DELIMS[ch]);
3328
3328
  }
@@ -4011,8 +4011,8 @@ var require_fast_uri = __commonJS({
4011
4011
  const host = matches[4];
4012
4012
  return hasMalformedPercentEncoding(matches[3]) || host !== void 0 && !isIPLiteral(host) && hasMalformedPercentEncoding(host) || hasMalformedPercentEncoding(matches[6]) || hasMalformedPercentEncoding(matches[7]) || hasMalformedPercentEncoding(matches[8]);
4013
4013
  }
4014
- function canonicalizeHost(parsed, options, schemeHandler, isIP) {
4015
- if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport) && parsed.host && !isIPLiteral(parsed.host) && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP === false && nonSimpleDomain(parsed.host)) {
4014
+ function canonicalizeHost(parsed, options, schemeHandler, isIP2) {
4015
+ if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport) && parsed.host && !isIPLiteral(parsed.host) && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP2 === false && nonSimpleDomain(parsed.host)) {
4016
4016
  try {
4017
4017
  parsed.host = new URL("http://" + parsed.host).hostname;
4018
4018
  } catch (e) {
@@ -4039,7 +4039,7 @@ var require_fast_uri = __commonJS({
4039
4039
  let malformedHost = false;
4040
4040
  let malformedIPLiteral = false;
4041
4041
  let malformedScheme = false;
4042
- let isIP = false;
4042
+ let isIP2 = false;
4043
4043
  if (options.reference === "suffix") {
4044
4044
  if (options.scheme) {
4045
4045
  uri = options.scheme + ":" + uri;
@@ -4102,15 +4102,15 @@ var require_fast_uri = __commonJS({
4102
4102
  const bracketedIPLiteral = isIPLiteral(parsed.host);
4103
4103
  const hasIPLiteralBracket = parsed.host.indexOf("[") !== -1 || parsed.host.indexOf("]") !== -1;
4104
4104
  const ipv6result = normalizeIPv6(parsed.host);
4105
- isIP = ipv6result.isIPV6 || ipv6result.isIPVFuture === true;
4105
+ isIP2 = ipv6result.isIPV6 || ipv6result.isIPVFuture === true;
4106
4106
  malformedIPLiteral = hasIPLiteralBracket && (!bracketedIPLiteral || ipv6result.error === true);
4107
- parsed.host = isIP ? ipv6result.host : ipv6result.host.toLowerCase();
4107
+ parsed.host = isIP2 ? ipv6result.host : ipv6result.host.toLowerCase();
4108
4108
  if (malformedIPLiteral) {
4109
4109
  parsed.error = parsed.error || "URI host is malformed.";
4110
4110
  malformedAuthorityOrPort = true;
4111
4111
  }
4112
4112
  } else {
4113
- isIP = true;
4113
+ isIP2 = true;
4114
4114
  }
4115
4115
  }
4116
4116
  if (parsed.scheme === void 0 && parsed.userinfo === void 0 && parsed.host === void 0 && parsed.port === void 0 && parsed.query === void 0 && !parsed.path) {
@@ -4127,14 +4127,14 @@ var require_fast_uri = __commonJS({
4127
4127
  }
4128
4128
  const schemeHandler = getSchemeHandler(options.scheme || parsed.scheme);
4129
4129
  if (!malformedIPLiteral) {
4130
- malformedHost = canonicalizeHost(parsed, options, schemeHandler, isIP);
4130
+ malformedHost = canonicalizeHost(parsed, options, schemeHandler, isIP2);
4131
4131
  }
4132
4132
  if (uri.indexOf("%") !== -1 && parsed.host !== void 0 && !malformedIPLiteral) {
4133
- let host = isIP ? parsed.host : normalizePercentEncoding(parsed.host, true);
4134
- if (!isIP) {
4133
+ let host = isIP2 ? parsed.host : normalizePercentEncoding(parsed.host, true);
4134
+ if (!isIP2) {
4135
4135
  host = normalizePercentEncoding(host.toLowerCase());
4136
4136
  }
4137
- parsed.host = reescapeHostDelimiters(host, isIP);
4137
+ parsed.host = reescapeHostDelimiters(host, isIP2);
4138
4138
  }
4139
4139
  if (!schemeHandler || schemeHandler && !schemeHandler.skipNormalize) {
4140
4140
  if (parsed.path) {
@@ -13335,6 +13335,71 @@ var PlatformDesktopSession = class {
13335
13335
  import { fork } from "node:child_process";
13336
13336
  import { randomBytes as randomBytes2, randomUUID as randomUUID16 } from "node:crypto";
13337
13337
  import { fileURLToPath as fileURLToPath4 } from "node:url";
13338
+
13339
+ // src/remote-stun.ts
13340
+ import { Resolver } from "node:dns/promises";
13341
+ import { isIP } from "node:net";
13342
+ var publicStunHost = "stun.cloudflare.com";
13343
+ var dnsJsonEndpoints = ["https://dns.alidns.com/resolve", "https://cloudflare-dns.com/dns-query"];
13344
+ var STUN_DNS_TIMEOUT = 1500;
13345
+ function defaultRemoteStunUrls(hubUrl) {
13346
+ return [.../* @__PURE__ */ new Set([`stun:${new URL(hubUrl).hostname}:3478`, `stun:${publicStunHost}:3478`])];
13347
+ }
13348
+ function publicAddress(address, family) {
13349
+ if (isIP(address) !== family) return false;
13350
+ if (family === 6) return /^[23][\da-f]{0,3}:/i.test(address);
13351
+ const [a, b] = address.split(".").map(Number);
13352
+ return a !== 0 && a !== 10 && a !== 127 && a < 224 && !(a === 100 && b >= 64 && b <= 127) && !(a === 169 && b === 254) && !(a === 172 && b >= 16 && b <= 31) && !(a === 192 && b === 168) && !(a === 198 && (b === 18 || b === 19));
13353
+ }
13354
+ async function systemLookup(family) {
13355
+ const resolver = new Resolver({ timeout: 500, tries: 1 });
13356
+ try {
13357
+ return await (family === 6 ? resolver.resolve6(publicStunHost) : resolver.resolve4(publicStunHost));
13358
+ } finally {
13359
+ resolver.cancel();
13360
+ }
13361
+ }
13362
+ async function queryDnsJson(endpoint, family, signal) {
13363
+ const type = family === 6 ? 28 : 1;
13364
+ const url = new URL(endpoint);
13365
+ url.searchParams.set("name", publicStunHost);
13366
+ url.searchParams.set("type", String(type));
13367
+ const response = await fetch(url, { headers: { accept: "application/dns-json" }, signal, redirect: "error" });
13368
+ if (!response.ok) throw new Error("STUN DNS request failed");
13369
+ const text = await response.text();
13370
+ if (text.length > 16384) throw new Error("STUN DNS response too large");
13371
+ const body = JSON.parse(text);
13372
+ if (body.Status !== 0 || !Array.isArray(body.Answer)) throw new Error("STUN DNS answer unavailable");
13373
+ return body.Answer.filter((record2) => record2.type === type && typeof record2.data === "string").map((record2) => record2.data);
13374
+ }
13375
+ async function resolvePublicStunAddresses(lookup = systemLookup, query = queryDnsJson) {
13376
+ const families = await Promise.all([6, 4].map(async (family) => {
13377
+ const valid = (addresses) => addresses.filter((address) => publicAddress(address, family)).slice(0, 1);
13378
+ const native = valid(await lookup(family).catch(() => []));
13379
+ if (native.length) return native;
13380
+ const controller = new AbortController();
13381
+ const timer = setTimeout(() => controller.abort(), STUN_DNS_TIMEOUT);
13382
+ try {
13383
+ return await Promise.any(dnsJsonEndpoints.map(async (endpoint) => {
13384
+ const addresses = valid(await query(endpoint, family, controller.signal));
13385
+ if (!addresses.length) throw new Error("No public STUN address");
13386
+ return addresses;
13387
+ })).catch(() => []);
13388
+ } finally {
13389
+ clearTimeout(timer);
13390
+ controller.abort();
13391
+ }
13392
+ }));
13393
+ return families.flat();
13394
+ }
13395
+ async function discoverRemoteStunUrls(hubUrl, configured, resolve5 = resolvePublicStunAddresses) {
13396
+ if (configured) return [...configured];
13397
+ const addresses = await resolve5().catch(() => []);
13398
+ const numeric = addresses.map((address) => `stun:${isIP(address) === 6 ? `[${address}]` : address}:3478`);
13399
+ return [.../* @__PURE__ */ new Set([...numeric, ...defaultRemoteStunUrls(hubUrl)])].slice(0, 4);
13400
+ }
13401
+
13402
+ // src/remote-manager.ts
13338
13403
  function spawnWorker(message) {
13339
13404
  const worker = fileURLToPath4(new URL(import.meta.url.endsWith(".ts") ? "./remote-worker.ts" : "./remote-worker.js", import.meta.url));
13340
13405
  const child = fork(worker, [], { stdio: ["ignore", "ignore", "ignore", "ipc"], execArgv: import.meta.url.endsWith(".ts") ? ["--import", "tsx"] : [] });
@@ -13342,7 +13407,7 @@ function spawnWorker(message) {
13342
13407
  return child;
13343
13408
  }
13344
13409
  var RemoteManager = class {
13345
- constructor(host, desktop, desktopSession, send, touch, workerFactory = spawnWorker, probe = probeRemote) {
13410
+ constructor(host, desktop, desktopSession, send, touch, workerFactory = spawnWorker, probe = probeRemote, discoverStun = discoverRemoteStunUrls) {
13346
13411
  this.host = host;
13347
13412
  this.desktop = desktop;
13348
13413
  this.desktopSession = desktopSession;
@@ -13350,13 +13415,14 @@ var RemoteManager = class {
13350
13415
  this.touch = touch;
13351
13416
  this.workerFactory = workerFactory;
13352
13417
  this.probe = probe;
13418
+ this.discoverStun = discoverStun;
13353
13419
  this.config = RemoteConfigSchema.parse(host.desktopRemote ?? {});
13354
13420
  }
13355
13421
  current;
13356
13422
  stopping = false;
13357
13423
  config;
13358
13424
  stunUrls() {
13359
- return this.config.stunUrls ?? [`stun:${new URL(this.host.hubUrl).hostname}:3478`];
13425
+ return this.config.stunUrls ?? defaultRemoteStunUrls(this.host.hubUrl);
13360
13426
  }
13361
13427
  async status() {
13362
13428
  const base = { qualityModes: RemoteQualitySchema.options, platform: process.platform, passwordInput: process.platform === "darwin", enabled: this.config.enabled, busy: !!this.current, stunUrls: this.stunUrls(), budget: { ...REMOTE_DEFAULTS, totalBitrate: this.config.totalBitrate, videoMaxBitrate: this.config.videoMaxBitrate } };
@@ -13404,7 +13470,10 @@ var RemoteManager = class {
13404
13470
  const session = { remoteId: randomUUID16(), owner, closed: false, signaling: Promise.resolve(), signals: 0, signalWindow: Date.now(), candidates: 0 };
13405
13471
  this.current = session;
13406
13472
  try {
13407
- const permissions = await this.probe(this.host.stateDirectory, this.config);
13473
+ const [permissions, stunUrls] = await Promise.all([
13474
+ this.probe(this.host.stateDirectory, this.config),
13475
+ this.discoverStun(this.host.hubUrl, this.config.stunUrls)
13476
+ ]);
13408
13477
  if (!permissions.session || !permissions.capture || !permissions.input) throw new Error("\u8FDC\u63A7\u9700\u8981\u4E3B\u673A\u56FE\u5F62\u4F1A\u8BDD\u3001\u5C4F\u5E55\u5F55\u5236\u53CA\u8F85\u52A9\u529F\u80FD\u6743\u9650");
13409
13478
  if (permissions.locked === true && permissions.lockCapture === false) throw new Error("\u9501\u5C4F\u8FDC\u63A7\u9700\u8981 macOS 14 \u53CA\u4EE5\u4E0A\u53CA\u65B0\u7248 Xcode Command Line Tools\uFF1B\u5F53\u524D\u4E3B\u673A\u8BF7\u5148\u672C\u5730\u89E3\u9501");
13410
13479
  const identity = await this.desktopSession.sessionIdentity();
@@ -13413,7 +13482,7 @@ var RemoteManager = class {
13413
13482
  const layout = await this.desktop.layoutIdentity?.(display.id);
13414
13483
  if (session.closed || this.current !== session) throw new Error("\u5DE5\u4F5C\u533A\u8FDE\u63A5\u5DF2\u5173\u95ED\uFF0C\u8FDC\u63A7\u5DF2\u53D6\u6D88");
13415
13484
  const bindToken = randomBytes2(32).toString("base64url"), expiresAt = Date.now() + 2e4;
13416
- const child = this.workerFactory({ remoteId: session.remoteId, bindToken, expiresAt, display, helper: permissions.helper, quality, legacy: payload.quality === void 0, config: this.config, stunUrls: this.stunUrls() });
13485
+ const child = this.workerFactory({ remoteId: session.remoteId, bindToken, expiresAt, display, helper: permissions.helper, quality, legacy: payload.quality === void 0, config: this.config, stunUrls });
13417
13486
  session.child = child;
13418
13487
  child.on("message", (message) => {
13419
13488
  if (this.current !== session || session.closed || !message || typeof message !== "object") return;
@@ -13447,7 +13516,7 @@ var RemoteManager = class {
13447
13516
  }
13448
13517
  }, 1e3);
13449
13518
  session.monitor.unref();
13450
- return { remoteId: session.remoteId, bindToken, expiresAt: new Date(expiresAt).toISOString(), display, revision: 1, stunUrls: this.stunUrls(), budget, quality, qualityModes: RemoteQualitySchema.options, protocolVersion: 1, heartbeatAck: true };
13519
+ return { remoteId: session.remoteId, bindToken, expiresAt: new Date(expiresAt).toISOString(), display, revision: 1, stunUrls, budget, quality, qualityModes: RemoteQualitySchema.options, protocolVersion: 1, heartbeatAck: true };
13451
13520
  } catch (error) {
13452
13521
  this.finish(session);
13453
13522
  throw error;
package/dist/cli.js CHANGED
@@ -31,7 +31,7 @@ import {
31
31
  supportedPlatform,
32
32
  systemdUnitPath,
33
33
  toolSearchDirs
34
- } from "./chunk-55TEBVWY.js";
34
+ } from "./chunk-3KIA6HF6.js";
35
35
  import {
36
36
  DESKTOP_PERMISSION_GUIDANCE,
37
37
  PlatformDesktop,
package/dist/main.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  ConfigError,
4
4
  runDaemon
5
- } from "./chunk-55TEBVWY.js";
5
+ } from "./chunk-3KIA6HF6.js";
6
6
  import "./chunk-XIJKEY4L.js";
7
7
 
8
8
  // src/main.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skylandnpm/octopus-proxy",
3
- "version": "0.1.51",
3
+ "version": "0.1.52",
4
4
  "type": "module",
5
5
  "description": "Octopus host proxy: OpenCode, Codex, ZCode and Pi adapters with service installer CLI",
6
6
  "bin": {
@@ -7,7 +7,6 @@
7
7
  "tool": { "kind": "opencode", "command": "/Users/luhuili/.opencode/bin/opencode", "baseUrl": "http://127.0.0.1:4096", "mode": "external", "password": "replace-with-a-random-secret-at-least-20-characters" },
8
8
  "desktopRemote": {
9
9
  "enabled": true,
10
- "stunUrls": ["stun:hub.example.com:3478"],
11
10
  "totalBitrate": 22000000,
12
11
  "videoMaxBitrate": 16000000
13
12
  }