esque-bridge 0.6.0 → 0.6.1
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/index.js +44 -4
- package/package.json +2 -2
package/index.js
CHANGED
|
@@ -778,6 +778,43 @@ function confirmWorkdir(dir) {
|
|
|
778
778
|
});
|
|
779
779
|
}
|
|
780
780
|
|
|
781
|
+
// Bind the local HTTP server to the first free port at/after `startPort`.
|
|
782
|
+
// EADDRINUSE on the default port almost always means a previous esque-bridge
|
|
783
|
+
// is still running (or another app grabbed 3030). Rather than crash with a raw
|
|
784
|
+
// Node stack trace, we step to the next port and tell the user. The pairing
|
|
785
|
+
// URL travels over the tunnel, so the exact local port doesn't matter to the
|
|
786
|
+
// app. Returns { server, port } for the port we actually bound.
|
|
787
|
+
function listenOnFreePort(app, startPort, maxTries = 25) {
|
|
788
|
+
return new Promise((resolve, reject) => {
|
|
789
|
+
let attempts = 0;
|
|
790
|
+
const attempt = (port) => {
|
|
791
|
+
const server = app.listen(port);
|
|
792
|
+
const onError = (err) => {
|
|
793
|
+
server.removeListener('listening', onListening);
|
|
794
|
+
if (err && err.code === 'EADDRINUSE' && attempts < maxTries) {
|
|
795
|
+
if (attempts === 0) {
|
|
796
|
+
console.error(
|
|
797
|
+
`\n Port ${startPort} is busy — another Esque bridge may already be running.`,
|
|
798
|
+
);
|
|
799
|
+
console.error(' Stepping to the next free port…');
|
|
800
|
+
}
|
|
801
|
+
attempts += 1;
|
|
802
|
+
attempt(port + 1);
|
|
803
|
+
} else {
|
|
804
|
+
reject(err);
|
|
805
|
+
}
|
|
806
|
+
};
|
|
807
|
+
const onListening = () => {
|
|
808
|
+
server.removeListener('error', onError);
|
|
809
|
+
resolve({ server, port });
|
|
810
|
+
};
|
|
811
|
+
server.once('error', onError);
|
|
812
|
+
server.once('listening', onListening);
|
|
813
|
+
};
|
|
814
|
+
attempt(startPort);
|
|
815
|
+
});
|
|
816
|
+
}
|
|
817
|
+
|
|
781
818
|
async function main() {
|
|
782
819
|
if (!fs.existsSync(WORKDIR) || !fs.statSync(WORKDIR).isDirectory()) {
|
|
783
820
|
console.error(`workdir does not exist: ${WORKDIR}`);
|
|
@@ -812,15 +849,18 @@ async function main() {
|
|
|
812
849
|
process.exit(1);
|
|
813
850
|
}
|
|
814
851
|
|
|
815
|
-
|
|
852
|
+
const { port: boundPort } = await listenOnFreePort(app, PORT);
|
|
853
|
+
if (boundPort !== PORT) {
|
|
854
|
+
console.log(` ✓ Using port ${boundPort} instead (${PORT} was taken).`);
|
|
855
|
+
}
|
|
816
856
|
|
|
817
857
|
let tunnel;
|
|
818
858
|
try {
|
|
819
|
-
tunnel = await localtunnel({ port:
|
|
859
|
+
tunnel = await localtunnel({ port: boundPort, subdomain: LT_SUBDOMAIN });
|
|
820
860
|
} catch (err) {
|
|
821
861
|
console.error('Failed to open localtunnel:', err.message);
|
|
822
862
|
console.error('If localtunnel.me is blocked, try cloudflared:');
|
|
823
|
-
console.error(` cloudflared tunnel --url http://localhost:${
|
|
863
|
+
console.error(` cloudflared tunnel --url http://localhost:${boundPort}`);
|
|
824
864
|
process.exit(1);
|
|
825
865
|
}
|
|
826
866
|
|
|
@@ -836,7 +876,7 @@ async function main() {
|
|
|
836
876
|
qrcode.generate(pairUrl, { small: true });
|
|
837
877
|
console.log('');
|
|
838
878
|
console.log(` Agent ${adapter.label} (${AGENT_TYPE})`);
|
|
839
|
-
console.log(` Local http://localhost:${
|
|
879
|
+
console.log(` Local http://localhost:${boundPort}`);
|
|
840
880
|
console.log(` Tunnel ${tunnel.url}`);
|
|
841
881
|
console.log(` Workdir ${WORKDIR}`);
|
|
842
882
|
console.log(` Binary ${AGENT_BIN ?? '(custom)'}`);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "esque-bridge",
|
|
3
|
-
"version": "0.6.
|
|
4
|
-
"description": "Desktop-side receiver for the Esque Agent mobile app. Pairs your phone with a local coding-agent CLI (Claude Code, Aider, or any custom command) via a tunnel + QR code, so prompts run through your subscription instead of per-token API billing.",
|
|
3
|
+
"version": "0.6.1",
|
|
4
|
+
"description": "Desktop-side receiver for the Esque Agent mobile app. Pairs your phone with a local coding-agent CLI (Claude Code, Codex, Aider, or any custom command) via a tunnel + QR code, so prompts run through your subscription instead of per-token API billing.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"esque-bridge": "index.js"
|
|
7
7
|
},
|