diff --git a/dev-docs/REMOTE_CONTROL.md b/dev-docs/REMOTE_CONTROL.md index a5eae7564..b1745f012 100644 --- a/dev-docs/REMOTE_CONTROL.md +++ b/dev-docs/REMOTE_CONTROL.md @@ -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-`, 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. diff --git a/package-lock.json b/package-lock.json index 0ec0cbd41..0fd75a566 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/packages/remote-tunnel/package.json b/packages/remote-tunnel/package.json index e67b5c972..574478500 100644 --- a/packages/remote-tunnel/package.json +++ b/packages/remote-tunnel/package.json @@ -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" } } diff --git a/packages/server/scripts/remote-control-live.test.ts b/packages/server/scripts/remote-control-live.test.ts index 0b5bc192a..a7193bc43 100644 --- a/packages/server/scripts/remote-control-live.test.ts +++ b/packages/server/scripts/remote-control-live.test.ts @@ -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 { // 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 })