@blue-soda/dsh-remote 0.5.3 → 0.5.4
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/README.md +2 -2
- package/dist/client.github.js +17 -12
- package/dist/client.js +17 -12
- package/dist/index.js +74 -11
- package/dist/index.js.map +2 -2
- package/dsh-plugin.json +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ Continue DeepSeek Harness sessions and experimental Codex workspaces from anothe
|
|
|
23
23
|
Add the current package version to the `web` profile, then restart Harness:
|
|
24
24
|
|
|
25
25
|
```sh
|
|
26
|
-
dsh plugin --profile web add -w @blue-soda/dsh-remote@0.5.
|
|
26
|
+
dsh plugin --profile web add -w @blue-soda/dsh-remote@0.5.4
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
### dsh-TUI Host
|
|
@@ -31,7 +31,7 @@ dsh plugin --profile web add -w @blue-soda/dsh-remote@0.5.3
|
|
|
31
31
|
Remote can also run as a Host in a terminal-only [dsh-TUI](https://github.com/ccch1mneyyy/dsh-TUI) profile:
|
|
32
32
|
|
|
33
33
|
```sh
|
|
34
|
-
dsh plugin --profile dsh-tui add -w @blue-soda/dsh-remote@0.5.
|
|
34
|
+
dsh plugin --profile dsh-tui add -w @blue-soda/dsh-remote@0.5.4
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
After starting dsh-TUI, manage Remote with `/remote`, `/remote login`, `/remote status`, and `/remote logout`.
|
package/dist/client.github.js
CHANGED
|
@@ -265,7 +265,7 @@
|
|
|
265
265
|
reconnectingAction: "Reconnecting\u2026",
|
|
266
266
|
reconnectStarted: "Reconnect requested.",
|
|
267
267
|
connectionAuthorizationExpired: "Authorization expired. Sign in again to authorize this device.",
|
|
268
|
-
connectionReplaced: "Another instance is using this Host identity.
|
|
268
|
+
connectionReplaced: "Another instance is using this Host identity. Standing by: retrying every five minutes, and taking over as soon as it leaves.",
|
|
269
269
|
connectionCredentialsBusy: "Credential refresh is locked. Stop other instances. After a crash, stop all instances before removing server-credentials.json.refresh-lock and authorizing again.",
|
|
270
270
|
connectionDeviceRevoked: "This device was removed on the Server. Sign in again to add it back.",
|
|
271
271
|
connectionOwnershipRequired: "The Server no longer recognizes this Host as an owned device.",
|
|
@@ -328,12 +328,12 @@
|
|
|
328
328
|
exitRemote: "Exit",
|
|
329
329
|
stopReconnecting: "Stop reconnecting",
|
|
330
330
|
addRemoteWorkspace: "Add remote workspace",
|
|
331
|
-
addCodexWorkspace: "Add
|
|
332
|
-
noCodexWorkspaces: "No
|
|
331
|
+
addCodexWorkspace: "Add Codex workspace",
|
|
332
|
+
noCodexWorkspaces: "No Codex workspaces yet.",
|
|
333
333
|
cancelAddWorkspace: "Cancel",
|
|
334
334
|
confirmAddWorkspace: "Add and open",
|
|
335
335
|
showAllWorkspaces: "Show all DSH workspaces",
|
|
336
|
-
showAllCodexWorkspaces: "Show all
|
|
336
|
+
showAllCodexWorkspaces: "Show all Codex workspaces",
|
|
337
337
|
remoteModeDsh: "Remote DSH \xB7 ",
|
|
338
338
|
remoteModeCodex: "Remote Codex \xB7 ",
|
|
339
339
|
remoteNetworkP2p: "P2P",
|
|
@@ -430,7 +430,7 @@
|
|
|
430
430
|
accountPasswordLogin: "Password",
|
|
431
431
|
qrLoginExpired: "This QR code expired. Refresh it to continue.",
|
|
432
432
|
refreshQrCode: "Refresh QR code",
|
|
433
|
-
codexVirtualWorkspace: "
|
|
433
|
+
codexVirtualWorkspace: "Codex virtual workspace",
|
|
434
434
|
codexVirtualSessions: "Sessions"
|
|
435
435
|
}, zh = {
|
|
436
436
|
terminalRemote: "\u8FDC\u7A0B\u7EC8\u7AEF",
|
|
@@ -539,7 +539,7 @@
|
|
|
539
539
|
reconnectingAction: "\u6B63\u5728\u91CD\u8FDE\u2026",
|
|
540
540
|
reconnectStarted: "\u5DF2\u53D1\u8D77\u91CD\u8FDE\u3002",
|
|
541
541
|
connectionAuthorizationExpired: "\u6388\u6743\u5DF2\u5931\u6548\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55\u4EE5\u91CD\u65B0\u6388\u6743\u6B64\u8BBE\u5907\u3002",
|
|
542
|
-
connectionReplaced: "\u53E6\u4E00\u5B9E\u4F8B\u6B63\u5728\u4F7F\u7528\u6B64 Host \u8EAB\u4EFD\u3002\
|
|
542
|
+
connectionReplaced: "\u53E6\u4E00\u5B9E\u4F8B\u6B63\u5728\u4F7F\u7528\u6B64 Host \u8EAB\u4EFD\u3002\u672C\u673A\u5DF2\u8FDB\u5165\u5F85\u547D\uFF1A\u6BCF 5 \u5206\u949F\u91CD\u8BD5\u4E00\u6B21\uFF0C\u5BF9\u65B9\u9000\u51FA\u540E\u4F1A\u81EA\u52A8\u63A5\u7BA1\u3002",
|
|
543
543
|
connectionCredentialsBusy: "\u51ED\u636E\u5237\u65B0\u88AB\u9501\u5B9A\uFF0C\u8BF7\u505C\u6B62\u5176\u4ED6\u5B9E\u4F8B\u3002\u82E5\u6B64\u524D\u5F02\u5E38\u9000\u51FA\uFF0C\u8BF7\u505C\u6B62\u6240\u6709\u5B9E\u4F8B\u540E\u79FB\u9664 server-credentials.json.refresh-lock \u5E76\u91CD\u65B0\u6388\u6743\u3002",
|
|
544
544
|
connectionDeviceRevoked: "\u6B64\u8BBE\u5907\u5DF2\u5728 Server \u4E0A\u88AB\u79FB\u9664\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55\u4EE5\u91CD\u65B0\u6DFB\u52A0\u3002",
|
|
545
545
|
connectionOwnershipRequired: "Server \u5DF2\u4E0D\u518D\u5C06\u6B64 Host \u8BC6\u522B\u4E3A\u5F53\u524D\u8D26\u53F7\u7684\u8BBE\u5907\u3002",
|
|
@@ -602,12 +602,12 @@
|
|
|
602
602
|
exitRemote: "\u9000\u51FA",
|
|
603
603
|
stopReconnecting: "\u505C\u6B62\u91CD\u8FDE",
|
|
604
604
|
addRemoteWorkspace: "\u6DFB\u52A0\u8FDC\u7A0B\u5DE5\u4F5C\u533A",
|
|
605
|
-
addCodexWorkspace: "\u6DFB\u52A0
|
|
606
|
-
noCodexWorkspaces: "\u8FD8\u6CA1\u6709
|
|
605
|
+
addCodexWorkspace: "\u6DFB\u52A0 Codex \u5DE5\u4F5C\u533A",
|
|
606
|
+
noCodexWorkspaces: "\u8FD8\u6CA1\u6709 Codex \u5DE5\u4F5C\u533A\u3002",
|
|
607
607
|
cancelAddWorkspace: "\u53D6\u6D88",
|
|
608
608
|
confirmAddWorkspace: "\u786E\u8BA4\u5E76\u6253\u5F00",
|
|
609
609
|
showAllWorkspaces: "\u663E\u793A\u5168\u90E8 DSH \u5DE5\u4F5C\u533A",
|
|
610
|
-
showAllCodexWorkspaces: "\u663E\u793A\u5168\u90E8
|
|
610
|
+
showAllCodexWorkspaces: "\u663E\u793A\u5168\u90E8 Codex \u5DE5\u4F5C\u533A",
|
|
611
611
|
remoteModeDsh: "\u8FDC\u7A0BDSH \xB7 ",
|
|
612
612
|
remoteModeCodex: "\u8FDC\u7A0BCodex \xB7 ",
|
|
613
613
|
remoteNetworkP2p: "P2P",
|
|
@@ -704,7 +704,7 @@
|
|
|
704
704
|
accountPasswordLogin: "\u8D26\u53F7\u5BC6\u7801",
|
|
705
705
|
qrLoginExpired: "\u4E8C\u7EF4\u7801\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u5237\u65B0\u540E\u91CD\u8BD5\u3002",
|
|
706
706
|
refreshQrCode: "\u5237\u65B0\u4E8C\u7EF4\u7801",
|
|
707
|
-
codexVirtualWorkspace: "
|
|
707
|
+
codexVirtualWorkspace: "Codex \u5DE5\u4F5C\u533A",
|
|
708
708
|
codexVirtualSessions: "Sessions"
|
|
709
709
|
};
|
|
710
710
|
function codexUnavailableKey(code) {
|
|
@@ -1514,7 +1514,12 @@
|
|
|
1514
1514
|
targetDeviceId: host.deviceId
|
|
1515
1515
|
}), codexReason, nextCodexWorkspaces = await props.control("codex.workspaces.list", {
|
|
1516
1516
|
targetDeviceId: host.deviceId
|
|
1517
|
-
}).catch(
|
|
1517
|
+
}).catch(async (reason) => {
|
|
1518
|
+
let code = typeof reason?.code == "string" ? String(reason.code) : "";
|
|
1519
|
+
return ["TRANSPORT_CLOSED", "NOT_CONNECTED", "CONNECTION_FAILED", "RPC_TIMEOUT"].includes(code) ? await props.control("codex.workspaces.list", {
|
|
1520
|
+
targetDeviceId: host.deviceId
|
|
1521
|
+
}).catch((again) => (codexReason = remoteErrorMessage(again, t), [])) : (codexReason = remoteErrorMessage(reason, t), []);
|
|
1522
|
+
}), nextStatus = await props.control("status").catch(() => {
|
|
1518
1523
|
});
|
|
1519
1524
|
return nextStatus !== void 0 && setStatus(nextStatus), setCodexNotice(codexReason), { workspaces: nextWorkspaces, codexWorkspaces: nextCodexWorkspaces, status: nextStatus };
|
|
1520
1525
|
},
|
|
@@ -2219,7 +2224,7 @@
|
|
|
2219
2224
|
)
|
|
2220
2225
|
),
|
|
2221
2226
|
notice === void 0 ? null : React.createElement("p", { className: "dshRemoteNotice", role: "status" }, notice),
|
|
2222
|
-
error === void 0 ? null : React.createElement("p", { className: "dshRemoteError", role: "alert" }, error)
|
|
2227
|
+
error === void 0 || error === codexNotice ? null : React.createElement("p", { className: "dshRemoteError", role: "alert" }, error)
|
|
2223
2228
|
)
|
|
2224
2229
|
)) : null
|
|
2225
2230
|
);
|
package/dist/client.js
CHANGED
|
@@ -265,7 +265,7 @@
|
|
|
265
265
|
reconnectingAction: "Reconnecting\u2026",
|
|
266
266
|
reconnectStarted: "Reconnect requested.",
|
|
267
267
|
connectionAuthorizationExpired: "Authorization expired. Sign in again to authorize this device.",
|
|
268
|
-
connectionReplaced: "Another instance is using this Host identity.
|
|
268
|
+
connectionReplaced: "Another instance is using this Host identity. Standing by: retrying every five minutes, and taking over as soon as it leaves.",
|
|
269
269
|
connectionCredentialsBusy: "Credential refresh is locked. Stop other instances. After a crash, stop all instances before removing server-credentials.json.refresh-lock and authorizing again.",
|
|
270
270
|
connectionDeviceRevoked: "This device was removed on the Server. Sign in again to add it back.",
|
|
271
271
|
connectionOwnershipRequired: "The Server no longer recognizes this Host as an owned device.",
|
|
@@ -328,12 +328,12 @@
|
|
|
328
328
|
exitRemote: "Exit",
|
|
329
329
|
stopReconnecting: "Stop reconnecting",
|
|
330
330
|
addRemoteWorkspace: "Add remote workspace",
|
|
331
|
-
addCodexWorkspace: "Add
|
|
332
|
-
noCodexWorkspaces: "No
|
|
331
|
+
addCodexWorkspace: "Add Codex workspace",
|
|
332
|
+
noCodexWorkspaces: "No Codex workspaces yet.",
|
|
333
333
|
cancelAddWorkspace: "Cancel",
|
|
334
334
|
confirmAddWorkspace: "Add and open",
|
|
335
335
|
showAllWorkspaces: "Show all DSH workspaces",
|
|
336
|
-
showAllCodexWorkspaces: "Show all
|
|
336
|
+
showAllCodexWorkspaces: "Show all Codex workspaces",
|
|
337
337
|
remoteModeDsh: "Remote DSH \xB7 ",
|
|
338
338
|
remoteModeCodex: "Remote Codex \xB7 ",
|
|
339
339
|
remoteNetworkP2p: "P2P",
|
|
@@ -430,7 +430,7 @@
|
|
|
430
430
|
accountPasswordLogin: "Password",
|
|
431
431
|
qrLoginExpired: "This QR code expired. Refresh it to continue.",
|
|
432
432
|
refreshQrCode: "Refresh QR code",
|
|
433
|
-
codexVirtualWorkspace: "
|
|
433
|
+
codexVirtualWorkspace: "Codex virtual workspace",
|
|
434
434
|
codexVirtualSessions: "Sessions"
|
|
435
435
|
}, zh = {
|
|
436
436
|
terminalRemote: "\u8FDC\u7A0B\u7EC8\u7AEF",
|
|
@@ -539,7 +539,7 @@
|
|
|
539
539
|
reconnectingAction: "\u6B63\u5728\u91CD\u8FDE\u2026",
|
|
540
540
|
reconnectStarted: "\u5DF2\u53D1\u8D77\u91CD\u8FDE\u3002",
|
|
541
541
|
connectionAuthorizationExpired: "\u6388\u6743\u5DF2\u5931\u6548\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55\u4EE5\u91CD\u65B0\u6388\u6743\u6B64\u8BBE\u5907\u3002",
|
|
542
|
-
connectionReplaced: "\u53E6\u4E00\u5B9E\u4F8B\u6B63\u5728\u4F7F\u7528\u6B64 Host \u8EAB\u4EFD\u3002\
|
|
542
|
+
connectionReplaced: "\u53E6\u4E00\u5B9E\u4F8B\u6B63\u5728\u4F7F\u7528\u6B64 Host \u8EAB\u4EFD\u3002\u672C\u673A\u5DF2\u8FDB\u5165\u5F85\u547D\uFF1A\u6BCF 5 \u5206\u949F\u91CD\u8BD5\u4E00\u6B21\uFF0C\u5BF9\u65B9\u9000\u51FA\u540E\u4F1A\u81EA\u52A8\u63A5\u7BA1\u3002",
|
|
543
543
|
connectionCredentialsBusy: "\u51ED\u636E\u5237\u65B0\u88AB\u9501\u5B9A\uFF0C\u8BF7\u505C\u6B62\u5176\u4ED6\u5B9E\u4F8B\u3002\u82E5\u6B64\u524D\u5F02\u5E38\u9000\u51FA\uFF0C\u8BF7\u505C\u6B62\u6240\u6709\u5B9E\u4F8B\u540E\u79FB\u9664 server-credentials.json.refresh-lock \u5E76\u91CD\u65B0\u6388\u6743\u3002",
|
|
544
544
|
connectionDeviceRevoked: "\u6B64\u8BBE\u5907\u5DF2\u5728 Server \u4E0A\u88AB\u79FB\u9664\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55\u4EE5\u91CD\u65B0\u6DFB\u52A0\u3002",
|
|
545
545
|
connectionOwnershipRequired: "Server \u5DF2\u4E0D\u518D\u5C06\u6B64 Host \u8BC6\u522B\u4E3A\u5F53\u524D\u8D26\u53F7\u7684\u8BBE\u5907\u3002",
|
|
@@ -602,12 +602,12 @@
|
|
|
602
602
|
exitRemote: "\u9000\u51FA",
|
|
603
603
|
stopReconnecting: "\u505C\u6B62\u91CD\u8FDE",
|
|
604
604
|
addRemoteWorkspace: "\u6DFB\u52A0\u8FDC\u7A0B\u5DE5\u4F5C\u533A",
|
|
605
|
-
addCodexWorkspace: "\u6DFB\u52A0
|
|
606
|
-
noCodexWorkspaces: "\u8FD8\u6CA1\u6709
|
|
605
|
+
addCodexWorkspace: "\u6DFB\u52A0 Codex \u5DE5\u4F5C\u533A",
|
|
606
|
+
noCodexWorkspaces: "\u8FD8\u6CA1\u6709 Codex \u5DE5\u4F5C\u533A\u3002",
|
|
607
607
|
cancelAddWorkspace: "\u53D6\u6D88",
|
|
608
608
|
confirmAddWorkspace: "\u786E\u8BA4\u5E76\u6253\u5F00",
|
|
609
609
|
showAllWorkspaces: "\u663E\u793A\u5168\u90E8 DSH \u5DE5\u4F5C\u533A",
|
|
610
|
-
showAllCodexWorkspaces: "\u663E\u793A\u5168\u90E8
|
|
610
|
+
showAllCodexWorkspaces: "\u663E\u793A\u5168\u90E8 Codex \u5DE5\u4F5C\u533A",
|
|
611
611
|
remoteModeDsh: "\u8FDC\u7A0BDSH \xB7 ",
|
|
612
612
|
remoteModeCodex: "\u8FDC\u7A0BCodex \xB7 ",
|
|
613
613
|
remoteNetworkP2p: "P2P",
|
|
@@ -704,7 +704,7 @@
|
|
|
704
704
|
accountPasswordLogin: "\u8D26\u53F7\u5BC6\u7801",
|
|
705
705
|
qrLoginExpired: "\u4E8C\u7EF4\u7801\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u5237\u65B0\u540E\u91CD\u8BD5\u3002",
|
|
706
706
|
refreshQrCode: "\u5237\u65B0\u4E8C\u7EF4\u7801",
|
|
707
|
-
codexVirtualWorkspace: "
|
|
707
|
+
codexVirtualWorkspace: "Codex \u5DE5\u4F5C\u533A",
|
|
708
708
|
codexVirtualSessions: "Sessions"
|
|
709
709
|
};
|
|
710
710
|
function codexUnavailableKey(code) {
|
|
@@ -1514,7 +1514,12 @@
|
|
|
1514
1514
|
targetDeviceId: host.deviceId
|
|
1515
1515
|
}), codexReason, nextCodexWorkspaces = await props.control("codex.workspaces.list", {
|
|
1516
1516
|
targetDeviceId: host.deviceId
|
|
1517
|
-
}).catch(
|
|
1517
|
+
}).catch(async (reason) => {
|
|
1518
|
+
let code = typeof reason?.code == "string" ? String(reason.code) : "";
|
|
1519
|
+
return ["TRANSPORT_CLOSED", "NOT_CONNECTED", "CONNECTION_FAILED", "RPC_TIMEOUT"].includes(code) ? await props.control("codex.workspaces.list", {
|
|
1520
|
+
targetDeviceId: host.deviceId
|
|
1521
|
+
}).catch((again) => (codexReason = remoteErrorMessage(again, t), [])) : (codexReason = remoteErrorMessage(reason, t), []);
|
|
1522
|
+
}), nextStatus = await props.control("status").catch(() => {
|
|
1518
1523
|
});
|
|
1519
1524
|
return nextStatus !== void 0 && setStatus(nextStatus), setCodexNotice(codexReason), { workspaces: nextWorkspaces, codexWorkspaces: nextCodexWorkspaces, status: nextStatus };
|
|
1520
1525
|
},
|
|
@@ -2219,7 +2224,7 @@
|
|
|
2219
2224
|
)
|
|
2220
2225
|
),
|
|
2221
2226
|
notice === void 0 ? null : React.createElement("p", { className: "dshRemoteNotice", role: "status" }, notice),
|
|
2222
|
-
error === void 0 ? null : React.createElement("p", { className: "dshRemoteError", role: "alert" }, error)
|
|
2227
|
+
error === void 0 || error === codexNotice ? null : React.createElement("p", { className: "dshRemoteError", role: "alert" }, error)
|
|
2223
2228
|
)
|
|
2224
2229
|
)) : null
|
|
2225
2230
|
);
|
package/dist/index.js
CHANGED
|
@@ -13480,7 +13480,12 @@ function resolveConfig(input2 = {}, env = process.env) {
|
|
|
13480
13480
|
enabled: parsed.reconnect !== false,
|
|
13481
13481
|
initialDelayMs,
|
|
13482
13482
|
maxDelayMs,
|
|
13483
|
-
jitter: reconnect.jitter ?? 0.2
|
|
13483
|
+
jitter: reconnect.jitter ?? 0.2,
|
|
13484
|
+
// A displaced connection is a standby: another instance holds this device identity, so it waits five
|
|
13485
|
+
// minutes - well outside the Server's 40-65 s reap window - and then tries again, taking over as soon
|
|
13486
|
+
// as that instance leaves. Giving up for good is how the device stayed offline after the winner left.
|
|
13487
|
+
// The fixture matrix needs a short interval to watch a standby take over, so an operator can override it.
|
|
13488
|
+
replacedRetryMs: reconnect.replacedRetryMs ?? (Number.isFinite(Number(env.DSH_REMOTE_RECONNECT_REPLACED_RETRY_MS)) ? Number(env.DSH_REMOTE_RECONNECT_REPLACED_RETRY_MS) : 3e5)
|
|
13484
13489
|
},
|
|
13485
13490
|
codex: {
|
|
13486
13491
|
// Off by default (2026-10-14). Carrying another agent's workspaces is a capability the user opts into, not
|
|
@@ -13516,7 +13521,7 @@ function normalizeServerUrl(value) {
|
|
|
13516
13521
|
}
|
|
13517
13522
|
|
|
13518
13523
|
// src/version.ts
|
|
13519
|
-
var PLUGIN_VERSION = "0.5.
|
|
13524
|
+
var PLUGIN_VERSION = "0.5.4";
|
|
13520
13525
|
|
|
13521
13526
|
// src/server-api.ts
|
|
13522
13527
|
var ENABLED_QR_PROVIDERS = ["github"];
|
|
@@ -14080,15 +14085,21 @@ var HostServerConnection = class {
|
|
|
14080
14085
|
}
|
|
14081
14086
|
async run() {
|
|
14082
14087
|
let delayMs = this.config.reconnect.initialDelayMs;
|
|
14088
|
+
let replacedRetryMs;
|
|
14089
|
+
let replacedAttempts = 0;
|
|
14083
14090
|
this.authRecoveryAttempted = false;
|
|
14084
14091
|
while (!this.stopped) {
|
|
14085
14092
|
try {
|
|
14086
14093
|
await this.connectOnce();
|
|
14087
14094
|
delayMs = this.config.reconnect.initialDelayMs;
|
|
14095
|
+
replacedRetryMs = void 0;
|
|
14096
|
+
replacedAttempts = 0;
|
|
14088
14097
|
} catch (error) {
|
|
14089
14098
|
const code = errorCode2(error);
|
|
14090
14099
|
if (code === "CREDENTIALS_REFRESHED") continue;
|
|
14091
14100
|
this.terminalError = code;
|
|
14101
|
+
replacedRetryMs = code === "CONNECTION_REPLACED" ? replacedAttempts === 0 ? Math.min(this.config.reconnect.replacedRetryMs, REPLACED_FIRST_RETRY_MS) : this.config.reconnect.replacedRetryMs : void 0;
|
|
14102
|
+
replacedAttempts = code === "CONNECTION_REPLACED" ? replacedAttempts + 1 : 0;
|
|
14092
14103
|
this.logger.warn("server control connection failed", {
|
|
14093
14104
|
code,
|
|
14094
14105
|
retryable: isRetryable(error),
|
|
@@ -14096,7 +14107,7 @@ var HostServerConnection = class {
|
|
|
14096
14107
|
...connectionFailureDetail(error)
|
|
14097
14108
|
});
|
|
14098
14109
|
if (TERMINAL_AUTH_ERRORS.has(code)) {
|
|
14099
|
-
this.logger.warn(code === "CONNECTION_REPLACED" ? "Another instance is using this Host identity.
|
|
14110
|
+
this.logger.warn(code === "CONNECTION_REPLACED" ? "Another instance is using this Host identity. Standing by: retrying in five minutes, and taking over as soon as it leaves." : code === "SERVER_CREDENTIALS_BUSY" ? "Credential refresh is locked. Stop other instances; after a crash, stop all instances before removing server-credentials.json.refresh-lock and authorizing again." : "Host authorization failed. Run /remote login or authorize this Host again in Remote settings.");
|
|
14100
14111
|
}
|
|
14101
14112
|
if (code === "DEVICE_REVOKED") {
|
|
14102
14113
|
try {
|
|
@@ -14114,7 +14125,7 @@ var HostServerConnection = class {
|
|
|
14114
14125
|
continue;
|
|
14115
14126
|
}
|
|
14116
14127
|
if (!this.config.reconnect.enabled) return;
|
|
14117
|
-
await this.waitBeforeRetry(delayMs);
|
|
14128
|
+
await (replacedRetryMs === void 0 ? this.waitBeforeRetry(delayMs) : this.waitBeforeRetry(replacedRetryMs, true));
|
|
14118
14129
|
if (this.reconnectRequested) {
|
|
14119
14130
|
this.reconnectRequested = false;
|
|
14120
14131
|
delayMs = this.config.reconnect.initialDelayMs;
|
|
@@ -14678,8 +14689,8 @@ var HostServerConnection = class {
|
|
|
14678
14689
|
tunnel.noise.destroy();
|
|
14679
14690
|
}
|
|
14680
14691
|
}
|
|
14681
|
-
waitBeforeRetry(baseDelay) {
|
|
14682
|
-
const spread = baseDelay * this.config.reconnect.jitter;
|
|
14692
|
+
waitBeforeRetry(baseDelay, exact = false) {
|
|
14693
|
+
const spread = exact ? 0 : baseDelay * this.config.reconnect.jitter;
|
|
14683
14694
|
const delay = Math.max(0, Math.round(baseDelay - spread + Math.random() * spread * 2));
|
|
14684
14695
|
return new Promise((resolve4) => {
|
|
14685
14696
|
const timer = setTimeout(() => {
|
|
@@ -14693,8 +14704,8 @@ var HostServerConnection = class {
|
|
|
14693
14704
|
});
|
|
14694
14705
|
}
|
|
14695
14706
|
};
|
|
14707
|
+
var REPLACED_FIRST_RETRY_MS = 65e3;
|
|
14696
14708
|
var TERMINAL_AUTH_ERRORS = /* @__PURE__ */ new Set([
|
|
14697
|
-
"CONNECTION_REPLACED",
|
|
14698
14709
|
"SERVER_CREDENTIALS_BUSY",
|
|
14699
14710
|
"ACCOUNT_AUTH_REQUIRED",
|
|
14700
14711
|
"AUTH_INVALID",
|
|
@@ -22077,9 +22088,26 @@ var ClientModeRuntime = class {
|
|
|
22077
22088
|
}
|
|
22078
22089
|
async setHostAuthorization(enabled) {
|
|
22079
22090
|
if (this.host === void 0) throw new ClientModeError("METHOD_NOT_ALLOWED", "This plugin is not running as a Host.");
|
|
22080
|
-
|
|
22081
|
-
|
|
22082
|
-
|
|
22091
|
+
if (!enabled) {
|
|
22092
|
+
await this.host.setHostControl?.(false);
|
|
22093
|
+
await this.host.pauseHostConnection?.();
|
|
22094
|
+
return this.status();
|
|
22095
|
+
}
|
|
22096
|
+
let flagError;
|
|
22097
|
+
try {
|
|
22098
|
+
await this.host.setHostControl?.(true);
|
|
22099
|
+
} catch (error) {
|
|
22100
|
+
flagError = error;
|
|
22101
|
+
}
|
|
22102
|
+
await this.host.resumeHostConnection?.();
|
|
22103
|
+
try {
|
|
22104
|
+
await this.host.setHostControl?.(true);
|
|
22105
|
+
} catch (error) {
|
|
22106
|
+
this.logger.warn("could not tell the Server that this Host accepts control; retrying when it reconnects", {
|
|
22107
|
+
code: safeErrorCode(error),
|
|
22108
|
+
...flagError === void 0 ? {} : { firstCode: safeErrorCode(flagError) }
|
|
22109
|
+
});
|
|
22110
|
+
}
|
|
22083
22111
|
return this.status();
|
|
22084
22112
|
}
|
|
22085
22113
|
async setMode(mode, targetDeviceId, signal) {
|
|
@@ -22515,6 +22543,22 @@ var ClientModeRuntime = class {
|
|
|
22515
22543
|
return;
|
|
22516
22544
|
}
|
|
22517
22545
|
const transportLost = reason !== "codex-closed";
|
|
22546
|
+
if (String(reason) === "CONNECTION_REPLACED" && this.reconnecting === void 0) {
|
|
22547
|
+
const run = this.remoteReconnectRun;
|
|
22548
|
+
this.reconnecting = { targetDeviceId, ...targetName === void 0 ? {} : { targetName }, phase: "fast" };
|
|
22549
|
+
this.logger.info("the link was closed by this client's own connection replacement; retrying in place", {
|
|
22550
|
+
targetDeviceId: shortId2(targetDeviceId),
|
|
22551
|
+
kind
|
|
22552
|
+
});
|
|
22553
|
+
const rescued = await this.quickReconnectSession(targetDeviceId, target2, transportLost, run);
|
|
22554
|
+
if (run !== this.remoteReconnectRun) return;
|
|
22555
|
+
if (rescued) {
|
|
22556
|
+
this.finishRecovery(targetDeviceId, "replacement", kind);
|
|
22557
|
+
return;
|
|
22558
|
+
}
|
|
22559
|
+
void this.reconnectRemoteSession(targetDeviceId, target2);
|
|
22560
|
+
return;
|
|
22561
|
+
}
|
|
22518
22562
|
if (this.reconnecting === void 0) {
|
|
22519
22563
|
const run = this.remoteReconnectRun;
|
|
22520
22564
|
this.reconnecting = { targetDeviceId, ...targetName === void 0 ? {} : { targetName }, phase: "fast" };
|
|
@@ -23227,7 +23271,26 @@ var ClientModeRuntime = class {
|
|
|
23227
23271
|
throw error;
|
|
23228
23272
|
}
|
|
23229
23273
|
}
|
|
23274
|
+
/**
|
|
23275
|
+
* One attempt at a time per target: the panel's refresh and the automatic reconnect can ask at the same moment, and
|
|
23276
|
+
* two control connections in flight make the Server close the older one with 4003 (`CONNECTION_REPLACED`), which
|
|
23277
|
+
* also kills the link that was being built on it - what the user saw as being kicked out of the card.
|
|
23278
|
+
*/
|
|
23279
|
+
inFlightConnect;
|
|
23230
23280
|
async connect(targetDeviceId, signal, forceRelay = false) {
|
|
23281
|
+
const key = `${targetDeviceId}|${forceRelay ? "relay" : "auto"}`;
|
|
23282
|
+
const inFlight = this.inFlightConnect;
|
|
23283
|
+
if (inFlight?.key === key) {
|
|
23284
|
+
signal?.throwIfAborted();
|
|
23285
|
+
return await inFlight.promise;
|
|
23286
|
+
}
|
|
23287
|
+
const promise = this.connectNow(targetDeviceId, signal, forceRelay).finally(() => {
|
|
23288
|
+
if (this.inFlightConnect?.promise === promise) this.inFlightConnect = void 0;
|
|
23289
|
+
});
|
|
23290
|
+
this.inFlightConnect = { key, promise };
|
|
23291
|
+
return await promise;
|
|
23292
|
+
}
|
|
23293
|
+
async connectNow(targetDeviceId, signal, forceRelay = false) {
|
|
23231
23294
|
signal?.throwIfAborted();
|
|
23232
23295
|
const progressRunId = this.connectionProgressRun + 1;
|
|
23233
23296
|
this.connectionProgressRun = progressRunId;
|
|
@@ -29994,7 +30057,7 @@ function remoteStatusLines(target2) {
|
|
|
29994
30057
|
`Authorization: ${status2.authorized ? status2.account === void 0 ? "logged in" : `logged in (${status2.account})` : "logged out"}`,
|
|
29995
30058
|
`Server connection: ${connection}`,
|
|
29996
30059
|
...status2.error === void 0 ? [] : [`Connection error: ${status2.error}`],
|
|
29997
|
-
...status2.error === "CONNECTION_REPLACED" ? ["Another instance is using this Host identity.
|
|
30060
|
+
...status2.error === "CONNECTION_REPLACED" ? ["Another instance is using this Host identity. Standing by: retrying every five minutes, and taking over as soon as it leaves."] : status2.error === "SERVER_CREDENTIALS_BUSY" ? ["Credential refresh is locked. Stop all instances before removing an orphaned server-credentials.json.refresh-lock and authorizing again."] : status2.accountRequired ? ["Authorize again with /remote login [github|zhihu]."] : [],
|
|
29998
30061
|
`Harness Remote API: ${capabilities.has("harness.api.v1") ? "available (ApiProxy)" : capabilities.has("harness.remote.v3") ? "available (Typert Remote Session V3)" : capabilities.has("harness.remote.v1") ? "available (Typert Remote)" : "unavailable"}`,
|
|
29999
30062
|
`Remote clients: ${diagnostics.activeConnections}`,
|
|
30000
30063
|
`Codex Remote: ${codex.enabled ? codex.state : "disabled"}`,
|