chore(remote-control): update OpenTunnel to 0.5.1 (#922)

Adopt the upstream fix for the hosted API's intermittent Cloudflare 525s (anomalyco/opentunnel#45): transient API failures are retried with capped backoff, errors name the HTTP status, and an unexpired saved certificate keeps serving when the check still fails. The PROXY protocol stays disabled. The adapter declares the workspace's single stable Effect 4.0.1, and the opt-in live test resolves the SDK wherever npm places it. Validated typechecks, build, 38 Remote Control tests, packaging checks, packaged Node imports and the live hosted-relay run; merged without waiting for CI at the user's request.
This commit is contained in:
Pascal André 2026-10-12 17:52:07 +02:00 • committed by GitHub
parent 887808c97e
commit 52133e2148
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 49 additions and 47 deletions

View file

@ -12,9 +12,10 @@ browser ─TLS─▶ OpenTunnel relay (forwards ciphertext) ─▶ OpenTunnel SD
- `packages/remote-tunnel` wraps `@opentunnel/client` behind CodeNomad-owned
types. The server build bundles it into `dist/remote-control/tunnel-runtime.js`
with its own Effect 4.0.x: the SDK peers `effect ^4.0.0` and imports
`effect/http`, while `@opencode/client` pins `4.0.0-rc.112`. A strict npm
install of both side by side fails with `ERESOLVE`, so the published server keeps no runtime
with the Effect copy it resolves (the SDK peers `effect ^4.0.0`; since
OpenCode 2.0.27 the workspace shares one stable 4.0.x). It was introduced
while `@opencode/client` pinned `4.0.0-rc.112`, where a strict npm install of
both side by side failed with `ERESOLVE`; the published server still keeps no runtime
dependency on the private workspace package.
- CodeNomad claims one random route (`codenomad-<hex>`, persisted) on the
device's shared `default` identity. OpenCode's `opencode pair --remote` and
@ -102,6 +103,11 @@ upstream affects Bun only.
- [anomalyco/opentunnel#34](https://github.com/anomalyco/opentunnel/pull/34):
both clients enforce the `max_conns` they advertise.
- [anomalyco/opentunnel#45](https://github.com/anomalyco/opentunnel/issues/45)
(0.5.1): API calls retry transient failures (5xx, Cloudflare 52x) with capped
backoff, errors name the HTTP status, and `connect` keeps an unexpired saved
certificate when the check still fails. CodeNomad does not enable the 0.5.0
per-route PROXY protocol: the ingress expects plain HTTP.
- Open topics: atomic identity writes with a provisioning lock, an in-process
connection target (no loopback hop, preserved peer address), per-connection
flow control, and relay-side `max_conns` enforcement.
connection target (no loopback hop, preserved peer address) and
per-connection flow control. The repository now accepts issues only.

66
package-lock.json generated
View file

@ -5192,6 +5192,33 @@
"node": ">=14"
}
},
"node_modules/@opentunnel/client": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/@opentunnel/client/-/client-0.5.1.tgz",
"integrity": "sha512-TdrdD0RCgX5vGRjLEKAsPD0vshUdjJEIUr7KEmJA3wmgbmRj5TQSHO66AMWF6REhd7UJ2ZnYQ3XNjqgnT+ghfQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@opentunnel/protocol": "0.5.1"
},
"engines": {
"bun": ">=1.4.0",
"node": ">=22"
},
"peerDependencies": {
"effect": "^4.0.0"
}
},
"node_modules/@opentunnel/protocol": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/@opentunnel/protocol/-/protocol-0.5.1.tgz",
"integrity": "sha512-8DIZNyDxEFyzl6WzVB/M7hZauCU+zFABo0iviywj6kX3yO9jFSPzqWCBBaarpjWJEOXYXaSILllJI62fSlEdeA==",
"dev": true,
"license": "MIT",
"peerDependencies": {
"effect": "^4.0.0"
}
},
"node_modules/@pinojs/redact": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz",
@ -18283,46 +18310,11 @@
"version": "0.1.0",
"license": "MIT",
"devDependencies": {
"@opentunnel/client": "0.4.1",
"effect": "4.0.2",
"@opentunnel/client": "0.5.1",
"effect": "4.0.1",
"typescript": "^5.6.3"
}
},
"packages/remote-tunnel/node_modules/@opentunnel/client": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/@opentunnel/client/-/client-0.4.1.tgz",
"integrity": "sha512-TLHCko/cSoF6LY/IBvex9s/u9vh4CB3K/S06q7Zwu0HUISikMC+j/4437Y39slF7hmx1oSpTo7AySELu7j5DQg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@opentunnel/protocol": "0.4.1"
},
"engines": {
"bun": ">=1.4.0",
"node": ">=22"
},
"peerDependencies": {
"effect": "^4.0.0"
}
},
"packages/remote-tunnel/node_modules/@opentunnel/protocol": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/@opentunnel/protocol/-/protocol-0.4.1.tgz",
"integrity": "sha512-ebAO6/H5VdeAtweqWhMBqTthTMqQfsmZRsbHJULpDLK3HGJa02L1VO4e4LnigLbIPhP/PULqPjFe5nmcMXUvNQ==",
"dev": true,
"license": "MIT",
"peerDependencies": {
"effect": "^4.0.0"
}
},
"packages/remote-tunnel/node_modules/effect": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/effect/-/effect-4.0.2.tgz",
"integrity": "sha512-YeYnLqSFrL4+tCOXRrfNP03w8FILoF/wDl+ukraJAGzXrjcS383W7LAAvGBbLatEyktmLxRq+9xJinx7QzXW5A==",
"dev": true,
"license": "MIT",
"peer": true
},
"packages/server": {
"name": "@neuralnomads/codenomad",
"version": "0.20.1",

View file

@ -12,8 +12,8 @@
"typecheck": "tsc --noEmit -p tsconfig.json"
},
"devDependencies": {
"@opentunnel/client": "0.4.1",
"effect": "4.0.2",
"@opentunnel/client": "0.5.1",
"effect": "4.0.1",
"typescript": "^5.6.3"
}
}

View file

@ -10,7 +10,8 @@ import https from "node:https"
import os from "node:os"
import path from "node:path"
import test from "node:test"
import { fileURLToPath, pathToFileURL } from "node:url"
import { createRequire } from "node:module"
import { pathToFileURL } from "node:url"
import { openRemoteTunnel } from "@codenomad/remote-tunnel"
import { startRemoteSurface } from "../src/remote-control/test-support/surface"
@ -39,8 +40,11 @@ function request(url: string, options: { method?: string; headers?: Record<strin
async function removeThrowawayTunnel(dataHome: string): Promise<void> {
// The facade never deletes identities; the qualification uses the SDK directly.
const sdk = path.resolve(fileURLToPath(import.meta.url), "../../../remote-tunnel/node_modules/@opentunnel/client/dist/promise/index.js")
const { create, OpenTunnelStorage } = await import(pathToFileURL(sdk).href)
// Resolved from the adapter package, wherever npm placed the SDK (nested or hoisted).
const lookup = createRequire(new URL("../../remote-tunnel/package.json", import.meta.url)).resolve.paths("@opentunnel/client") ?? []
const root = lookup.map((directory) => path.join(directory, "@opentunnel", "client")).find((directory) => fs.existsSync(directory))
if (!root) throw new Error("OpenTunnel SDK not found for throwaway tunnel removal")
const { create, OpenTunnelStorage } = await import(pathToFileURL(path.join(root, "dist", "promise", "index.js")).href)
const client = create({ store: OpenTunnelStorage.xdg({ env: { ...process.env, XDG_DATA_HOME: dataHome } }) })
try {
await client.tunnel.remove({ profile: PROFILE })