@vibedeckx/linux-x64 0.3.33 → 0.3.34
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/bin.js +139 -51
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -240344,7 +240344,7 @@ ${opts.reviewFocus}` : null,
|
|
|
240344
240344
|
## Scope \u2014 the change under review
|
|
240345
240345
|
|
|
240346
240346
|
The reviewed turn changed exactly these files:
|
|
240347
|
-
${scope.changedFiles.map((f2) => `-
|
|
240347
|
+
${scope.changedFiles.map((f2) => `- \`${f2}\``).join("\n")}
|
|
240348
240348
|
|
|
240349
240349
|
It starts from commit \`${scope.startHead}\` \u2014 use \`git diff ${scope.startHead} -- <file>\` and \`git log ${scope.startHead}..HEAD\` to see the content.
|
|
240350
240350
|
|
|
@@ -263527,6 +263527,10 @@ var MACHINE_KEY_SETTING = "reverse_machine_private_key";
|
|
|
263527
263527
|
var RECONNECT_BASE_DELAY_MS = 1e3;
|
|
263528
263528
|
var RECONNECT_MAX_DELAY_MS = 3e4;
|
|
263529
263529
|
var NO_PING_TIMEOUT_MS = 4e4;
|
|
263530
|
+
var HANDSHAKE_TIMEOUT_MS = 2e4;
|
|
263531
|
+
var SUPERVISOR_INTERVAL_MS = 1e4;
|
|
263532
|
+
var DIAL_TIMEOUT_MS = 25e3;
|
|
263533
|
+
var CLOSING_TIMEOUT_MS = 1e4;
|
|
263530
263534
|
var AUTH_REJECT_CODES = /* @__PURE__ */ new Set([4001, 4003]);
|
|
263531
263535
|
var HEALTHY_CONNECTION_MS = 5e3;
|
|
263532
263536
|
function isTextualContentType(contentType2) {
|
|
@@ -263544,7 +263548,13 @@ var ReverseConnectClient = class {
|
|
|
263544
263548
|
reconnectAttempt = 0;
|
|
263545
263549
|
openedAt = null;
|
|
263546
263550
|
reconnectTimer = null;
|
|
263547
|
-
|
|
263551
|
+
supervisorTimer = null;
|
|
263552
|
+
/** When the current dial started — the CONNECTING watchdog's reference point. */
|
|
263553
|
+
dialStartedAt = 0;
|
|
263554
|
+
/** Last time the hub said anything on the control socket (a superset of pings). */
|
|
263555
|
+
lastFrameAt = 0;
|
|
263556
|
+
/** First tick that observed CLOSING, so a stalled teardown can be forced. */
|
|
263557
|
+
closingSince = 0;
|
|
263548
263558
|
shuttingDown = false;
|
|
263549
263559
|
constructor(localServer, serverUrl, token, localPort) {
|
|
263550
263560
|
this.localServer = localServer;
|
|
@@ -263554,27 +263564,42 @@ var ReverseConnectClient = class {
|
|
|
263554
263564
|
}
|
|
263555
263565
|
connect() {
|
|
263556
263566
|
if (this.shuttingDown) return;
|
|
263567
|
+
this.startSupervisor();
|
|
263568
|
+
if (this.ws) {
|
|
263569
|
+
console.warn("[ReverseClient] connect() called with a live socket \u2014 discarding the old one");
|
|
263570
|
+
const stale = this.ws;
|
|
263571
|
+
this.ws = null;
|
|
263572
|
+
try {
|
|
263573
|
+
stale.terminate();
|
|
263574
|
+
} catch {
|
|
263575
|
+
}
|
|
263576
|
+
}
|
|
263557
263577
|
const cleanUrl = this.serverUrl.replace(/\/+$/, "");
|
|
263558
263578
|
const wsProtocol = cleanUrl.startsWith("https") ? "wss" : "ws";
|
|
263559
263579
|
const wsUrl = cleanUrl.replace(/^https?/, wsProtocol);
|
|
263560
263580
|
const connectUrl = `${wsUrl}/api/reverse-connect?token=${encodeURIComponent(this.token)}`;
|
|
263561
263581
|
console.log(`[ReverseClient] Connecting to ${cleanUrl}...`);
|
|
263562
|
-
this.
|
|
263563
|
-
|
|
263564
|
-
|
|
263565
|
-
|
|
263582
|
+
this.dialStartedAt = Date.now();
|
|
263583
|
+
this.closingSince = 0;
|
|
263584
|
+
const ws = new wrapper_default(connectUrl, {
|
|
263585
|
+
maxPayload: 11 * 1024 * 1024,
|
|
263586
|
+
handshakeTimeout: HANDSHAKE_TIMEOUT_MS
|
|
263587
|
+
});
|
|
263588
|
+
this.ws = ws;
|
|
263589
|
+
ws.on("open", () => {
|
|
263566
263590
|
console.log("[ReverseClient] Socket open, awaiting server handshake");
|
|
263567
263591
|
this.openedAt = Date.now();
|
|
263568
|
-
this.
|
|
263592
|
+
this.lastFrameAt = Date.now();
|
|
263569
263593
|
const frame = {
|
|
263570
263594
|
type: "status",
|
|
263571
263595
|
ready: true,
|
|
263572
263596
|
version: readPackageVersion(),
|
|
263573
263597
|
capabilities: WORKER_CAPABILITY_KEYS
|
|
263574
263598
|
};
|
|
263575
|
-
|
|
263599
|
+
ws.send(JSON.stringify(frame));
|
|
263576
263600
|
});
|
|
263577
|
-
|
|
263601
|
+
ws.on("message", (data) => {
|
|
263602
|
+
this.lastFrameAt = Date.now();
|
|
263578
263603
|
try {
|
|
263579
263604
|
const frame = JSON.parse(data.toString());
|
|
263580
263605
|
this.handleFrame(frame);
|
|
@@ -263582,48 +263607,127 @@ var ReverseConnectClient = class {
|
|
|
263582
263607
|
console.error("[ReverseClient] Failed to parse frame:", err);
|
|
263583
263608
|
}
|
|
263584
263609
|
});
|
|
263585
|
-
|
|
263610
|
+
ws.on("close", (code, reason) => {
|
|
263586
263611
|
const safeReason = redactSecretForms(reason?.toString() || "", this.token);
|
|
263587
|
-
|
|
263588
|
-
const uptime = this.openedAt === null ? 0 : Date.now() - this.openedAt;
|
|
263589
|
-
if (rejected) {
|
|
263590
|
-
console.error(
|
|
263591
|
-
`[ReverseClient] Server rejected this connection (code=${code}, reason=${safeReason}). ` + (code === 4001 ? "The connect token is no longer valid \u2014 open Settings \u2192 Remote Servers, read the current token, and re-run `vibedeckx connect` with it." : "This machine's identity was refused \u2014 the remote record may belong to another machine or another account.") + " Retrying with backoff, but it will not recover on its own."
|
|
263592
|
-
);
|
|
263593
|
-
} else {
|
|
263594
|
-
console.log(`[ReverseClient] Disconnected (code=${code}, reason=${safeReason})`);
|
|
263595
|
-
}
|
|
263596
|
-
if (!rejected && this.openedAt !== null && uptime >= HEALTHY_CONNECTION_MS) {
|
|
263597
|
-
this.reconnectAttempt = 0;
|
|
263598
|
-
}
|
|
263599
|
-
this.openedAt = null;
|
|
263600
|
-
this.clearNoPingTimer();
|
|
263601
|
-
this.closeAllLocalChannels();
|
|
263602
|
-
void this.localServer.remoteMcpSessionManager?.closeAll("reverse-connect disconnected");
|
|
263603
|
-
this.ws = null;
|
|
263604
|
-
if (!this.shuttingDown) {
|
|
263605
|
-
this.scheduleReconnect();
|
|
263606
|
-
}
|
|
263612
|
+
this.handleDisconnect(ws, `code=${code}, reason=${safeReason}`, code);
|
|
263607
263613
|
});
|
|
263608
|
-
|
|
263614
|
+
ws.on("error", (err) => {
|
|
263615
|
+
if (ws !== this.ws) return;
|
|
263609
263616
|
console.error(
|
|
263610
263617
|
"[ReverseClient] WebSocket error:",
|
|
263611
263618
|
redactErrorSecret(err, this.token)
|
|
263612
263619
|
);
|
|
263613
263620
|
});
|
|
263614
263621
|
}
|
|
263622
|
+
/**
|
|
263623
|
+
* The single "this connection is over" funnel. Reached from the close event,
|
|
263624
|
+
* from the supervisor declaring a socket dead, or from both in that order —
|
|
263625
|
+
* so it keys on socket identity and schedules at most one reconnect per
|
|
263626
|
+
* connection.
|
|
263627
|
+
*/
|
|
263628
|
+
handleDisconnect(ws, reason, code) {
|
|
263629
|
+
if (ws !== null && ws !== this.ws) return;
|
|
263630
|
+
const rejected = code !== void 0 && AUTH_REJECT_CODES.has(code);
|
|
263631
|
+
const uptime = this.openedAt === null ? 0 : Date.now() - this.openedAt;
|
|
263632
|
+
if (rejected) {
|
|
263633
|
+
console.error(
|
|
263634
|
+
`[ReverseClient] Server rejected this connection (${reason}). ` + (code === 4001 ? "The connect token is no longer valid \u2014 open Settings \u2192 Remote Servers, read the current token, and re-run `vibedeckx connect` with it." : "This machine's identity was refused \u2014 the remote record may belong to another machine or another account.") + " Retrying with backoff, but it will not recover on its own."
|
|
263635
|
+
);
|
|
263636
|
+
} else {
|
|
263637
|
+
console.log(`[ReverseClient] Disconnected (${reason})`);
|
|
263638
|
+
}
|
|
263639
|
+
if (!rejected && this.openedAt !== null && uptime >= HEALTHY_CONNECTION_MS) {
|
|
263640
|
+
this.reconnectAttempt = 0;
|
|
263641
|
+
}
|
|
263642
|
+
this.openedAt = null;
|
|
263643
|
+
this.closingSince = 0;
|
|
263644
|
+
this.closeAllLocalChannels();
|
|
263645
|
+
void this.localServer.remoteMcpSessionManager?.closeAll("reverse-connect disconnected");
|
|
263646
|
+
this.ws = null;
|
|
263647
|
+
if (ws) {
|
|
263648
|
+
try {
|
|
263649
|
+
ws.terminate();
|
|
263650
|
+
} catch {
|
|
263651
|
+
}
|
|
263652
|
+
}
|
|
263653
|
+
if (this.shuttingDown) return;
|
|
263654
|
+
if (this.reconnectTimer) return;
|
|
263655
|
+
this.scheduleReconnect();
|
|
263656
|
+
}
|
|
263657
|
+
startSupervisor() {
|
|
263658
|
+
if (this.supervisorTimer) return;
|
|
263659
|
+
this.supervisorTimer = setInterval(() => this.superviseTick(), SUPERVISOR_INTERVAL_MS);
|
|
263660
|
+
this.supervisorTimer.unref?.();
|
|
263661
|
+
}
|
|
263662
|
+
stopSupervisor() {
|
|
263663
|
+
if (!this.supervisorTimer) return;
|
|
263664
|
+
clearInterval(this.supervisorTimer);
|
|
263665
|
+
this.supervisorTimer = null;
|
|
263666
|
+
}
|
|
263667
|
+
/**
|
|
263668
|
+
* Level-triggered recovery: judges the current state against the wall clock
|
|
263669
|
+
* rather than waiting to be armed by an event. That is what survives a dial
|
|
263670
|
+
* that never completes, a close frame the peer never answers, and a host that
|
|
263671
|
+
* was suspended for hours.
|
|
263672
|
+
*/
|
|
263673
|
+
superviseTick() {
|
|
263674
|
+
if (this.shuttingDown) return;
|
|
263675
|
+
const now3 = Date.now();
|
|
263676
|
+
const ws = this.ws;
|
|
263677
|
+
if (ws === null) {
|
|
263678
|
+
if (!this.reconnectTimer) {
|
|
263679
|
+
console.warn("[ReverseClient] Supervisor: no socket and no pending reconnect \u2014 self-healing");
|
|
263680
|
+
this.scheduleReconnect();
|
|
263681
|
+
}
|
|
263682
|
+
return;
|
|
263683
|
+
}
|
|
263684
|
+
switch (ws.readyState) {
|
|
263685
|
+
case wrapper_default.CONNECTING: {
|
|
263686
|
+
const stuckFor = now3 - this.dialStartedAt;
|
|
263687
|
+
if (stuckFor > DIAL_TIMEOUT_MS) {
|
|
263688
|
+
console.warn(
|
|
263689
|
+
`[ReverseClient] Supervisor: dial stuck ${Math.round(stuckFor / 1e3)}s with no handshake \u2014 terminating`
|
|
263690
|
+
);
|
|
263691
|
+
this.handleDisconnect(ws, "dial timeout");
|
|
263692
|
+
}
|
|
263693
|
+
break;
|
|
263694
|
+
}
|
|
263695
|
+
case wrapper_default.OPEN: {
|
|
263696
|
+
const silentFor = now3 - this.lastFrameAt;
|
|
263697
|
+
if (silentFor > NO_PING_TIMEOUT_MS) {
|
|
263698
|
+
console.warn(
|
|
263699
|
+
`[ReverseClient] Supervisor: no hub traffic for ${Math.round(silentFor / 1e3)}s \u2014 terminating`
|
|
263700
|
+
);
|
|
263701
|
+
this.handleDisconnect(ws, "no ping timeout");
|
|
263702
|
+
}
|
|
263703
|
+
break;
|
|
263704
|
+
}
|
|
263705
|
+
case wrapper_default.CLOSING: {
|
|
263706
|
+
if (this.closingSince === 0) {
|
|
263707
|
+
this.closingSince = now3;
|
|
263708
|
+
} else if (now3 - this.closingSince > CLOSING_TIMEOUT_MS) {
|
|
263709
|
+
console.warn("[ReverseClient] Supervisor: close handshake stalled \u2014 terminating");
|
|
263710
|
+
this.handleDisconnect(ws, "closing stalled");
|
|
263711
|
+
}
|
|
263712
|
+
break;
|
|
263713
|
+
}
|
|
263714
|
+
default:
|
|
263715
|
+
break;
|
|
263716
|
+
}
|
|
263717
|
+
}
|
|
263615
263718
|
shutdown() {
|
|
263616
263719
|
this.shuttingDown = true;
|
|
263617
|
-
this.
|
|
263720
|
+
this.stopSupervisor();
|
|
263618
263721
|
if (this.reconnectTimer) {
|
|
263619
263722
|
clearTimeout(this.reconnectTimer);
|
|
263620
263723
|
this.reconnectTimer = null;
|
|
263621
263724
|
}
|
|
263622
263725
|
this.closeAllLocalChannels();
|
|
263623
|
-
|
|
263624
|
-
this.ws.close(1e3, "Shutdown");
|
|
263625
|
-
}
|
|
263726
|
+
const ws = this.ws;
|
|
263626
263727
|
this.ws = null;
|
|
263728
|
+
if (ws && (ws.readyState === wrapper_default.OPEN || ws.readyState === wrapper_default.CONNECTING)) {
|
|
263729
|
+
ws.close(1e3, "Shutdown");
|
|
263730
|
+
}
|
|
263627
263731
|
}
|
|
263628
263732
|
async handleFrame(frame) {
|
|
263629
263733
|
switch (frame.type) {
|
|
@@ -263796,7 +263900,6 @@ var ReverseConnectClient = class {
|
|
|
263796
263900
|
}
|
|
263797
263901
|
}
|
|
263798
263902
|
handlePing(frame) {
|
|
263799
|
-
this.resetNoPingTimer();
|
|
263800
263903
|
const pong = { type: "pong", ts: frame.ts };
|
|
263801
263904
|
this.sendFrame(pong);
|
|
263802
263905
|
}
|
|
@@ -263819,21 +263922,6 @@ var ReverseConnectClient = class {
|
|
|
263819
263922
|
this.connect();
|
|
263820
263923
|
}, totalDelay);
|
|
263821
263924
|
}
|
|
263822
|
-
resetNoPingTimer() {
|
|
263823
|
-
this.clearNoPingTimer();
|
|
263824
|
-
this.noPingTimer = setTimeout(() => {
|
|
263825
|
-
console.log(`[ReverseClient] No ping received in ${NO_PING_TIMEOUT_MS / 1e3}s, reconnecting...`);
|
|
263826
|
-
if (this.ws) {
|
|
263827
|
-
this.ws.close(1e3, "No ping timeout");
|
|
263828
|
-
}
|
|
263829
|
-
}, NO_PING_TIMEOUT_MS);
|
|
263830
|
-
}
|
|
263831
|
-
clearNoPingTimer() {
|
|
263832
|
-
if (this.noPingTimer) {
|
|
263833
|
-
clearTimeout(this.noPingTimer);
|
|
263834
|
-
this.noPingTimer = null;
|
|
263835
|
-
}
|
|
263836
|
-
}
|
|
263837
263925
|
closeAllLocalChannels() {
|
|
263838
263926
|
for (const [id, ws] of this.localChannels) {
|
|
263839
263927
|
try {
|