@blue-soda/dsh-remote 0.5.2 → 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 +187 -30
- package/dist/index.js.map +3 -3
- 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.2
|
|
|
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
|
@@ -13162,7 +13162,7 @@ function fromBase64Url2(value) {
|
|
|
13162
13162
|
import { platform } from "node:os";
|
|
13163
13163
|
|
|
13164
13164
|
// src/server-credentials.ts
|
|
13165
|
-
import { chmod, mkdir, readFile, rm as rm2, stat as stat2, writeFile } from "node:fs/promises";
|
|
13165
|
+
import { chmod, mkdir, readFile, rename as rename2, rm as rm2, stat as stat2, writeFile } from "node:fs/promises";
|
|
13166
13166
|
import { dirname, join as join2 } from "node:path";
|
|
13167
13167
|
|
|
13168
13168
|
// src/atomic-file.ts
|
|
@@ -13237,6 +13237,11 @@ function uuidV7(now = Date.now()) {
|
|
|
13237
13237
|
}
|
|
13238
13238
|
|
|
13239
13239
|
// src/server-credentials.ts
|
|
13240
|
+
var LOCK_ABANDONED_MS = 10 * 6e4;
|
|
13241
|
+
var lockOwnerSchema = external_exports.object({
|
|
13242
|
+
pid: external_exports.number().int().positive(),
|
|
13243
|
+
at: external_exports.number().int().nonnegative()
|
|
13244
|
+
});
|
|
13240
13245
|
var credentialSchema = external_exports.object({
|
|
13241
13246
|
schemaVersion: external_exports.literal(1),
|
|
13242
13247
|
serverUrl: external_exports.string().url(),
|
|
@@ -13254,8 +13259,13 @@ var ServerCredentialStore = class {
|
|
|
13254
13259
|
this.path = join2(directory, "server-credentials.json");
|
|
13255
13260
|
}
|
|
13256
13261
|
/** Serialize the complete read/refresh/write transaction across processes.
|
|
13257
|
-
*
|
|
13258
|
-
*
|
|
13262
|
+
*
|
|
13263
|
+
* A lock left by a process that no longer exists can never be released by that process, and being killed
|
|
13264
|
+
* mid-refresh is routine on a phone (the app is backgrounded or swept away) - the old rule then left the user
|
|
13265
|
+
* with `SERVER_CREDENTIALS_BUSY` forever and told them to delete a file they cannot reach. So the lock now
|
|
13266
|
+
* records its owner and a contender reclaims it when that owner is **gone**. Liveness is what is checked, not
|
|
13267
|
+
* age: an owner that is merely suspended still protects its one-use refresh token, which is exactly what the
|
|
13268
|
+
* old unconditional rule was guarding against.
|
|
13259
13269
|
*/
|
|
13260
13270
|
async withRefreshLock(operation) {
|
|
13261
13271
|
const lock = `${this.path}.refresh-lock`;
|
|
@@ -13263,10 +13273,11 @@ var ServerCredentialStore = class {
|
|
|
13263
13273
|
const deadline = Date.now() + 15e3;
|
|
13264
13274
|
for (; ; ) {
|
|
13265
13275
|
try {
|
|
13266
|
-
await
|
|
13276
|
+
await claimRefreshLock(lock);
|
|
13267
13277
|
break;
|
|
13268
13278
|
} catch (error) {
|
|
13269
13279
|
if (!(error instanceof Error) || !("code" in error) || error.code !== "EEXIST") throw error;
|
|
13280
|
+
if (await reclaimAbandonedRefreshLock(lock)) continue;
|
|
13270
13281
|
if (Date.now() >= deadline) throw new ServerCredentialsBusyError();
|
|
13271
13282
|
await new Promise((resolve4) => setTimeout(resolve4, 50));
|
|
13272
13283
|
}
|
|
@@ -13274,7 +13285,7 @@ var ServerCredentialStore = class {
|
|
|
13274
13285
|
try {
|
|
13275
13286
|
return await operation();
|
|
13276
13287
|
} finally {
|
|
13277
|
-
await rm2(lock, { recursive: true });
|
|
13288
|
+
await rm2(lock, { recursive: true, force: true });
|
|
13278
13289
|
}
|
|
13279
13290
|
}
|
|
13280
13291
|
async load(serverUrl, deviceId) {
|
|
@@ -13307,6 +13318,49 @@ var ServerCredentialsBusyError = class extends Error {
|
|
|
13307
13318
|
super("Credential refresh is locked. Stop other instances; after a crash, stop all instances before removing server-credentials.json.refresh-lock and authorizing again.");
|
|
13308
13319
|
}
|
|
13309
13320
|
};
|
|
13321
|
+
async function claimRefreshLock(lock) {
|
|
13322
|
+
await mkdir(lock, { mode: 448 });
|
|
13323
|
+
await writeFile(join2(lock, "owner.json"), `${JSON.stringify({ pid: process.pid, at: Date.now() })}
|
|
13324
|
+
`, {
|
|
13325
|
+
encoding: "utf8",
|
|
13326
|
+
mode: 384
|
|
13327
|
+
});
|
|
13328
|
+
}
|
|
13329
|
+
async function reclaimAbandonedRefreshLock(lock) {
|
|
13330
|
+
const owner = await readRefreshLockOwner(lock);
|
|
13331
|
+
const abandoned = owner === void 0 ? await olderThan(lock, LOCK_ABANDONED_MS) : !processAlive(owner.pid);
|
|
13332
|
+
if (!abandoned) return false;
|
|
13333
|
+
const tomb = `${lock}.${process.pid}.${uuidV7()}.abandoned`;
|
|
13334
|
+
try {
|
|
13335
|
+
await rename2(lock, tomb);
|
|
13336
|
+
} catch {
|
|
13337
|
+
return false;
|
|
13338
|
+
}
|
|
13339
|
+
await rm2(tomb, { recursive: true, force: true });
|
|
13340
|
+
return true;
|
|
13341
|
+
}
|
|
13342
|
+
async function readRefreshLockOwner(lock) {
|
|
13343
|
+
try {
|
|
13344
|
+
return lockOwnerSchema.parse(JSON.parse(await readFile(join2(lock, "owner.json"), "utf8")));
|
|
13345
|
+
} catch {
|
|
13346
|
+
return void 0;
|
|
13347
|
+
}
|
|
13348
|
+
}
|
|
13349
|
+
function processAlive(pid) {
|
|
13350
|
+
try {
|
|
13351
|
+
process.kill(pid, 0);
|
|
13352
|
+
return true;
|
|
13353
|
+
} catch (error) {
|
|
13354
|
+
return !(error instanceof Error && "code" in error && error.code === "ESRCH");
|
|
13355
|
+
}
|
|
13356
|
+
}
|
|
13357
|
+
async function olderThan(path, ageMs) {
|
|
13358
|
+
try {
|
|
13359
|
+
return Date.now() - (await stat2(path)).mtimeMs > ageMs;
|
|
13360
|
+
} catch {
|
|
13361
|
+
return false;
|
|
13362
|
+
}
|
|
13363
|
+
}
|
|
13310
13364
|
async function atomicWrite(path, contents) {
|
|
13311
13365
|
const directory = dirname(path);
|
|
13312
13366
|
await mkdir(directory, { recursive: true, mode: 448 });
|
|
@@ -13426,7 +13480,12 @@ function resolveConfig(input2 = {}, env = process.env) {
|
|
|
13426
13480
|
enabled: parsed.reconnect !== false,
|
|
13427
13481
|
initialDelayMs,
|
|
13428
13482
|
maxDelayMs,
|
|
13429
|
-
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)
|
|
13430
13489
|
},
|
|
13431
13490
|
codex: {
|
|
13432
13491
|
// Off by default (2026-10-14). Carrying another agent's workspaces is a capability the user opts into, not
|
|
@@ -13462,7 +13521,7 @@ function normalizeServerUrl(value) {
|
|
|
13462
13521
|
}
|
|
13463
13522
|
|
|
13464
13523
|
// src/version.ts
|
|
13465
|
-
var PLUGIN_VERSION = "0.5.
|
|
13524
|
+
var PLUGIN_VERSION = "0.5.4";
|
|
13466
13525
|
|
|
13467
13526
|
// src/server-api.ts
|
|
13468
13527
|
var ENABLED_QR_PROVIDERS = ["github"];
|
|
@@ -14026,15 +14085,21 @@ var HostServerConnection = class {
|
|
|
14026
14085
|
}
|
|
14027
14086
|
async run() {
|
|
14028
14087
|
let delayMs = this.config.reconnect.initialDelayMs;
|
|
14088
|
+
let replacedRetryMs;
|
|
14089
|
+
let replacedAttempts = 0;
|
|
14029
14090
|
this.authRecoveryAttempted = false;
|
|
14030
14091
|
while (!this.stopped) {
|
|
14031
14092
|
try {
|
|
14032
14093
|
await this.connectOnce();
|
|
14033
14094
|
delayMs = this.config.reconnect.initialDelayMs;
|
|
14095
|
+
replacedRetryMs = void 0;
|
|
14096
|
+
replacedAttempts = 0;
|
|
14034
14097
|
} catch (error) {
|
|
14035
14098
|
const code = errorCode2(error);
|
|
14036
14099
|
if (code === "CREDENTIALS_REFRESHED") continue;
|
|
14037
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;
|
|
14038
14103
|
this.logger.warn("server control connection failed", {
|
|
14039
14104
|
code,
|
|
14040
14105
|
retryable: isRetryable(error),
|
|
@@ -14042,7 +14107,7 @@ var HostServerConnection = class {
|
|
|
14042
14107
|
...connectionFailureDetail(error)
|
|
14043
14108
|
});
|
|
14044
14109
|
if (TERMINAL_AUTH_ERRORS.has(code)) {
|
|
14045
|
-
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.");
|
|
14046
14111
|
}
|
|
14047
14112
|
if (code === "DEVICE_REVOKED") {
|
|
14048
14113
|
try {
|
|
@@ -14060,7 +14125,7 @@ var HostServerConnection = class {
|
|
|
14060
14125
|
continue;
|
|
14061
14126
|
}
|
|
14062
14127
|
if (!this.config.reconnect.enabled) return;
|
|
14063
|
-
await this.waitBeforeRetry(delayMs);
|
|
14128
|
+
await (replacedRetryMs === void 0 ? this.waitBeforeRetry(delayMs) : this.waitBeforeRetry(replacedRetryMs, true));
|
|
14064
14129
|
if (this.reconnectRequested) {
|
|
14065
14130
|
this.reconnectRequested = false;
|
|
14066
14131
|
delayMs = this.config.reconnect.initialDelayMs;
|
|
@@ -14624,8 +14689,8 @@ var HostServerConnection = class {
|
|
|
14624
14689
|
tunnel.noise.destroy();
|
|
14625
14690
|
}
|
|
14626
14691
|
}
|
|
14627
|
-
waitBeforeRetry(baseDelay) {
|
|
14628
|
-
const spread = baseDelay * this.config.reconnect.jitter;
|
|
14692
|
+
waitBeforeRetry(baseDelay, exact = false) {
|
|
14693
|
+
const spread = exact ? 0 : baseDelay * this.config.reconnect.jitter;
|
|
14629
14694
|
const delay = Math.max(0, Math.round(baseDelay - spread + Math.random() * spread * 2));
|
|
14630
14695
|
return new Promise((resolve4) => {
|
|
14631
14696
|
const timer = setTimeout(() => {
|
|
@@ -14639,8 +14704,8 @@ var HostServerConnection = class {
|
|
|
14639
14704
|
});
|
|
14640
14705
|
}
|
|
14641
14706
|
};
|
|
14707
|
+
var REPLACED_FIRST_RETRY_MS = 65e3;
|
|
14642
14708
|
var TERMINAL_AUTH_ERRORS = /* @__PURE__ */ new Set([
|
|
14643
|
-
"CONNECTION_REPLACED",
|
|
14644
14709
|
"SERVER_CREDENTIALS_BUSY",
|
|
14645
14710
|
"ACCOUNT_AUTH_REQUIRED",
|
|
14646
14711
|
"AUTH_INVALID",
|
|
@@ -20291,8 +20356,12 @@ var TypertGatewaySwitch = class {
|
|
|
20291
20356
|
installed = false;
|
|
20292
20357
|
/** Defaults to reachable so a caller that never wires this keeps the old behaviour. */
|
|
20293
20358
|
remoteAvailability = () => true;
|
|
20294
|
-
|
|
20359
|
+
/** Supplies the peer carrier calls speak for; see {@link ownerPeer}. */
|
|
20360
|
+
peerFor;
|
|
20361
|
+
peer;
|
|
20362
|
+
constructor(gateway, options = {}) {
|
|
20295
20363
|
this.runtime = gateway;
|
|
20364
|
+
this.peerFor = options.peer;
|
|
20296
20365
|
this.originalInvoke = gateway.invoke;
|
|
20297
20366
|
this.localInvoke = this.originalInvoke.bind(gateway);
|
|
20298
20367
|
this.originalStream = gateway.stream;
|
|
@@ -20300,18 +20369,42 @@ var TypertGatewaySwitch = class {
|
|
|
20300
20369
|
this.originalDispatch = this.runtime.dispatchRpc;
|
|
20301
20370
|
this.localDispatch = this.runtime.dispatchRpc?.bind(gateway);
|
|
20302
20371
|
this.originalOpen = this.runtime.openWireStream;
|
|
20303
|
-
this.localOpen = createLocalOpen(gateway, this.runtime);
|
|
20372
|
+
this.localOpen = createLocalOpen(gateway, this.runtime, () => this.ownerPeer());
|
|
20373
|
+
}
|
|
20374
|
+
/**
|
|
20375
|
+
* The peer a carrier call speaks for, created once and reused.
|
|
20376
|
+
*
|
|
20377
|
+
* dsh 0.2.1-rc.2 reads `peer.signal` on every carrier call and rejects one that arrives without a peer; a remote
|
|
20378
|
+
* client runs with the Host owner's authority (decided 2026-10-19), which is the same principal dsh gives its own
|
|
20379
|
+
* browser peer, so the plugin serves remote calls as the owner. Releases before rc.2 ignore the argument, and a
|
|
20380
|
+
* profile without a `connection` service simply has no peer to offer.
|
|
20381
|
+
*/
|
|
20382
|
+
ownerPeer() {
|
|
20383
|
+
if (this.peer !== void 0) return this.peer;
|
|
20384
|
+
this.peer = this.peerFor?.();
|
|
20385
|
+
return this.peer;
|
|
20386
|
+
}
|
|
20387
|
+
/** Trailing argument the carrier wants when the caller did not supply one. */
|
|
20388
|
+
peerArguments() {
|
|
20389
|
+
const peer = this.ownerPeer();
|
|
20390
|
+
return peer === void 0 ? [] : [peer];
|
|
20304
20391
|
}
|
|
20305
20392
|
/** Original local dispatcher, used by the Host bridge without switch recursion. */
|
|
20306
20393
|
local() {
|
|
20307
|
-
const
|
|
20394
|
+
const bare = this.localDispatch;
|
|
20395
|
+
const dispatch = bare === void 0 ? (async (endpoint, payload, signal) => {
|
|
20308
20396
|
try {
|
|
20309
20397
|
const request = requestFromCarrier(endpoint, payload, signal);
|
|
20310
20398
|
return { ok: true, value: await this.localInvoke(request) };
|
|
20311
20399
|
} catch (error) {
|
|
20312
20400
|
return { ok: false, error: this.failure(error) };
|
|
20313
20401
|
}
|
|
20314
|
-
})
|
|
20402
|
+
}) : (endpoint, payload, signal, ...rest) => Promise.resolve(bare(
|
|
20403
|
+
endpoint,
|
|
20404
|
+
payload,
|
|
20405
|
+
signal,
|
|
20406
|
+
...rest.length === 0 ? this.peerArguments() : rest
|
|
20407
|
+
));
|
|
20315
20408
|
const open = this.localOpen ?? (async (endpoint, payload, signal) => {
|
|
20316
20409
|
if (this.localStream === void 0) throw new Error("The local Harness Gateway does not support Remote streams.");
|
|
20317
20410
|
return this.localStream(requestFromCarrier(endpoint, payload, signal));
|
|
@@ -20346,17 +20439,17 @@ var TypertGatewaySwitch = class {
|
|
|
20346
20439
|
);
|
|
20347
20440
|
}
|
|
20348
20441
|
if (previousDispatch !== void 0) {
|
|
20349
|
-
this.runtime.dispatchRpc = (endpoint, payload, signal) => forwards(endpoint) ? Promise.resolve(previousDispatch(endpoint, payload, signal)).catch((error) => {
|
|
20442
|
+
this.runtime.dispatchRpc = (endpoint, payload, signal, ...rest) => forwards(endpoint) ? Promise.resolve(previousDispatch(endpoint, payload, signal, ...rest)).catch((error) => {
|
|
20350
20443
|
console.warn("[dsh-remote] local call failed with no remote target selected", {
|
|
20351
20444
|
endpoint,
|
|
20352
20445
|
code: typeof error === "object" && error !== null && "code" in error ? String(error.code) : void 0,
|
|
20353
20446
|
message: error instanceof Error ? error.message : String(error)
|
|
20354
20447
|
});
|
|
20355
20448
|
throw error;
|
|
20356
|
-
}) : !this.routesToRemote(endpoint) ? this.localDispatch(endpoint, payload, signal) : this.withLocalFallback(
|
|
20449
|
+
}) : !this.routesToRemote(endpoint) ? this.localDispatch(endpoint, payload, signal, ...rest) : this.withLocalFallback(
|
|
20357
20450
|
endpoint,
|
|
20358
20451
|
() => Promise.resolve(this.remoteTarget.dispatch(endpoint, payload, signal)),
|
|
20359
|
-
() => this.localDispatch(endpoint, payload, signal)
|
|
20452
|
+
() => this.localDispatch(endpoint, payload, signal, ...rest)
|
|
20360
20453
|
).then((result) => normalizeByteResult3(endpoint, result));
|
|
20361
20454
|
}
|
|
20362
20455
|
if (previousOpen !== void 0) {
|
|
@@ -20495,17 +20588,17 @@ function linkControl(signal) {
|
|
|
20495
20588
|
else signal.addEventListener("abort", () => control.abort(signal.reason), { once: true });
|
|
20496
20589
|
return control;
|
|
20497
20590
|
}
|
|
20498
|
-
function createLocalOpen(gateway, runtime) {
|
|
20591
|
+
function createLocalOpen(gateway, runtime, peer = () => void 0) {
|
|
20499
20592
|
const open = runtime.openWireStream;
|
|
20500
20593
|
if (open !== void 0) {
|
|
20501
|
-
return usesRc1Arity(open) ? (endpoint, payload, signal) => open.call(runtime, endpoint, payload, endedUplink(),
|
|
20594
|
+
return usesRc1Arity(open) ? (endpoint, payload, signal) => open.call(runtime, endpoint, payload, endedUplink(), peer(), signal, linkControl(signal)) : (endpoint, payload, signal) => open.call(runtime, endpoint, payload, signal);
|
|
20502
20595
|
}
|
|
20503
20596
|
const wire = gateway.wireStream;
|
|
20504
20597
|
if (wire === void 0) return void 0;
|
|
20505
20598
|
const wireOpen = wire.open;
|
|
20506
20599
|
if (usesRc1Arity(wireOpen)) {
|
|
20507
20600
|
const rc1 = wireOpen;
|
|
20508
|
-
return (endpoint, payload, signal) => rc1.call(wire, endpoint, payload, endedUplink(),
|
|
20601
|
+
return (endpoint, payload, signal) => rc1.call(wire, endpoint, payload, endedUplink(), peer(), signal);
|
|
20509
20602
|
}
|
|
20510
20603
|
const legacy = wireOpen;
|
|
20511
20604
|
return (endpoint, payload, signal) => legacy.call(wire, endpoint, payload, signal);
|
|
@@ -21673,7 +21766,7 @@ var QUICK_RECONNECT_RETRY_DELAY_MS = 5e3;
|
|
|
21673
21766
|
var QUICK_RECONNECT_FORCE_RELAY = true;
|
|
21674
21767
|
var BACKGROUND_RETRY_DELAYS_MS = [5e3, 1e4, 2e4, 3e4, 3e4, 3e4, 3e4];
|
|
21675
21768
|
var ClientModeRuntime = class {
|
|
21676
|
-
constructor(config, identities, server, apiProxy, typertGateway, logger, host, rtcFactoryProvider = loadNodeRtcFactory, targetStore) {
|
|
21769
|
+
constructor(config, identities, server, apiProxy, typertGateway, logger, host, rtcFactoryProvider = loadNodeRtcFactory, targetStore, peer) {
|
|
21677
21770
|
this.config = config;
|
|
21678
21771
|
this.identities = identities;
|
|
21679
21772
|
this.server = server;
|
|
@@ -21681,8 +21774,9 @@ var ClientModeRuntime = class {
|
|
|
21681
21774
|
this.host = host;
|
|
21682
21775
|
this.rtcFactoryProvider = rtcFactoryProvider;
|
|
21683
21776
|
this.targetStore = targetStore;
|
|
21777
|
+
this.peer = peer;
|
|
21684
21778
|
this.proxySwitch = apiProxy === void 0 ? void 0 : new ApiProxySwitch(apiProxy);
|
|
21685
|
-
this.gatewaySwitch = new TypertGatewaySwitch(typertGateway);
|
|
21779
|
+
this.gatewaySwitch = new TypertGatewaySwitch(typertGateway, { peer: this.peer });
|
|
21686
21780
|
this.gatewaySwitch.setRemoteAvailability(() => this.connected !== void 0);
|
|
21687
21781
|
}
|
|
21688
21782
|
preview;
|
|
@@ -21994,9 +22088,26 @@ var ClientModeRuntime = class {
|
|
|
21994
22088
|
}
|
|
21995
22089
|
async setHostAuthorization(enabled) {
|
|
21996
22090
|
if (this.host === void 0) throw new ClientModeError("METHOD_NOT_ALLOWED", "This plugin is not running as a Host.");
|
|
21997
|
-
|
|
21998
|
-
|
|
21999
|
-
|
|
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
|
+
}
|
|
22000
22111
|
return this.status();
|
|
22001
22112
|
}
|
|
22002
22113
|
async setMode(mode, targetDeviceId, signal) {
|
|
@@ -22432,6 +22543,22 @@ var ClientModeRuntime = class {
|
|
|
22432
22543
|
return;
|
|
22433
22544
|
}
|
|
22434
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
|
+
}
|
|
22435
22562
|
if (this.reconnecting === void 0) {
|
|
22436
22563
|
const run = this.remoteReconnectRun;
|
|
22437
22564
|
this.reconnecting = { targetDeviceId, ...targetName === void 0 ? {} : { targetName }, phase: "fast" };
|
|
@@ -23144,7 +23271,26 @@ var ClientModeRuntime = class {
|
|
|
23144
23271
|
throw error;
|
|
23145
23272
|
}
|
|
23146
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;
|
|
23147
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) {
|
|
23148
23294
|
signal?.throwIfAborted();
|
|
23149
23295
|
const progressRunId = this.connectionProgressRun + 1;
|
|
23150
23296
|
this.connectionProgressRun = progressRunId;
|
|
@@ -29911,7 +30057,7 @@ function remoteStatusLines(target2) {
|
|
|
29911
30057
|
`Authorization: ${status2.authorized ? status2.account === void 0 ? "logged in" : `logged in (${status2.account})` : "logged out"}`,
|
|
29912
30058
|
`Server connection: ${connection}`,
|
|
29913
30059
|
...status2.error === void 0 ? [] : [`Connection error: ${status2.error}`],
|
|
29914
|
-
...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]."] : [],
|
|
29915
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"}`,
|
|
29916
30062
|
`Remote clients: ${diagnostics.activeConnections}`,
|
|
29917
30063
|
`Codex Remote: ${codex.enabled ? codex.state : "disabled"}`,
|
|
@@ -30269,8 +30415,18 @@ async function activate(ctx, readConfig, entryId, tuiBinding) {
|
|
|
30269
30415
|
const deviceDirectory = config.serverUrl === void 0 ? defaultIdentityDirectory : await ensureDeviceDirectory(defaultIdentityDirectory, config.serverUrl);
|
|
30270
30416
|
const hostIdentities = new IdentityStore({ directory: deviceDirectory });
|
|
30271
30417
|
const apiProxy = ctx.get("apiProxy");
|
|
30418
|
+
const connectionService = ctx.get("connection", false);
|
|
30419
|
+
let ownerPeerResolved = false;
|
|
30420
|
+
let ownerPeer;
|
|
30421
|
+
const peerFor = () => {
|
|
30422
|
+
if (!ownerPeerResolved) {
|
|
30423
|
+
ownerPeerResolved = true;
|
|
30424
|
+
ownerPeer = connectionService?.createPeer?.({ principal: { kind: "owner" } });
|
|
30425
|
+
}
|
|
30426
|
+
return ownerPeer;
|
|
30427
|
+
};
|
|
30272
30428
|
const nativeTypertGateway = ctx.get("typertGateway");
|
|
30273
|
-
const localTypertGateway = new TypertGatewaySwitch(nativeTypertGateway).local();
|
|
30429
|
+
const localTypertGateway = new TypertGatewaySwitch(nativeTypertGateway, { peer: peerFor }).local();
|
|
30274
30430
|
const subprocess = ctx.get("subprocess", false);
|
|
30275
30431
|
const deepseekAccount = ctx.get("deepseekAccount", false);
|
|
30276
30432
|
const deepseekSession = deepseekAccount === void 0 ? void 0 : {
|
|
@@ -30319,7 +30475,8 @@ async function activate(ctx, readConfig, entryId, tuiBinding) {
|
|
|
30319
30475
|
hostControl,
|
|
30320
30476
|
// The RTC factory keeps its default; the target store lives beside the plugin state.
|
|
30321
30477
|
void 0,
|
|
30322
|
-
new ClientTargetStore(defaultIdentityDirectory)
|
|
30478
|
+
new ClientTargetStore(defaultIdentityDirectory),
|
|
30479
|
+
peerFor
|
|
30323
30480
|
);
|
|
30324
30481
|
}
|
|
30325
30482
|
const controlRuntime = connection === void 0 ? void 0 : new PluginControlRuntime(config, defaultIdentityDirectory, settingsBinding, clientRuntime, hostControl, deepseekSession);
|