From 7abee44e78465ff78602c1afca05b97a8f73acac Mon Sep 17 00:00:00 2001 From: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com> Date: Sun, 11 Oct 2026 02:53:57 -0400 Subject: [PATCH] feat(mobile): show SSH host health on the workspace row badge (#26415) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(mobile): show SSH host health on the workspace row host badge Rows on an SSH host the desktop cannot reach looked healthy on the phone. The badge now adds the desktop sidebar's own word ("devbox · Disconnected"), derived by the same shared function from the connection status ssh.listTargetSummaries already sends; old desktops keep today's badge. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * feat(mobile): name an unhealthy SSH host even in a single-host list The desktop card shows SSH status on any host count, so the phone now reads target summaries whenever a repo is on SSH and badges an unhealthy host alone too. Labels and health share one target-to-host keying, and a hook-level test pins the reply-to-row wiring. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): clear stale SSH host badges and announce them without the dot A catalog that no longer spans hosts or holds an SSH repo now clears the health and label maps, so a host that left cannot keep badging rows. The badge gets a screen-reader label ("devbox, Disconnected") instead of reading the middle dot. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): type the single-host health fixtures inline so the test tsc passes Fixtures hoisted into consts widened their health maps to string, which the now-required hostHealthById rejects under the tests-typecheck ratchet; passing them inline keeps the union. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): re-record the repo-metadata goldens for SSH host health The settled metadata state now carries hostHealthById, and an SSH-only catalog now reads the target summaries so a lone unhealthy host can be badged. Scoped re-record via rpc-recording.mts. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb --- ...ettings.repo-metadata-host.platform-1.json | 185 +++--- ...ix-settings.repo-metadata-repo.list-1.json | 61 +- ...settings.repo-metadata-settings.get-1.json | 150 +++-- ...po-metadata-ssh.listtargetsummaries-1.json | 160 +++-- ...ules-settings-repo-metadata-fulfilled.json | 161 ++--- .../goldens/settings-repo-cache-expiry.json | 148 +++-- .../settings-repo-metadata-fulfilled.json | 61 +- .../goldens/settings-repo-metadata-icons.json | 107 ++-- ...tings-repo-metadata-refuse-after-data.json | 604 +++++++++--------- .../settings-repo-metadata-refused.json | 61 +- .../settings-repo-metadata-single-host.json | 237 ++++++- ...ettings-repo-metadata-transport-error.json | 63 +- mobile/src/components/WorktreeListRow.test.ts | 25 + mobile/src/components/WorktreeListRow.tsx | 18 +- .../host-screen-reply-schema.test.ts | 14 + .../host-screen/host-screen-reply-schema.ts | 10 +- .../host-show-pinned-in-groups.test.ts | 1 + .../host-ssh-health-badge-wiring.test.ts | 135 ++++ .../host-screen/use-host-display-worktrees.ts | 33 + .../src/host-screen/use-host-repo-metadata.ts | 23 +- .../host-screen/use-host-screen-controller.ts | 26 +- .../host-screen/use-host-screen-identity.ts | 2 + .../src/host-screen/use-host-screen-state.ts | 6 + .../workspace-source-reply-schema.test.ts | 2 +- .../tasks/workspace-source-reply-schema.ts | 21 +- .../transport/ssh-connection-status-arms.ts | 20 + .../worktree/host-worktree-refresh.test.ts | 15 + mobile/src/worktree/host-worktree-refresh.ts | 3 + mobile/src/worktree/workspace-list-types.ts | 2 + .../worktree-host-context-labels.test.ts | 53 +- .../worktree/worktree-host-context-labels.ts | 24 +- .../src/worktree/worktree-host-health.test.ts | 46 ++ mobile/src/worktree/worktree-host-health.ts | 39 ++ .../sidebar/sidebar-host-options.ts | 18 +- src/shared/execution-host-health.ts | 47 ++ src/shared/execution-host-registry.ts | 29 +- src/shared/worktree/host-context-labels.ts | 15 +- 37 files changed, 1699 insertions(+), 926 deletions(-) create mode 100644 mobile/src/host-screen/host-ssh-health-badge-wiring.test.ts create mode 100644 mobile/src/host-screen/use-host-display-worktrees.ts create mode 100644 mobile/src/transport/ssh-connection-status-arms.ts create mode 100644 mobile/src/worktree/worktree-host-health.test.ts create mode 100644 mobile/src/worktree/worktree-host-health.ts create mode 100644 src/shared/execution-host-health.ts diff --git a/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-host.platform-1.json b/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-host.platform-1.json index 061899e316a..27eda6c704c 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-host.platform-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-host.platform-1.json @@ -38,23 +38,6 @@ } } }, - "071880b671a1": { - "hostLabelById": [["ssh:ssh-1", "SSH"]], - "hostPlatform": "linux", - "repoColorsByName": [ - ["Local", "#6366f1"], - ["Remote", "#f97316"] - ], - "repoHostIdByRepoId": [ - ["repo-1", "local"], - ["repo-2", "ssh:ssh-1"] - ], - "repoIconsByName": [], - "repoIdsByName": [ - ["Local", "repo-1"], - ["Remote", "repo-2"] - ] - }, "0c4296f215a4": { "name": "settings.get#1", "ordinal": 8, @@ -195,25 +178,6 @@ "startedAt": 0 } }, - "1de50f3b4aac": { - "hostLabelById": [["ssh:ssh-1", "SSH"]], - "hostPlatform": { - "$rpc": "null" - }, - "repoColorsByName": [ - ["Local", "#6366f1"], - ["Remote", "#f97316"] - ], - "repoHostIdByRepoId": [ - ["repo-1", "local"], - ["repo-2", "ssh:ssh-1"] - ], - "repoIconsByName": [], - "repoIdsByName": [ - ["Local", "repo-1"], - ["Remote", "repo-2"] - ] - }, "1ff13ee8621c": { "name": "repoIconsByName", "ordinal": 4, @@ -417,6 +381,11 @@ } } }, + "53226cb82ec3": { + "name": "hostPlatform", + "ordinal": 15, + "value": "linux" + }, "53dea128b181": { "name": "host.platform#1", "ordinal": 9, @@ -484,10 +453,10 @@ } } }, - "56ab81cb67dc": { - "name": "hostLabelById", + "6063e534d9db": { + "name": "hostHealthById", "ordinal": 13, - "value": [["ssh:ssh-1", "SSH"]] + "value": [] }, "6134b73f18d0": { "repoColorsByName": [ @@ -618,11 +587,23 @@ } } }, + "9d20695fdb34": { + "name": "hostPlatform", + "ordinal": 15, + "value": { + "$rpc": "null" + } + }, "b428bce85ee7": { "name": "settings.get#1", "ordinal": 11, "json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}" }, + "bea13a4a2e87": { + "name": "hostLabelById", + "ordinal": 14, + "value": [["ssh:ssh-1", "SSH"]] + }, "c752e67787f9": { "name": "ssh.listTargetSummaries#1", "ordinal": 7, @@ -697,12 +678,25 @@ } } }, - "e9480602a4c6": { - "name": "hostPlatform", - "ordinal": 14, - "value": { + "e841f87c65aa": { + "hostHealthById": [], + "hostLabelById": [["ssh:ssh-1", "SSH"]], + "hostPlatform": { "$rpc": "null" - } + }, + "repoColorsByName": [ + ["Local", "#6366f1"], + ["Remote", "#f97316"] + ], + "repoHostIdByRepoId": [ + ["repo-1", "local"], + ["repo-2", "ssh:ssh-1"] + ], + "repoIconsByName": [], + "repoIdsByName": [ + ["Local", "repo-1"], + ["Remote", "repo-2"] + ] }, "eb79a9b3682a": { "status": "fulfilled", @@ -712,10 +706,23 @@ "$rpc": "undefined" } }, - "f9193cae5824": { - "name": "hostPlatform", - "ordinal": 14, - "value": "linux" + "f4667dda0187": { + "hostHealthById": [], + "hostLabelById": [["ssh:ssh-1", "SSH"]], + "hostPlatform": "linux", + "repoColorsByName": [ + ["Local", "#6366f1"], + ["Remote", "#f97316"] + ], + "repoHostIdByRepoId": [ + ["repo-1", "local"], + ["repo-2", "ssh:ssh-1"] + ], + "repoIconsByName": [], + "repoIdsByName": [ + ["Local", "repo-1"], + ["Remote", "repo-2"] + ] } }, "recording": { @@ -749,8 +756,9 @@ "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "e9480602a4c6" + "6063e534d9db", + "bea13a4a2e87", + "9d20695fdb34" ] } }, @@ -763,14 +771,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "071880b671a1", + "state": "f4667dda0187", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "f9193cae5824" + "6063e534d9db", + "bea13a4a2e87", + "53226cb82ec3" ] } }, @@ -783,14 +792,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "1de50f3b4aac", + "state": "e841f87c65aa", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "e9480602a4c6" + "6063e534d9db", + "bea13a4a2e87", + "9d20695fdb34" ] } }, @@ -803,14 +813,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "1de50f3b4aac", + "state": "e841f87c65aa", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "e9480602a4c6" + "6063e534d9db", + "bea13a4a2e87", + "9d20695fdb34" ] } }, @@ -823,14 +834,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "1de50f3b4aac", + "state": "e841f87c65aa", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "e9480602a4c6" + "6063e534d9db", + "bea13a4a2e87", + "9d20695fdb34" ] } }, @@ -843,14 +855,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "1de50f3b4aac", + "state": "e841f87c65aa", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "e9480602a4c6" + "6063e534d9db", + "bea13a4a2e87", + "9d20695fdb34" ] } }, @@ -863,14 +876,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "1de50f3b4aac", + "state": "e841f87c65aa", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "e9480602a4c6" + "6063e534d9db", + "bea13a4a2e87", + "9d20695fdb34" ] } }, @@ -883,14 +897,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "1de50f3b4aac", + "state": "e841f87c65aa", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "e9480602a4c6" + "6063e534d9db", + "bea13a4a2e87", + "9d20695fdb34" ] } }, @@ -903,14 +918,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "1de50f3b4aac", + "state": "e841f87c65aa", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "e9480602a4c6" + "6063e534d9db", + "bea13a4a2e87", + "9d20695fdb34" ] } }, @@ -923,14 +939,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "1de50f3b4aac", + "state": "e841f87c65aa", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "e9480602a4c6" + "6063e534d9db", + "bea13a4a2e87", + "9d20695fdb34" ] } }, @@ -943,14 +960,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "1de50f3b4aac", + "state": "e841f87c65aa", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "e9480602a4c6" + "6063e534d9db", + "bea13a4a2e87", + "9d20695fdb34" ] } }, @@ -963,14 +981,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "1de50f3b4aac", + "state": "e841f87c65aa", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "e9480602a4c6" + "6063e534d9db", + "bea13a4a2e87", + "9d20695fdb34" ] } } diff --git a/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-repo.list-1.json b/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-repo.list-1.json index 4256c025d06..bd38ed25958 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-repo.list-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-repo.list-1.json @@ -38,23 +38,6 @@ } } }, - "071880b671a1": { - "hostLabelById": [["ssh:ssh-1", "SSH"]], - "hostPlatform": "linux", - "repoColorsByName": [ - ["Local", "#6366f1"], - ["Remote", "#f97316"] - ], - "repoHostIdByRepoId": [ - ["repo-1", "local"], - ["repo-2", "ssh:ssh-1"] - ], - "repoIconsByName": [], - "repoIdsByName": [ - ["Local", "repo-1"], - ["Remote", "repo-2"] - ] - }, "0c4296f215a4": { "name": "settings.get#1", "ordinal": 8, @@ -327,10 +310,15 @@ } } }, - "56ab81cb67dc": { - "name": "hostLabelById", + "53226cb82ec3": { + "name": "hostPlatform", + "ordinal": 15, + "value": "linux" + }, + "6063e534d9db": { + "name": "hostHealthById", "ordinal": 13, - "value": [["ssh:ssh-1", "SSH"]] + "value": [] }, "6134b73f18d0": { "repoColorsByName": [ @@ -504,6 +492,11 @@ "ordinal": 11, "json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}" }, + "bea13a4a2e87": { + "name": "hostLabelById", + "ordinal": 14, + "value": [["ssh:ssh-1", "SSH"]] + }, "c752e67787f9": { "name": "ssh.listTargetSummaries#1", "ordinal": 7, @@ -655,10 +648,23 @@ } } }, - "f9193cae5824": { - "name": "hostPlatform", - "ordinal": 14, - "value": "linux" + "f4667dda0187": { + "hostHealthById": [], + "hostLabelById": [["ssh:ssh-1", "SSH"]], + "hostPlatform": "linux", + "repoColorsByName": [ + ["Local", "#6366f1"], + ["Remote", "#f97316"] + ], + "repoHostIdByRepoId": [ + ["repo-1", "local"], + ["repo-2", "ssh:ssh-1"] + ], + "repoIconsByName": [], + "repoIdsByName": [ + ["Local", "repo-1"], + ["Remote", "repo-2"] + ] } }, "recording": { @@ -686,14 +692,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "071880b671a1", + "state": "f4667dda0187", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "f9193cae5824" + "6063e534d9db", + "bea13a4a2e87", + "53226cb82ec3" ] } }, diff --git a/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-settings.get-1.json b/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-settings.get-1.json index 4052935a5e6..423f3062147 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-settings.get-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-settings.get-1.json @@ -38,23 +38,6 @@ } } }, - "071880b671a1": { - "hostLabelById": [["ssh:ssh-1", "SSH"]], - "hostPlatform": "linux", - "repoColorsByName": [ - ["Local", "#6366f1"], - ["Remote", "#f97316"] - ], - "repoHostIdByRepoId": [ - ["repo-1", "local"], - ["repo-2", "ssh:ssh-1"] - ], - "repoIconsByName": [], - "repoIdsByName": [ - ["Local", "repo-1"], - ["Remote", "repo-2"] - ] - }, "0c4296f215a4": { "name": "settings.get#1", "ordinal": 8, @@ -295,6 +278,11 @@ } } }, + "53226cb82ec3": { + "name": "hostPlatform", + "ordinal": 15, + "value": "linux" + }, "53dea128b181": { "name": "host.platform#1", "ordinal": 9, @@ -327,10 +315,10 @@ } } }, - "56ab81cb67dc": { - "name": "hostLabelById", + "6063e534d9db": { + "name": "hostHealthById", "ordinal": 13, - "value": [["ssh:ssh-1", "SSH"]] + "value": [] }, "6134b73f18d0": { "repoColorsByName": [ @@ -496,6 +484,13 @@ "startedAt": 0 } }, + "9d20695fdb34": { + "name": "hostPlatform", + "ordinal": 15, + "value": { + "$rpc": "null" + } + }, "ae308dad876a": { "name": "settings.get#1", "ordinal": 8, @@ -567,6 +562,11 @@ } } }, + "bea13a4a2e87": { + "name": "hostLabelById", + "ordinal": 14, + "value": [["ssh:ssh-1", "SSH"]] + }, "c752e67787f9": { "name": "ssh.listTargetSummaries#1", "ordinal": 7, @@ -710,13 +710,6 @@ } } }, - "e9480602a4c6": { - "name": "hostPlatform", - "ordinal": 14, - "value": { - "$rpc": "null" - } - }, "eb79a9b3682a": { "status": "fulfilled", "startedAt": 0, @@ -725,10 +718,23 @@ "$rpc": "undefined" } }, - "f9193cae5824": { - "name": "hostPlatform", - "ordinal": 14, - "value": "linux" + "f4667dda0187": { + "hostHealthById": [], + "hostLabelById": [["ssh:ssh-1", "SSH"]], + "hostPlatform": "linux", + "repoColorsByName": [ + ["Local", "#6366f1"], + ["Remote", "#f97316"] + ], + "repoHostIdByRepoId": [ + ["repo-1", "local"], + ["repo-2", "ssh:ssh-1"] + ], + "repoIconsByName": [], + "repoIdsByName": [ + ["Local", "repo-1"], + ["Remote", "repo-2"] + ] } }, "recording": { @@ -762,8 +768,9 @@ "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "e9480602a4c6" + "6063e534d9db", + "bea13a4a2e87", + "9d20695fdb34" ] } }, @@ -776,14 +783,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "071880b671a1", + "state": "f4667dda0187", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "f9193cae5824" + "6063e534d9db", + "bea13a4a2e87", + "53226cb82ec3" ] } }, @@ -796,14 +804,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "071880b671a1", + "state": "f4667dda0187", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "f9193cae5824" + "6063e534d9db", + "bea13a4a2e87", + "53226cb82ec3" ] } }, @@ -816,14 +825,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "071880b671a1", + "state": "f4667dda0187", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "f9193cae5824" + "6063e534d9db", + "bea13a4a2e87", + "53226cb82ec3" ] } }, @@ -836,14 +846,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "071880b671a1", + "state": "f4667dda0187", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "f9193cae5824" + "6063e534d9db", + "bea13a4a2e87", + "53226cb82ec3" ] } }, @@ -856,14 +867,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "071880b671a1", + "state": "f4667dda0187", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "f9193cae5824" + "6063e534d9db", + "bea13a4a2e87", + "53226cb82ec3" ] } }, @@ -876,14 +888,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "071880b671a1", + "state": "f4667dda0187", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "f9193cae5824" + "6063e534d9db", + "bea13a4a2e87", + "53226cb82ec3" ] } }, @@ -896,14 +909,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "071880b671a1", + "state": "f4667dda0187", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "f9193cae5824" + "6063e534d9db", + "bea13a4a2e87", + "53226cb82ec3" ] } }, @@ -916,14 +930,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "071880b671a1", + "state": "f4667dda0187", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "f9193cae5824" + "6063e534d9db", + "bea13a4a2e87", + "53226cb82ec3" ] } }, @@ -936,14 +951,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "071880b671a1", + "state": "f4667dda0187", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "f9193cae5824" + "6063e534d9db", + "bea13a4a2e87", + "53226cb82ec3" ] } }, @@ -956,14 +972,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "071880b671a1", + "state": "f4667dda0187", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "f9193cae5824" + "6063e534d9db", + "bea13a4a2e87", + "53226cb82ec3" ] } }, @@ -976,14 +993,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "071880b671a1", + "state": "f4667dda0187", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "f9193cae5824" + "6063e534d9db", + "bea13a4a2e87", + "53226cb82ec3" ] } } diff --git a/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-ssh.listtargetsummaries-1.json b/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-ssh.listtargetsummaries-1.json index 8ba0eb0ce8d..eaf4e75fb05 100644 --- a/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-ssh.listtargetsummaries-1.json +++ b/mobile/rpc-foundation/goldens/matrix-settings.repo-metadata-ssh.listtargetsummaries-1.json @@ -72,23 +72,6 @@ } } }, - "071880b671a1": { - "hostLabelById": [["ssh:ssh-1", "SSH"]], - "hostPlatform": "linux", - "repoColorsByName": [ - ["Local", "#6366f1"], - ["Remote", "#f97316"] - ], - "repoHostIdByRepoId": [ - ["repo-1", "local"], - ["repo-2", "ssh:ssh-1"] - ], - "repoIconsByName": [], - "repoIdsByName": [ - ["Local", "repo-1"], - ["Remote", "repo-2"] - ] - }, "08d05f42e4a2": { "name": "ssh.listTargetSummaries#1", "ordinal": 7, @@ -427,13 +410,31 @@ } } }, - "56ab81cb67dc": { - "name": "hostLabelById", - "ordinal": 13, - "value": [["ssh:ssh-1", "SSH"]] + "523753167e39": { + "hostHealthById": [], + "hostLabelById": [], + "hostPlatform": "linux", + "repoColorsByName": [ + ["Local", "#6366f1"], + ["Remote", "#f97316"] + ], + "repoHostIdByRepoId": [ + ["repo-1", "local"], + ["repo-2", "ssh:ssh-1"] + ], + "repoIconsByName": [], + "repoIdsByName": [ + ["Local", "repo-1"], + ["Remote", "repo-2"] + ] }, - "5ffccd000213": { - "name": "hostLabelById", + "53226cb82ec3": { + "name": "hostPlatform", + "ordinal": 15, + "value": "linux" + }, + "6063e534d9db": { + "name": "hostHealthById", "ordinal": 13, "value": [] }, @@ -577,6 +578,11 @@ "ordinal": 11, "json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}" }, + "bea13a4a2e87": { + "name": "hostLabelById", + "ordinal": 14, + "value": [["ssh:ssh-1", "SSH"]] + }, "c752e67787f9": { "name": "ssh.listTargetSummaries#1", "ordinal": 7, @@ -616,8 +622,22 @@ } } }, - "eb68427ac627": { - "hostLabelById": [], + "daecdc0e521b": { + "name": "hostLabelById", + "ordinal": 14, + "value": [] + }, + "eb79a9b3682a": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "$rpc": "undefined" + } + }, + "f4667dda0187": { + "hostHealthById": [], + "hostLabelById": [["ssh:ssh-1", "SSH"]], "hostPlatform": "linux", "repoColorsByName": [ ["Local", "#6366f1"], @@ -633,19 +653,6 @@ ["Remote", "repo-2"] ] }, - "eb79a9b3682a": { - "status": "fulfilled", - "startedAt": 0, - "settledAt": 0, - "value": { - "$rpc": "undefined" - } - }, - "f9193cae5824": { - "name": "hostPlatform", - "ordinal": 14, - "value": "linux" - }, "fa2cb04c517b": { "name": "ssh.listTargetSummaries#1", "ordinal": 7, @@ -707,14 +714,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "071880b671a1", + "state": "f4667dda0187", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "f9193cae5824" + "6063e534d9db", + "bea13a4a2e87", + "53226cb82ec3" ] } }, @@ -740,14 +748,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "eb68427ac627", + "state": "523753167e39", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "5ffccd000213", - "f9193cae5824" + "6063e534d9db", + "daecdc0e521b", + "53226cb82ec3" ] } }, @@ -773,14 +782,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "eb68427ac627", + "state": "523753167e39", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "5ffccd000213", - "f9193cae5824" + "6063e534d9db", + "daecdc0e521b", + "53226cb82ec3" ] } }, @@ -806,14 +816,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "eb68427ac627", + "state": "523753167e39", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "5ffccd000213", - "f9193cae5824" + "6063e534d9db", + "daecdc0e521b", + "53226cb82ec3" ] } }, @@ -839,14 +850,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "eb68427ac627", + "state": "523753167e39", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "5ffccd000213", - "f9193cae5824" + "6063e534d9db", + "daecdc0e521b", + "53226cb82ec3" ] } }, @@ -872,14 +884,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "eb68427ac627", + "state": "523753167e39", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "5ffccd000213", - "f9193cae5824" + "6063e534d9db", + "daecdc0e521b", + "53226cb82ec3" ] } }, @@ -905,14 +918,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "eb68427ac627", + "state": "523753167e39", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "5ffccd000213", - "f9193cae5824" + "6063e534d9db", + "daecdc0e521b", + "53226cb82ec3" ] } }, @@ -938,14 +952,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "eb68427ac627", + "state": "523753167e39", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "5ffccd000213", - "f9193cae5824" + "6063e534d9db", + "daecdc0e521b", + "53226cb82ec3" ] } }, @@ -971,14 +986,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "eb68427ac627", + "state": "523753167e39", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "5ffccd000213", - "f9193cae5824" + "6063e534d9db", + "daecdc0e521b", + "53226cb82ec3" ] } }, @@ -1004,14 +1020,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "eb68427ac627", + "state": "523753167e39", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "5ffccd000213", - "f9193cae5824" + "6063e534d9db", + "daecdc0e521b", + "53226cb82ec3" ] } }, @@ -1037,14 +1054,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "eb68427ac627", + "state": "523753167e39", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "5ffccd000213", - "f9193cae5824" + "6063e534d9db", + "daecdc0e521b", + "53226cb82ec3" ] } } diff --git a/mobile/rpc-foundation/goldens/schedules-settings-repo-metadata-fulfilled.json b/mobile/rpc-foundation/goldens/schedules-settings-repo-metadata-fulfilled.json index f8d65fba25b..a81b7772b49 100644 --- a/mobile/rpc-foundation/goldens/schedules-settings-repo-metadata-fulfilled.json +++ b/mobile/rpc-foundation/goldens/schedules-settings-repo-metadata-fulfilled.json @@ -75,23 +75,6 @@ } } }, - "071880b671a1": { - "hostLabelById": [["ssh:ssh-1", "SSH"]], - "hostPlatform": "linux", - "repoColorsByName": [ - ["Local", "#6366f1"], - ["Remote", "#f97316"] - ], - "repoHostIdByRepoId": [ - ["repo-1", "local"], - ["repo-2", "ssh:ssh-1"] - ], - "repoIconsByName": [], - "repoIdsByName": [ - ["Local", "repo-1"], - ["Remote", "repo-2"] - ] - }, "0c4296f215a4": { "name": "settings.get#1", "ordinal": 8, @@ -163,25 +146,6 @@ "startedAt": 0 } }, - "1de50f3b4aac": { - "hostLabelById": [["ssh:ssh-1", "SSH"]], - "hostPlatform": { - "$rpc": "null" - }, - "repoColorsByName": [ - ["Local", "#6366f1"], - ["Remote", "#f97316"] - ], - "repoHostIdByRepoId": [ - ["repo-1", "local"], - ["repo-2", "ssh:ssh-1"] - ], - "repoIconsByName": [], - "repoIdsByName": [ - ["Local", "repo-1"], - ["Remote", "repo-2"] - ] - }, "1ff13ee8621c": { "name": "repoIconsByName", "ordinal": 4, @@ -279,6 +243,11 @@ } } }, + "53226cb82ec3": { + "name": "hostPlatform", + "ordinal": 15, + "value": "linux" + }, "53dea128b181": { "name": "host.platform#1", "ordinal": 9, @@ -311,11 +280,6 @@ } } }, - "56ab81cb67dc": { - "name": "hostLabelById", - "ordinal": 13, - "value": [["ssh:ssh-1", "SSH"]] - }, "5b781918b903": { "name": "host.platform#1", "ordinal": 9, @@ -348,6 +312,11 @@ } } }, + "6063e534d9db": { + "name": "hostHealthById", + "ordinal": 13, + "value": [] + }, "6134b73f18d0": { "repoColorsByName": [ ["Local", "#6366f1"], @@ -515,11 +484,23 @@ } } }, + "9d20695fdb34": { + "name": "hostPlatform", + "ordinal": 15, + "value": { + "$rpc": "null" + } + }, "b428bce85ee7": { "name": "settings.get#1", "ordinal": 11, "json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}" }, + "bea13a4a2e87": { + "name": "hostLabelById", + "ordinal": 14, + "value": [["ssh:ssh-1", "SSH"]] + }, "c752e67787f9": { "name": "ssh.listTargetSummaries#1", "ordinal": 7, @@ -631,12 +612,25 @@ } } }, - "e9480602a4c6": { - "name": "hostPlatform", - "ordinal": 14, - "value": { + "e841f87c65aa": { + "hostHealthById": [], + "hostLabelById": [["ssh:ssh-1", "SSH"]], + "hostPlatform": { "$rpc": "null" - } + }, + "repoColorsByName": [ + ["Local", "#6366f1"], + ["Remote", "#f97316"] + ], + "repoHostIdByRepoId": [ + ["repo-1", "local"], + ["repo-2", "ssh:ssh-1"] + ], + "repoIconsByName": [], + "repoIdsByName": [ + ["Local", "repo-1"], + ["Remote", "repo-2"] + ] }, "eb79a9b3682a": { "status": "fulfilled", @@ -646,10 +640,23 @@ "$rpc": "undefined" } }, - "f9193cae5824": { - "name": "hostPlatform", - "ordinal": 14, - "value": "linux" + "f4667dda0187": { + "hostHealthById": [], + "hostLabelById": [["ssh:ssh-1", "SSH"]], + "hostPlatform": "linux", + "repoColorsByName": [ + ["Local", "#6366f1"], + ["Remote", "#f97316"] + ], + "repoHostIdByRepoId": [ + ["repo-1", "local"], + ["repo-2", "ssh:ssh-1"] + ], + "repoIconsByName": [], + "repoIdsByName": [ + ["Local", "repo-1"], + ["Remote", "repo-2"] + ] } }, "recording": { @@ -677,14 +684,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "071880b671a1", + "state": "f4667dda0187", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "f9193cae5824" + "6063e534d9db", + "bea13a4a2e87", + "53226cb82ec3" ] } }, @@ -710,14 +718,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "071880b671a1", + "state": "f4667dda0187", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "f9193cae5824" + "6063e534d9db", + "bea13a4a2e87", + "53226cb82ec3" ] } }, @@ -743,14 +752,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "1de50f3b4aac", + "state": "e841f87c65aa", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "e9480602a4c6" + "6063e534d9db", + "bea13a4a2e87", + "9d20695fdb34" ] } }, @@ -776,14 +786,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "1de50f3b4aac", + "state": "e841f87c65aa", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "e9480602a4c6" + "6063e534d9db", + "bea13a4a2e87", + "9d20695fdb34" ] } }, @@ -828,8 +839,9 @@ "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "e9480602a4c6" + "6063e534d9db", + "bea13a4a2e87", + "9d20695fdb34" ] } }, @@ -842,14 +854,15 @@ "mount": "eb79a9b3682a", "load": "c7f150c7a054" }, - "state": "1de50f3b4aac", + "state": "e841f87c65aa", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "e9480602a4c6" + "6063e534d9db", + "bea13a4a2e87", + "9d20695fdb34" ] } }, @@ -863,14 +876,15 @@ "load": "eb79a9b3682a", "disconnect": "eb79a9b3682a" }, - "state": "1de50f3b4aac", + "state": "e841f87c65aa", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "e9480602a4c6" + "6063e534d9db", + "bea13a4a2e87", + "9d20695fdb34" ] } }, @@ -884,14 +898,15 @@ "load": "eb79a9b3682a", "cutover": "eb79a9b3682a" }, - "state": "1de50f3b4aac", + "state": "e841f87c65aa", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "e9480602a4c6" + "6063e534d9db", + "bea13a4a2e87", + "9d20695fdb34" ] } } diff --git a/mobile/rpc-foundation/goldens/settings-repo-cache-expiry.json b/mobile/rpc-foundation/goldens/settings-repo-cache-expiry.json index e9edf4df1a8..57c042b30b8 100644 --- a/mobile/rpc-foundation/goldens/settings-repo-cache-expiry.json +++ b/mobile/rpc-foundation/goldens/settings-repo-cache-expiry.json @@ -4,32 +4,6 @@ "family": "settings.repo-metadata", "namedDeltas": [], "values": { - "00271b9f4a55": { - "name": "repo.list#2", - "ordinal": 15, - "args": [ - { - "name": "method", - "value": "repo.list" - }, - { - "name": "params", - "value": { - "$rpc": "absent" - } - }, - { - "name": "options", - "value": { - "$rpc": "absent" - } - } - ], - "settlement": { - "status": "pending", - "startedAt": 60000 - } - }, "03c9a2ba60b9": { "name": "host.platform#1", "ordinal": 9, @@ -64,23 +38,6 @@ } } }, - "071880b671a1": { - "hostLabelById": [["ssh:ssh-1", "SSH"]], - "hostPlatform": "linux", - "repoColorsByName": [ - ["Local", "#6366f1"], - ["Remote", "#f97316"] - ], - "repoHostIdByRepoId": [ - ["repo-1", "local"], - ["repo-2", "ssh:ssh-1"] - ], - "repoIconsByName": [], - "repoIdsByName": [ - ["Local", "repo-1"], - ["Remote", "repo-2"] - ] - }, "0c4296f215a4": { "name": "settings.get#1", "ordinal": 8, @@ -157,6 +114,11 @@ "ordinal": 4, "value": [] }, + "2053629911e7": { + "name": "repo.list#2", + "ordinal": 17, + "json": "{\"id\":\"frame-5\",\"deviceToken\":\"recording-device\",\"method\":\"repo.list\"}" + }, "2f1b95eac8f1": { "name": "repoColorsByName", "ordinal": 3, @@ -217,10 +179,15 @@ } } }, - "56ab81cb67dc": { - "name": "hostLabelById", + "53226cb82ec3": { + "name": "hostPlatform", + "ordinal": 15, + "value": "linux" + }, + "6063e534d9db": { + "name": "hostHealthById", "ordinal": 13, - "value": [["ssh:ssh-1", "SSH"]] + "value": [] }, "6134b73f18d0": { "repoColorsByName": [ @@ -297,6 +264,37 @@ "status": "pending", "startedAt": 60000 }, + "bea13a4a2e87": { + "name": "hostLabelById", + "ordinal": 14, + "value": [["ssh:ssh-1", "SSH"]] + }, + "c402c05b8b96": { + "name": "repo.list#2", + "ordinal": 16, + "args": [ + { + "name": "method", + "value": "repo.list" + }, + { + "name": "params", + "value": { + "$rpc": "absent" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "pending", + "startedAt": 60000 + } + }, "c752e67787f9": { "name": "ssh.listTargetSummaries#1", "ordinal": 7, @@ -336,11 +334,6 @@ } } }, - "de81371c7ac8": { - "name": "repo.list#2", - "ordinal": 16, - "json": "{\"id\":\"frame-5\",\"deviceToken\":\"recording-device\",\"method\":\"repo.list\"}" - }, "eb79a9b3682a": { "status": "fulfilled", "startedAt": 0, @@ -357,10 +350,23 @@ "$rpc": "undefined" } }, - "f9193cae5824": { - "name": "hostPlatform", - "ordinal": 14, - "value": "linux" + "f4667dda0187": { + "hostHealthById": [], + "hostLabelById": [["ssh:ssh-1", "SSH"]], + "hostPlatform": "linux", + "repoColorsByName": [ + ["Local", "#6366f1"], + ["Remote", "#f97316"] + ], + "repoHostIdByRepoId": [ + ["repo-1", "local"], + ["repo-2", "ssh:ssh-1"] + ], + "repoIconsByName": [], + "repoIdsByName": [ + ["Local", "repo-1"], + ["Remote", "repo-2"] + ] } }, "recording": { @@ -388,14 +394,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "071880b671a1", + "state": "f4667dda0187", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "f9193cae5824" + "6063e534d9db", + "bea13a4a2e87", + "53226cb82ec3" ] } }, @@ -409,14 +416,15 @@ "load": "eb79a9b3682a", "cached-query": "eb79a9b3682a" }, - "state": "071880b671a1", + "state": "f4667dda0187", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "f9193cae5824" + "6063e534d9db", + "bea13a4a2e87", + "53226cb82ec3" ] } }, @@ -431,14 +439,15 @@ "cached-query": "eb79a9b3682a", "warm-query": "f43afee17848" }, - "state": "071880b671a1", + "state": "f4667dda0187", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "f9193cae5824" + "6063e534d9db", + "bea13a4a2e87", + "53226cb82ec3" ] } }, @@ -450,14 +459,14 @@ "c752e67787f9", "0c4296f215a4", "03c9a2ba60b9", - "00271b9f4a55" + "c402c05b8b96" ], "payloads": [ "19b2979d7fc2", "668a7e313975", "b428bce85ee7", "36a96ddbd396", - "de81371c7ac8" + "2053629911e7" ], "settlements": { "mount": "eb79a9b3682a", @@ -466,14 +475,15 @@ "warm-query": "f43afee17848", "expired-query": "ba8a19e3e2b1" }, - "state": "071880b671a1", + "state": "f4667dda0187", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "f9193cae5824" + "6063e534d9db", + "bea13a4a2e87", + "53226cb82ec3" ] } } diff --git a/mobile/rpc-foundation/goldens/settings-repo-metadata-fulfilled.json b/mobile/rpc-foundation/goldens/settings-repo-metadata-fulfilled.json index aabdbe23adb..e3be4ab006b 100644 --- a/mobile/rpc-foundation/goldens/settings-repo-metadata-fulfilled.json +++ b/mobile/rpc-foundation/goldens/settings-repo-metadata-fulfilled.json @@ -38,23 +38,6 @@ } } }, - "071880b671a1": { - "hostLabelById": [["ssh:ssh-1", "SSH"]], - "hostPlatform": "linux", - "repoColorsByName": [ - ["Local", "#6366f1"], - ["Remote", "#f97316"] - ], - "repoHostIdByRepoId": [ - ["repo-1", "local"], - ["repo-2", "ssh:ssh-1"] - ], - "repoIconsByName": [], - "repoIdsByName": [ - ["Local", "repo-1"], - ["Remote", "repo-2"] - ] - }, "0c4296f215a4": { "name": "settings.get#1", "ordinal": 8, @@ -191,10 +174,15 @@ } } }, - "56ab81cb67dc": { - "name": "hostLabelById", + "53226cb82ec3": { + "name": "hostPlatform", + "ordinal": 15, + "value": "linux" + }, + "6063e534d9db": { + "name": "hostHealthById", "ordinal": 13, - "value": [["ssh:ssh-1", "SSH"]] + "value": [] }, "6134b73f18d0": { "repoColorsByName": [ @@ -267,6 +255,11 @@ "ordinal": 11, "json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}" }, + "bea13a4a2e87": { + "name": "hostLabelById", + "ordinal": 14, + "value": [["ssh:ssh-1", "SSH"]] + }, "c752e67787f9": { "name": "ssh.listTargetSummaries#1", "ordinal": 7, @@ -314,10 +307,23 @@ "$rpc": "undefined" } }, - "f9193cae5824": { - "name": "hostPlatform", - "ordinal": 14, - "value": "linux" + "f4667dda0187": { + "hostHealthById": [], + "hostLabelById": [["ssh:ssh-1", "SSH"]], + "hostPlatform": "linux", + "repoColorsByName": [ + ["Local", "#6366f1"], + ["Remote", "#f97316"] + ], + "repoHostIdByRepoId": [ + ["repo-1", "local"], + ["repo-2", "ssh:ssh-1"] + ], + "repoIconsByName": [], + "repoIdsByName": [ + ["Local", "repo-1"], + ["Remote", "repo-2"] + ] } }, "recording": { @@ -345,14 +351,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "071880b671a1", + "state": "f4667dda0187", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "f9193cae5824" + "6063e534d9db", + "bea13a4a2e87", + "53226cb82ec3" ] } } diff --git a/mobile/rpc-foundation/goldens/settings-repo-metadata-icons.json b/mobile/rpc-foundation/goldens/settings-repo-metadata-icons.json index 48777a60761..dcd54acfef5 100644 --- a/mobile/rpc-foundation/goldens/settings-repo-metadata-icons.json +++ b/mobile/rpc-foundation/goldens/settings-repo-metadata-icons.json @@ -4,11 +4,6 @@ "family": "settings.repo-metadata", "namedDeltas": [], "values": { - "12ba3f41d4db": { - "name": "hostPlatform", - "ordinal": 14, - "value": "darwin" - }, "19b2979d7fc2": { "name": "repo.list#1", "ordinal": 2, @@ -49,15 +44,20 @@ "startedAt": 0 } }, + "25b44bca9e45": { + "name": "hostPlatform", + "ordinal": 15, + "value": "darwin" + }, "36a96ddbd396": { "name": "host.platform#1", "ordinal": 12, "json": "{\"id\":\"frame-4\",\"deviceToken\":\"recording-device\",\"method\":\"host.platform\"}" }, - "56ab81cb67dc": { - "name": "hostLabelById", + "6063e534d9db": { + "name": "hostHealthById", "ordinal": 13, - "value": [["ssh:ssh-1", "SSH"]] + "value": [] }, "6338d57f282f": { "repoColorsByName": [ @@ -293,46 +293,13 @@ "ordinal": 11, "json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}" }, - "c752e67787f9": { - "name": "ssh.listTargetSummaries#1", - "ordinal": 7, - "args": [ - { - "name": "method", - "value": "ssh.listTargetSummaries" - }, - { - "name": "params", - "value": { - "$rpc": "absent" - } - }, - { - "name": "options", - "value": { - "$rpc": "absent" - } - } - ], - "settlement": { - "status": "fulfilled", - "startedAt": 0, - "settledAt": 0, - "value": { - "id": "frame-2", - "ok": true, - "result": { - "targets": [ - { - "id": "ssh-1", - "label": "SSH" - } - ] - } - } - } + "bea13a4a2e87": { + "name": "hostLabelById", + "ordinal": 14, + "value": [["ssh:ssh-1", "SSH"]] }, - "e62b46a04ba1": { + "c020f709d835": { + "hostHealthById": [], "hostLabelById": [["ssh:ssh-1", "SSH"]], "hostPlatform": "darwin", "repoColorsByName": [ @@ -376,6 +343,45 @@ ["Avatar", "repo-3"] ] }, + "c752e67787f9": { + "name": "ssh.listTargetSummaries#1", + "ordinal": 7, + "args": [ + { + "name": "method", + "value": "ssh.listTargetSummaries" + }, + { + "name": "params", + "value": { + "$rpc": "absent" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-2", + "ok": true, + "result": { + "targets": [ + { + "id": "ssh-1", + "label": "SSH" + } + ] + } + } + } + }, "eb79a9b3682a": { "status": "fulfilled", "startedAt": 0, @@ -444,14 +450,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "e62b46a04ba1", + "state": "c020f709d835", "effects": [ "19bf6d2469a6", "ae27a5b525fa", "77cbfd1f087c", "6bc5e5b7a259", - "56ab81cb67dc", - "12ba3f41d4db" + "6063e534d9db", + "bea13a4a2e87", + "25b44bca9e45" ] } } diff --git a/mobile/rpc-foundation/goldens/settings-repo-metadata-refuse-after-data.json b/mobile/rpc-foundation/goldens/settings-repo-metadata-refuse-after-data.json index a458cc7adeb..53182455b8d 100644 --- a/mobile/rpc-foundation/goldens/settings-repo-metadata-refuse-after-data.json +++ b/mobile/rpc-foundation/goldens/settings-repo-metadata-refuse-after-data.json @@ -38,23 +38,6 @@ } } }, - "071880b671a1": { - "hostLabelById": [["ssh:ssh-1", "SSH"]], - "hostPlatform": "linux", - "repoColorsByName": [ - ["Local", "#6366f1"], - ["Remote", "#f97316"] - ], - "repoHostIdByRepoId": [ - ["repo-1", "local"], - ["repo-2", "ssh:ssh-1"] - ], - "repoIconsByName": [], - "repoIdsByName": [ - ["Local", "repo-1"], - ["Remote", "repo-2"] - ] - }, "0c4296f215a4": { "name": "settings.get#1", "ordinal": 8, @@ -95,6 +78,11 @@ } } }, + "0ef3d910f42e": { + "name": "hostLabelById", + "ordinal": 29, + "value": [["ssh:ssh-1", "SSH"]] + }, "19b2979d7fc2": { "name": "repo.list#1", "ordinal": 2, @@ -131,44 +119,10 @@ "ordinal": 4, "value": [] }, - "2a201bc09f96": { - "name": "ssh.listTargetSummaries#2", - "ordinal": 21, - "args": [ - { - "name": "method", - "value": "ssh.listTargetSummaries" - }, - { - "name": "params", - "value": { - "$rpc": "absent" - } - }, - { - "name": "options", - "value": { - "$rpc": "absent" - } - } - ], - "settlement": { - "status": "fulfilled", - "startedAt": 0, - "settledAt": 0, - "value": { - "id": "frame-6", - "ok": true, - "result": { - "targets": [ - { - "id": "ssh-1", - "label": "SSH" - } - ] - } - } - } + "2053629911e7": { + "name": "repo.list#2", + "ordinal": 17, + "json": "{\"id\":\"frame-5\",\"deviceToken\":\"recording-device\",\"method\":\"repo.list\"}" }, "2f1b95eac8f1": { "name": "repoColorsByName", @@ -178,27 +132,16 @@ ["Remote", "#f97316"] ] }, - "339b07cb7b7e": { - "name": "repoHostIdByRepoId", - "ordinal": 20, - "value": [ - ["repo-1", "local"], - ["repo-2", "ssh:ssh-1"] - ] - }, - "35a231ef3ff8": { - "name": "repoIdsByName", - "ordinal": 19, - "value": [ - ["Local", "repo-1"], - ["Remote", "repo-2"] - ] - }, "36a96ddbd396": { "name": "host.platform#1", "ordinal": 12, "json": "{\"id\":\"frame-4\",\"deviceToken\":\"recording-device\",\"method\":\"host.platform\"}" }, + "376d3874239c": { + "name": "repoIconsByName", + "ordinal": 19, + "value": [] + }, "4526bff18b74": { "name": "repo.list#1", "ordinal": 1, @@ -246,37 +189,9 @@ } } }, - "56ab81cb67dc": { - "name": "hostLabelById", - "ordinal": 13, - "value": [["ssh:ssh-1", "SSH"]] - }, - "5b7bcd015aba": { - "name": "repoColorsByName", - "ordinal": 17, - "value": [ - ["Local", "#6366f1"], - ["Remote", "#f97316"] - ] - }, - "6134b73f18d0": { - "repoColorsByName": [ - ["Local", "#6366f1"], - ["Remote", "#f97316"] - ], - "repoHostIdByRepoId": [ - ["repo-1", "local"], - ["repo-2", "ssh:ssh-1"] - ], - "repoIconsByName": [], - "repoIdsByName": [ - ["Local", "repo-1"], - ["Remote", "repo-2"] - ] - }, - "64986b5eb58e": { + "455855a85ab4": { "name": "settings.get#2", - "ordinal": 22, + "ordinal": 23, "args": [ { "name": "method", @@ -309,24 +224,240 @@ } } }, - "64aa5287977c": { - "name": "hostLabelById", - "ordinal": 27, - "value": [["ssh:ssh-1", "SSH"]] + "4771e3dbbbcf": { + "name": "host.platform#2", + "ordinal": 24, + "args": [ + { + "name": "method", + "value": "host.platform" + }, + { + "name": "params", + "value": { + "$rpc": "absent" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-8", + "ok": true, + "result": { + "platform": "linux" + } + } + } + }, + "53226cb82ec3": { + "name": "hostPlatform", + "ordinal": 15, + "value": "linux" + }, + "6063e534d9db": { + "name": "hostHealthById", + "ordinal": 13, + "value": [] + }, + "6134b73f18d0": { + "repoColorsByName": [ + ["Local", "#6366f1"], + ["Remote", "#f97316"] + ], + "repoHostIdByRepoId": [ + ["repo-1", "local"], + ["repo-2", "ssh:ssh-1"] + ], + "repoIconsByName": [], + "repoIdsByName": [ + ["Local", "repo-1"], + ["Remote", "repo-2"] + ] }, "668a7e313975": { "name": "ssh.listTargetSummaries#1", "ordinal": 10, "json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"ssh.listTargetSummaries\"}" }, - "673a2bbe62e3": { - "name": "hostPlatform", + "7e772c1c111d": { + "name": "repoIdsByName", + "ordinal": 5, + "value": [ + ["Local", "repo-1"], + ["Remote", "repo-2"] + ] + }, + "8c5c812580ad": { + "name": "host.platform#2", + "ordinal": 27, + "json": "{\"id\":\"frame-8\",\"deviceToken\":\"recording-device\",\"method\":\"host.platform\"}" + }, + "8ef3b348c179": { + "name": "repoHostIdByRepoId", + "ordinal": 6, + "value": [ + ["repo-1", "local"], + ["repo-2", "ssh:ssh-1"] + ] + }, + "9270aeb7d9c6": { + "status": "pending", + "startedAt": 0 + }, + "96e0c54d86b0": { + "name": "settings.get#1", + "ordinal": 8, + "args": [ + { + "name": "method", + "value": "settings.get" + }, + { + "name": "params", + "value": { + "$rpc": "absent" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "pending", + "startedAt": 0 + } + }, + "a67416f78b93": { + "name": "hostHealthById", "ordinal": 28, + "value": [] + }, + "b428bce85ee7": { + "name": "settings.get#1", + "ordinal": 11, + "json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}" + }, + "bea13a4a2e87": { + "name": "hostLabelById", + "ordinal": 14, + "value": [["ssh:ssh-1", "SSH"]] + }, + "c497d2d846c9": { + "name": "ssh.listTargetSummaries#2", + "ordinal": 25, + "json": "{\"id\":\"frame-6\",\"deviceToken\":\"recording-device\",\"method\":\"ssh.listTargetSummaries\"}" + }, + "c752e67787f9": { + "name": "ssh.listTargetSummaries#1", + "ordinal": 7, + "args": [ + { + "name": "method", + "value": "ssh.listTargetSummaries" + }, + { + "name": "params", + "value": { + "$rpc": "absent" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-2", + "ok": true, + "result": { + "targets": [ + { + "id": "ssh-1", + "label": "SSH" + } + ] + } + } + } + }, + "ceef481be25c": { + "name": "hostPlatform", + "ordinal": 30, "value": "linux" }, - "7d3ba1e92188": { + "d51254d2537f": { + "name": "settings.get#2", + "ordinal": 26, + "json": "{\"id\":\"frame-7\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}" + }, + "d569b5b2500f": { + "name": "ssh.listTargetSummaries#2", + "ordinal": 22, + "args": [ + { + "name": "method", + "value": "ssh.listTargetSummaries" + }, + { + "name": "params", + "value": { + "$rpc": "absent" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "id": "frame-6", + "ok": true, + "result": { + "targets": [ + { + "id": "ssh-1", + "label": "SSH" + } + ] + } + } + } + }, + "da7cf9752395": { + "name": "repoColorsByName", + "ordinal": 18, + "value": [ + ["Local", "#6366f1"], + ["Remote", "#f97316"] + ] + }, + "df78d2202264": { "name": "repo.list#2", - "ordinal": 15, + "ordinal": 16, "args": [ { "name": "method", @@ -371,22 +502,51 @@ } } }, - "7e772c1c111d": { + "ea2ced0895f3": { + "name": "repoHostIdByRepoId", + "ordinal": 21, + "value": [ + ["repo-1", "local"], + ["repo-2", "ssh:ssh-1"] + ] + }, + "eb79a9b3682a": { + "status": "fulfilled", + "startedAt": 0, + "settledAt": 0, + "value": { + "$rpc": "undefined" + } + }, + "eb885d2ceee5": { "name": "repoIdsByName", - "ordinal": 5, + "ordinal": 20, "value": [ ["Local", "repo-1"], ["Remote", "repo-2"] ] }, - "8800889a5f18": { - "name": "host.platform#2", - "ordinal": 26, - "json": "{\"id\":\"frame-8\",\"deviceToken\":\"recording-device\",\"method\":\"host.platform\"}" + "f4667dda0187": { + "hostHealthById": [], + "hostLabelById": [["ssh:ssh-1", "SSH"]], + "hostPlatform": "linux", + "repoColorsByName": [ + ["Local", "#6366f1"], + ["Remote", "#f97316"] + ], + "repoHostIdByRepoId": [ + ["repo-1", "local"], + ["repo-2", "ssh:ssh-1"] + ], + "repoIconsByName": [], + "repoIdsByName": [ + ["Local", "repo-1"], + ["Remote", "repo-2"] + ] }, - "8dd3e3e2a847": { + "fa585aceeec2": { "name": "repo.list#2", - "ordinal": 15, + "ordinal": 16, "args": [ { "name": "method", @@ -409,155 +569,6 @@ "status": "pending", "startedAt": 0 } - }, - "8ef3b348c179": { - "name": "repoHostIdByRepoId", - "ordinal": 6, - "value": [ - ["repo-1", "local"], - ["repo-2", "ssh:ssh-1"] - ] - }, - "9270aeb7d9c6": { - "status": "pending", - "startedAt": 0 - }, - "96e0c54d86b0": { - "name": "settings.get#1", - "ordinal": 8, - "args": [ - { - "name": "method", - "value": "settings.get" - }, - { - "name": "params", - "value": { - "$rpc": "absent" - } - }, - { - "name": "options", - "value": { - "$rpc": "absent" - } - } - ], - "settlement": { - "status": "pending", - "startedAt": 0 - } - }, - "983e55643949": { - "name": "settings.get#2", - "ordinal": 25, - "json": "{\"id\":\"frame-7\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}" - }, - "b428bce85ee7": { - "name": "settings.get#1", - "ordinal": 11, - "json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}" - }, - "be773224294b": { - "name": "repoIconsByName", - "ordinal": 18, - "value": [] - }, - "c752e67787f9": { - "name": "ssh.listTargetSummaries#1", - "ordinal": 7, - "args": [ - { - "name": "method", - "value": "ssh.listTargetSummaries" - }, - { - "name": "params", - "value": { - "$rpc": "absent" - } - }, - { - "name": "options", - "value": { - "$rpc": "absent" - } - } - ], - "settlement": { - "status": "fulfilled", - "startedAt": 0, - "settledAt": 0, - "value": { - "id": "frame-2", - "ok": true, - "result": { - "targets": [ - { - "id": "ssh-1", - "label": "SSH" - } - ] - } - } - } - }, - "d1f0a64c0651": { - "name": "ssh.listTargetSummaries#2", - "ordinal": 24, - "json": "{\"id\":\"frame-6\",\"deviceToken\":\"recording-device\",\"method\":\"ssh.listTargetSummaries\"}" - }, - "de81371c7ac8": { - "name": "repo.list#2", - "ordinal": 16, - "json": "{\"id\":\"frame-5\",\"deviceToken\":\"recording-device\",\"method\":\"repo.list\"}" - }, - "eb79a9b3682a": { - "status": "fulfilled", - "startedAt": 0, - "settledAt": 0, - "value": { - "$rpc": "undefined" - } - }, - "f9193cae5824": { - "name": "hostPlatform", - "ordinal": 14, - "value": "linux" - }, - "fc50a8ac2e64": { - "name": "host.platform#2", - "ordinal": 23, - "args": [ - { - "name": "method", - "value": "host.platform" - }, - { - "name": "params", - "value": { - "$rpc": "absent" - } - }, - { - "name": "options", - "value": { - "$rpc": "absent" - } - } - ], - "settlement": { - "status": "fulfilled", - "startedAt": 0, - "settledAt": 0, - "value": { - "id": "frame-8", - "ok": true, - "result": { - "platform": "linux" - } - } - } } }, "recording": { @@ -585,14 +596,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "071880b671a1", + "state": "f4667dda0187", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "f9193cae5824" + "6063e534d9db", + "bea13a4a2e87", + "53226cb82ec3" ] } }, @@ -605,14 +617,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "071880b671a1", + "state": "f4667dda0187", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "f9193cae5824" + "6063e534d9db", + "bea13a4a2e87", + "53226cb82ec3" ] } }, @@ -624,28 +637,29 @@ "c752e67787f9", "0c4296f215a4", "03c9a2ba60b9", - "8dd3e3e2a847" + "fa585aceeec2" ], "payloads": [ "19b2979d7fc2", "668a7e313975", "b428bce85ee7", "36a96ddbd396", - "de81371c7ac8" + "2053629911e7" ], "settlements": { "mount": "eb79a9b3682a", "load": "eb79a9b3682a", "reload": "9270aeb7d9c6" }, - "state": "071880b671a1", + "state": "f4667dda0187", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "f9193cae5824" + "6063e534d9db", + "bea13a4a2e87", + "53226cb82ec3" ] } }, @@ -657,40 +671,42 @@ "c752e67787f9", "0c4296f215a4", "03c9a2ba60b9", - "7d3ba1e92188", - "2a201bc09f96", - "64986b5eb58e", - "fc50a8ac2e64" + "df78d2202264", + "d569b5b2500f", + "455855a85ab4", + "4771e3dbbbcf" ], "payloads": [ "19b2979d7fc2", "668a7e313975", "b428bce85ee7", "36a96ddbd396", - "de81371c7ac8", - "d1f0a64c0651", - "983e55643949", - "8800889a5f18" + "2053629911e7", + "c497d2d846c9", + "d51254d2537f", + "8c5c812580ad" ], "settlements": { "mount": "eb79a9b3682a", "load": "eb79a9b3682a", "reload": "eb79a9b3682a" }, - "state": "071880b671a1", + "state": "f4667dda0187", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "f9193cae5824", - "5b7bcd015aba", - "be773224294b", - "35a231ef3ff8", - "339b07cb7b7e", - "64aa5287977c", - "673a2bbe62e3" + "6063e534d9db", + "bea13a4a2e87", + "53226cb82ec3", + "da7cf9752395", + "376d3874239c", + "eb885d2ceee5", + "ea2ced0895f3", + "a67416f78b93", + "0ef3d910f42e", + "ceef481be25c" ] } } diff --git a/mobile/rpc-foundation/goldens/settings-repo-metadata-refused.json b/mobile/rpc-foundation/goldens/settings-repo-metadata-refused.json index 0e850d65689..17e304bc7bc 100644 --- a/mobile/rpc-foundation/goldens/settings-repo-metadata-refused.json +++ b/mobile/rpc-foundation/goldens/settings-repo-metadata-refused.json @@ -38,23 +38,6 @@ } } }, - "071880b671a1": { - "hostLabelById": [["ssh:ssh-1", "SSH"]], - "hostPlatform": "linux", - "repoColorsByName": [ - ["Local", "#6366f1"], - ["Remote", "#f97316"] - ], - "repoHostIdByRepoId": [ - ["repo-1", "local"], - ["repo-2", "ssh:ssh-1"] - ], - "repoIconsByName": [], - "repoIdsByName": [ - ["Local", "repo-1"], - ["Remote", "repo-2"] - ] - }, "19b2979d7fc2": { "name": "repo.list#1", "ordinal": 2, @@ -186,10 +169,15 @@ } } }, - "56ab81cb67dc": { - "name": "hostLabelById", + "53226cb82ec3": { + "name": "hostPlatform", + "ordinal": 15, + "value": "linux" + }, + "6063e534d9db": { + "name": "hostHealthById", "ordinal": 13, - "value": [["ssh:ssh-1", "SSH"]] + "value": [] }, "6134b73f18d0": { "repoColorsByName": [ @@ -262,6 +250,11 @@ "ordinal": 11, "json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}" }, + "bea13a4a2e87": { + "name": "hostLabelById", + "ordinal": 14, + "value": [["ssh:ssh-1", "SSH"]] + }, "c752e67787f9": { "name": "ssh.listTargetSummaries#1", "ordinal": 7, @@ -309,10 +302,23 @@ "$rpc": "undefined" } }, - "f9193cae5824": { - "name": "hostPlatform", - "ordinal": 14, - "value": "linux" + "f4667dda0187": { + "hostHealthById": [], + "hostLabelById": [["ssh:ssh-1", "SSH"]], + "hostPlatform": "linux", + "repoColorsByName": [ + ["Local", "#6366f1"], + ["Remote", "#f97316"] + ], + "repoHostIdByRepoId": [ + ["repo-1", "local"], + ["repo-2", "ssh:ssh-1"] + ], + "repoIconsByName": [], + "repoIdsByName": [ + ["Local", "repo-1"], + ["Remote", "repo-2"] + ] } }, "recording": { @@ -340,14 +346,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "071880b671a1", + "state": "f4667dda0187", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "f9193cae5824" + "6063e534d9db", + "bea13a4a2e87", + "53226cb82ec3" ] } } diff --git a/mobile/rpc-foundation/goldens/settings-repo-metadata-single-host.json b/mobile/rpc-foundation/goldens/settings-repo-metadata-single-host.json index f3d00fb95da..277522dfed5 100644 --- a/mobile/rpc-foundation/goldens/settings-repo-metadata-single-host.json +++ b/mobile/rpc-foundation/goldens/settings-repo-metadata-single-host.json @@ -9,6 +9,32 @@ "ordinal": 2, "json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"repo.list\"}" }, + "1a160fc2d137": { + "name": "host.platform#1", + "ordinal": 9, + "args": [ + { + "name": "method", + "value": "host.platform" + }, + { + "name": "params", + "value": { + "$rpc": "absent" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "pending", + "startedAt": 0 + } + }, "1ff13ee8621c": { "name": "repoIconsByName", "ordinal": 4, @@ -29,6 +55,11 @@ ["Remote folder", "repo-2"] ] }, + "36a96ddbd396": { + "name": "host.platform#1", + "ordinal": 12, + "json": "{\"id\":\"frame-4\",\"deviceToken\":\"recording-device\",\"method\":\"host.platform\"}" + }, "4b6956e45bac": { "name": "repo.list#1", "ordinal": 1, @@ -74,6 +105,110 @@ } } }, + "4bf1ed33de14": { + "name": "ssh.listTargetSummaries#1", + "ordinal": 7, + "args": [ + { + "name": "method", + "value": "ssh.listTargetSummaries" + }, + { + "name": "params", + "value": { + "$rpc": "absent" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "rejected", + "startedAt": 0, + "settledAt": 0, + "error": { + "category": "Error", + "message": "Connection closed", + "isRpcDeliveryUnknown": true + } + } + }, + "53dea128b181": { + "name": "host.platform#1", + "ordinal": 9, + "args": [ + { + "name": "method", + "value": "host.platform" + }, + { + "name": "params", + "value": { + "$rpc": "absent" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "rejected", + "startedAt": 0, + "settledAt": 0, + "error": { + "category": "Error", + "message": "Connection closed", + "isRpcDeliveryUnknown": true + } + } + }, + "6063e534d9db": { + "name": "hostHealthById", + "ordinal": 13, + "value": [] + }, + "668a7e313975": { + "name": "ssh.listTargetSummaries#1", + "ordinal": 10, + "json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"ssh.listTargetSummaries\"}" + }, + "6a227ad27189": { + "name": "ssh.listTargetSummaries#1", + "ordinal": 7, + "args": [ + { + "name": "method", + "value": "ssh.listTargetSummaries" + }, + { + "name": "params", + "value": { + "$rpc": "absent" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "pending", + "startedAt": 0 + } + }, + "9270aeb7d9c6": { + "status": "pending", + "startedAt": 0 + }, "937107a86348": { "name": "repoIdsByName", "ordinal": 5, @@ -82,6 +217,39 @@ ["Remote folder", "repo-2"] ] }, + "96e0c54d86b0": { + "name": "settings.get#1", + "ordinal": 8, + "args": [ + { + "name": "method", + "value": "settings.get" + }, + { + "name": "params", + "value": { + "$rpc": "absent" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "pending", + "startedAt": 0 + } + }, + "9d20695fdb34": { + "name": "hostPlatform", + "ordinal": 15, + "value": { + "$rpc": "null" + } + }, "a086cfd03185": { "name": "repoHostIdByRepoId", "ordinal": 6, @@ -90,6 +258,48 @@ ["repo-2", "ssh:ssh-1"] ] }, + "b428bce85ee7": { + "name": "settings.get#1", + "ordinal": 11, + "json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}" + }, + "daecdc0e521b": { + "name": "hostLabelById", + "ordinal": 14, + "value": [] + }, + "dd3998116e8e": { + "name": "settings.get#1", + "ordinal": 8, + "args": [ + { + "name": "method", + "value": "settings.get" + }, + { + "name": "params", + "value": { + "$rpc": "absent" + } + }, + { + "name": "options", + "value": { + "$rpc": "absent" + } + } + ], + "settlement": { + "status": "rejected", + "startedAt": 0, + "settledAt": 0, + "error": { + "category": "Error", + "message": "Connection closed", + "isRpcDeliveryUnknown": true + } + } + }, "eb79a9b3682a": { "status": "fulfilled", "startedAt": 0, @@ -113,14 +323,35 @@ { "id": "single-host-without-label-lookups", "observation": { - "sender": ["4b6956e45bac"], - "payloads": ["19b2979d7fc2"], + "sender": ["4b6956e45bac", "6a227ad27189", "96e0c54d86b0", "1a160fc2d137"], + "payloads": ["19b2979d7fc2", "668a7e313975", "b428bce85ee7", "36a96ddbd396"], + "settlements": { + "mount": "eb79a9b3682a", + "load": "9270aeb7d9c6" + }, + "state": "2bd489a9fa29", + "effects": ["fc0785cc0b08", "1ff13ee8621c", "937107a86348", "a086cfd03185"] + } + }, + { + "id": "cleanup", + "observation": { + "sender": ["4b6956e45bac", "4bf1ed33de14", "dd3998116e8e", "53dea128b181"], + "payloads": ["19b2979d7fc2", "668a7e313975", "b428bce85ee7", "36a96ddbd396"], "settlements": { "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, "state": "2bd489a9fa29", - "effects": ["fc0785cc0b08", "1ff13ee8621c", "937107a86348", "a086cfd03185"] + "effects": [ + "fc0785cc0b08", + "1ff13ee8621c", + "937107a86348", + "a086cfd03185", + "6063e534d9db", + "daecdc0e521b", + "9d20695fdb34" + ] } } ] diff --git a/mobile/rpc-foundation/goldens/settings-repo-metadata-transport-error.json b/mobile/rpc-foundation/goldens/settings-repo-metadata-transport-error.json index 3dec40ec45f..965d2f81320 100644 --- a/mobile/rpc-foundation/goldens/settings-repo-metadata-transport-error.json +++ b/mobile/rpc-foundation/goldens/settings-repo-metadata-transport-error.json @@ -38,23 +38,6 @@ } } }, - "071880b671a1": { - "hostLabelById": [["ssh:ssh-1", "SSH"]], - "hostPlatform": "linux", - "repoColorsByName": [ - ["Local", "#6366f1"], - ["Remote", "#f97316"] - ], - "repoHostIdByRepoId": [ - ["repo-1", "local"], - ["repo-2", "ssh:ssh-1"] - ], - "repoIconsByName": [], - "repoIdsByName": [ - ["Local", "repo-1"], - ["Remote", "repo-2"] - ] - }, "19b2979d7fc2": { "name": "repo.list#1", "ordinal": 2, @@ -151,10 +134,10 @@ } } }, - "56ab81cb67dc": { - "name": "hostLabelById", - "ordinal": 13, - "value": [["ssh:ssh-1", "SSH"]] + "53226cb82ec3": { + "name": "hostPlatform", + "ordinal": 15, + "value": "linux" }, "6021c58b2fd2": { "name": "settings.get#1", @@ -188,6 +171,11 @@ } } }, + "6063e534d9db": { + "name": "hostHealthById", + "ordinal": 13, + "value": [] + }, "6134b73f18d0": { "repoColorsByName": [ ["Local", "#6366f1"], @@ -259,6 +247,11 @@ "ordinal": 11, "json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}" }, + "bea13a4a2e87": { + "name": "hostLabelById", + "ordinal": 14, + "value": [["ssh:ssh-1", "SSH"]] + }, "c752e67787f9": { "name": "ssh.listTargetSummaries#1", "ordinal": 7, @@ -306,10 +299,23 @@ "$rpc": "undefined" } }, - "f9193cae5824": { - "name": "hostPlatform", - "ordinal": 14, - "value": "linux" + "f4667dda0187": { + "hostHealthById": [], + "hostLabelById": [["ssh:ssh-1", "SSH"]], + "hostPlatform": "linux", + "repoColorsByName": [ + ["Local", "#6366f1"], + ["Remote", "#f97316"] + ], + "repoHostIdByRepoId": [ + ["repo-1", "local"], + ["repo-2", "ssh:ssh-1"] + ], + "repoIconsByName": [], + "repoIdsByName": [ + ["Local", "repo-1"], + ["Remote", "repo-2"] + ] } }, "recording": { @@ -337,14 +343,15 @@ "mount": "eb79a9b3682a", "load": "eb79a9b3682a" }, - "state": "071880b671a1", + "state": "f4667dda0187", "effects": [ "2f1b95eac8f1", "1ff13ee8621c", "7e772c1c111d", "8ef3b348c179", - "56ab81cb67dc", - "f9193cae5824" + "6063e534d9db", + "bea13a4a2e87", + "53226cb82ec3" ] } } diff --git a/mobile/src/components/WorktreeListRow.test.ts b/mobile/src/components/WorktreeListRow.test.ts index 7b6d222d29e..722202d004b 100644 --- a/mobile/src/components/WorktreeListRow.test.ts +++ b/mobile/src/components/WorktreeListRow.test.ts @@ -244,7 +244,32 @@ describe('memoized worktree rows', () => { ) }) expect(textNodes()).toContain('openclaw') + expect(textNodes().some((text) => text.includes('Disconnected'))).toBe(false) expect(renderer!.root.findAllByType('Server' as never)).toHaveLength(1) + + await act(async () => + renderer!.update( + createElement(ListRowHarness, { + item: { + ...baseItem, + hostId: 'ssh:ssh-1', + hostContextLabel: 'openclaw', + hostContextHealthLabel: 'Disconnected' + }, + now: 2_000 + }) + ) + ) + // Separate nodes: the name truncates while the health word stays whole. + expect(textNodes()).toEqual(expect.arrayContaining(['openclaw', '· Disconnected'])) + const healthText = renderer!.root + .findAllByType('Text' as never) + .find((node) => node.props.children === '· Disconnected') + expect([healthText?.props.style].flat()).toContainEqual({ flexShrink: 0 }) + const announcedBadges = renderer!.root.findAll( + (node) => node.props.accessibilityLabel === 'openclaw, Disconnected' + ) + expect(announcedBadges).toHaveLength(1) expect(renderer!.root.findAllByType('Monitor' as never)).toHaveLength(0) await act(async () => diff --git a/mobile/src/components/WorktreeListRow.tsx b/mobile/src/components/WorktreeListRow.tsx index 98684eab449..0da38990012 100644 --- a/mobile/src/components/WorktreeListRow.tsx +++ b/mobile/src/components/WorktreeListRow.tsx @@ -36,6 +36,8 @@ export type WorktreeListRowItem = { hostContextLabel?: string /** Resolved host for the display label; present when legacy rows omit hostId. */ hostContextHostId?: ExecutionHostId + /** The host's health word, e.g. "Disconnected"; absent while the host is healthy. */ + hostContextHealthLabel?: string repo: string branch: string displayName: string @@ -165,7 +167,12 @@ function WorktreeListRowComponent({ )} {item.hostContextLabel ? ( - + {/* Rows from hosts that predate hostId stamping are local: a remote row always carries one. */} {(parseExecutionHostId(item.hostContextHostId ?? item.hostId)?.kind ?? 'local') === 'local' ? ( @@ -176,6 +183,12 @@ function WorktreeListRowComponent({ {item.hostContextLabel} + {/* Why separate Text: the name truncates first so the health word stays readable. */} + {item.hostContextHealthLabel ? ( + + {`· ${item.hostContextHealthLabel}`} + + ) : null} ) : null} {/* Repo glyph+name only when not already grouped under this repo; @@ -341,6 +354,9 @@ const styles = StyleSheet.create({ hostBadgeText: { flexShrink: 1 }, + hostHealthText: { + flexShrink: 0 + }, lineageToggle: { alignSelf: 'flex-start', flexDirection: 'row', diff --git a/mobile/src/host-screen/host-screen-reply-schema.test.ts b/mobile/src/host-screen/host-screen-reply-schema.test.ts index 0f36f86e3d4..2d1d152a9f0 100644 --- a/mobile/src/host-screen/host-screen-reply-schema.test.ts +++ b/mobile/src/host-screen/host-screen-reply-schema.test.ts @@ -83,6 +83,20 @@ describe('host screen reply schemas', () => { ).toEqual([{ id: 't', label: 'T' }]) }) + it('keeps SSH lifecycle fields raw and drops a malformed one without losing the target', () => { + expect( + hostSshTargetSummariesSchema.parse({ + targets: [ + { id: 't', label: 'T', connected: false, connectionStatus: 'hibernating' }, + { id: 'u', label: 'U', connected: 'no', connectionStatus: 7 } + ] + }) + ).toEqual([ + { id: 't', label: 'T', connected: false, connectionStatus: 'hibernating' }, + { id: 'u', label: 'U' } + ]) + }) + it('answers the empty target list for a reply that is no object at all', () => { // Why: the label write runs before the platform write in the same sequence, so a throw here // would take the platform down with it. readSshTargets answered [] for every one of these. diff --git a/mobile/src/host-screen/host-screen-reply-schema.ts b/mobile/src/host-screen/host-screen-reply-schema.ts index ddc72e29966..42869919561 100644 --- a/mobile/src/host-screen/host-screen-reply-schema.ts +++ b/mobile/src/host-screen/host-screen-reply-schema.ts @@ -93,7 +93,15 @@ export const hostSshTargetSummariesSchema = z .looseObject({ targets: salvagedOptional( 'targets', - salvagingArray(z.looseObject({ id: z.string(), label: z.string() })) + salvagingArray( + z.looseObject({ + id: z.string(), + label: z.string(), + // Read raw: an arm a newer desktop adds must leave the badge as it is, not read as a verdict. + connected: salvagedOptional('connected', z.boolean()), + connectionStatus: salvagedOptional('connectionStatus', z.string()) + }) + ) ) }) .transform((reply) => reply.targets ?? []) diff --git a/mobile/src/host-screen/host-show-pinned-in-groups.test.ts b/mobile/src/host-screen/host-show-pinned-in-groups.test.ts index 29e6c8db826..376b8dbebc2 100644 --- a/mobile/src/host-screen/host-show-pinned-in-groups.test.ts +++ b/mobile/src/host-screen/host-show-pinned-in-groups.test.ts @@ -48,6 +48,7 @@ function screenState(clientRef: ClientRef, applied: boolean[]) { 'setCatalogError', 'setError', 'setHostLabelById', + 'setHostHealthById', 'setHostName', 'setHostPlatform', 'setHostStoredDescriptor', diff --git a/mobile/src/host-screen/host-ssh-health-badge-wiring.test.ts b/mobile/src/host-screen/host-ssh-health-badge-wiring.test.ts new file mode 100644 index 00000000000..ffd7e73f63a --- /dev/null +++ b/mobile/src/host-screen/host-ssh-health-badge-wiring.test.ts @@ -0,0 +1,135 @@ +import { createElement } from 'react' +import { act, create } from 'react-test-renderer' +import { describe, expect, it, vi } from 'vitest' + +vi.mock('../cache/worktree-cache', () => ({ + getCachedWorktrees: () => null, + setCachedWorktrees: () => {} +})) +vi.mock('../cache/repo-cache', () => ({ setCachedRepos: () => {} })) + +import { FakeSession } from '../transport/mobile-endpoint-supervisor-test-fakes' +import type { RpcResponse } from '../transport/types' +import type { Worktree } from '../worktree/workspace-list-types' +import { useHostDisplayWorktrees } from './use-host-display-worktrees' +import { useHostRepoMetadata } from './use-host-repo-metadata' +import { useHostScreenState } from './use-host-screen-state' + +function reply(result: unknown): RpcResponse { + return { id: 'reply', ok: true, result, _meta: { runtimeId: 'runtime' } } +} + +function row(worktreeId: string, repoId: string): Worktree { + return { + workspaceKind: 'git', + worktreeId, + repoId, + repo: repoId, + branch: 'main', + displayName: worktreeId, + path: `/home/me/${worktreeId}`, + liveTerminalCount: 0, + hasAttachedPty: false, + preview: '', + unread: false, + isPinned: false, + linkedPR: null + } +} + +// Drives the screen's own state, metadata refresh and row projection against one desktop reply set. +async function displayedRows(args: { + repos: { id: string; displayName: string; connectionId?: string }[] + rows: Worktree[] + sshTarget: Record + /** The catalog a second refresh answers, once the first has applied. */ + thenRepos?: { id: string; displayName: string; connectionId?: string }[] +}): Promise { + let repos = args.repos + const client = new FakeSession('connected') + client.sendRequest.mockImplementation(async (method: string) => { + switch (method) { + case 'repo.list': + return reply({ repos }) + case 'ssh.listTargetSummaries': + return reply({ targets: [{ id: 'devbox', label: 'devbox', ...args.sshTarget }] }) + case 'host.platform': + return reply({ platform: 'darwin' }) + default: + return reply({}) + } + }) + const seen: { rows: Worktree[]; refresh?: (options: { force: boolean }) => Promise } = { + rows: [] + } + let setWorktrees: ((rows: Worktree[]) => void) | undefined + function Probe(): null { + const state = useHostScreenState('host-1', undefined) + state.clientRef.current = client + setWorktrees = state.setWorktrees + seen.refresh = useHostRepoMetadata({ client, connState: 'connected', hostId: 'host-1', state }) + seen.rows = useHostDisplayWorktrees('connected', state) + return null + } + await act(async () => { + create(createElement(Probe)) + }) + await act(async () => { + setWorktrees?.(args.rows) + await seen.refresh?.({ force: true }) + }) + if (args.thenRepos) { + repos = args.thenRepos + await act(async () => { + await seen.refresh?.({ force: true }) + }) + } + return seen.rows +} + +describe('SSH host health on the host list rows', () => { + it('names an unreachable SSH host with its health even when every row is on it', async () => { + const rows = await displayedRows({ + repos: [{ id: 'repo-ssh', displayName: 'api', connectionId: 'devbox' }], + rows: [row('a', 'repo-ssh'), row('b', 'repo-ssh')], + sshTarget: { connected: false, connectionStatus: 'disconnected' } + }) + expect(rows.map((r) => [r.hostContextLabel, r.hostContextHealthLabel])).toEqual([ + ['devbox', 'Disconnected'], + ['devbox', 'Disconnected'] + ]) + }) + + it('carries the health beside the label in a mixed-host list', async () => { + const rows = await displayedRows({ + repos: [ + { id: 'repo-local', displayName: 'web' }, + { id: 'repo-ssh', displayName: 'api', connectionId: 'devbox' } + ], + rows: [row('a', 'repo-local'), row('b', 'repo-ssh')], + sshTarget: { connected: false, connectionStatus: 'reconnecting' } + }) + expect(rows.map((r) => r.hostContextHealthLabel)).toEqual([undefined, 'Connecting']) + }) + + it('drops the badge once the SSH host leaves the catalog', async () => { + const rows = await displayedRows({ + repos: [{ id: 'repo-ssh', displayName: 'api', connectionId: 'devbox' }], + rows: [{ ...row('a', 'repo-ssh'), hostId: 'ssh:devbox' }], + sshTarget: { connected: false, connectionStatus: 'disconnected' }, + thenRepos: [{ id: 'repo-local', displayName: 'web' }] + }) + expect(rows[0].hostContextLabel).toBeUndefined() + expect(rows[0].hostContextHealthLabel).toBeUndefined() + }) + + it('keeps a healthy single-host list bare', async () => { + const rows = await displayedRows({ + repos: [{ id: 'repo-ssh', displayName: 'api', connectionId: 'devbox' }], + rows: [row('a', 'repo-ssh')], + sshTarget: { connected: true, connectionStatus: 'connected' } + }) + expect(rows[0].hostContextLabel).toBeUndefined() + expect(rows[0].hostContextHealthLabel).toBeUndefined() + }) +}) diff --git a/mobile/src/host-screen/use-host-display-worktrees.ts b/mobile/src/host-screen/use-host-display-worktrees.ts new file mode 100644 index 00000000000..3a24e56356d --- /dev/null +++ b/mobile/src/host-screen/use-host-display-worktrees.ts @@ -0,0 +1,33 @@ +import { useMemo } from 'react' +import type { ConnectionState } from '../transport/types' +import { applyWorktreeHostContextLabels } from '../worktree/worktree-host-context-labels' +import { applyWorktreeRowDisplayState } from '../worktree/worktree-host-row-identity' +import type { HostScreenState } from './use-host-screen-state' + +/** The rows the host list renders: the live or last-known catalog with display state and host badges. */ +export function useHostDisplayWorktrees(connState: ConnectionState, state: HostScreenState) { + return useMemo(() => { + // Why: live `worktrees` is authoritative only while connected; under the amber + // mount default, connecting/handshaking must keep the pre-reconnect list too. + const base = connState === 'connected' ? state.worktrees : state.lastKnownWorktrees + return applyWorktreeHostContextLabels( + applyWorktreeRowDisplayState(base, state.sleptIds, state.optimisticActiveWorktreeIdentity), + { + repoHostIdByRepoId: state.repoHostIdByRepoId, + hostLabelById: state.hostLabelById, + hostPlatform: state.hostPlatform, + hostHealthById: state.hostHealthById + } + ) + }, [ + connState, + state.worktrees, + state.lastKnownWorktrees, + state.sleptIds, + state.optimisticActiveWorktreeIdentity, + state.repoHostIdByRepoId, + state.hostLabelById, + state.hostPlatform, + state.hostHealthById + ]) +} diff --git a/mobile/src/host-screen/use-host-repo-metadata.ts b/mobile/src/host-screen/use-host-repo-metadata.ts index 0987aed14b0..892e90ff512 100644 --- a/mobile/src/host-screen/use-host-repo-metadata.ts +++ b/mobile/src/host-screen/use-host-repo-metadata.ts @@ -1,6 +1,6 @@ import { optionalSettingsRead } from '../transport/settings-read-operations' import { useCallback } from 'react' -import { getRepoExecutionHostId } from '../../../src/shared/execution-host' +import { getRepoExecutionHostId, parseExecutionHostId } from '../../../src/shared/execution-host' import { setCachedRepos } from '../cache/repo-cache' import type { RpcAcceptedResult } from '../transport/rpc-accepted-result' import type { RpcClient } from '../transport/rpc-client' @@ -10,6 +10,7 @@ import { buildHostLabelById, buildRepoHostIdByRepoId } from '../worktree/worktree-host-context-labels' +import { buildSshHostHealthById } from '../worktree/worktree-host-health' import { hostPlatformRead, hostRepoCatalogRead, @@ -58,6 +59,7 @@ export function useHostRepoMetadata(args: { fetchRepoMetadataPendingRef, repoMetadataFetchedAtRef, setHostLabelById, + setHostHealthById, setHostPlatform, setRepoColorsByName, setRepoHostIdByRepoId, @@ -116,11 +118,12 @@ export function useHostRepoMetadata(args: { ) setRepoIdsByName(new Map(catalog.map((repo) => [repo.displayName, repo.id]))) setRepoHostIdByRepoId(buildRepoHostIdByRepoId(catalog)) - // Why: rows only name their host when the list spans hosts, so a single-host - // catalog never pays for the label lookups. Counted over repos, not the id-keyed - // map: one repo id registered on two hosts is two hosts. + // Why: rows name their host when the list spans hosts or an SSH host may be unhealthy, + // so a local-only catalog never pays for the label lookups. Counted over repos, not the + // id-keyed map: one repo id registered on two hosts is two hosts. const hostIds = new Set(catalog.map((repo) => getRepoExecutionHostId(repo))) - if (hostIds.size > 1) { + const hasSshHost = [...hostIds].some((id) => parseExecutionHostId(id)?.kind === 'ssh') + if (hostIds.size > 1 || hasSshHost) { const [sshTargets, hostSettings, hostPlatform] = await Promise.all([ settledMetadataReply(() => hostSshTargetSummariesRead.request(requestClient)), optionalSettingsRead.request(requestClient).catch(() => null), @@ -132,16 +135,22 @@ export function useHostRepoMetadata(args: { const hostSettingsResult = hostSettings ? optionalSettingsRead.interpret(hostSettings) : null + const sshTargetSummaries = + acceptedMetadata(sshTargets, hostSshTargetSummariesRead.interpret) ?? [] + setHostHealthById(buildSshHostHealthById(sshTargetSummaries)) setHostLabelById( buildHostLabelById({ - sshTargets: - acceptedMetadata(sshTargets, hostSshTargetSummariesRead.interpret) ?? [], + sshTargets: sshTargetSummaries, hostSettingOverrides: readHostSettingOverrides( hostSettingsResult?.accepted ? hostSettingsResult.value : undefined ) }) ) setHostPlatform(acceptedMetadata(hostPlatform, hostPlatformRead.interpret) ?? null) + } else { + // Why: a host that left the catalog must not keep badging rows that still name it. + setHostHealthById(new Map()) + setHostLabelById(new Map()) } } while (fetchRepoMetadataPendingRef.current.has(requestClient)) } catch { diff --git a/mobile/src/host-screen/use-host-screen-controller.ts b/mobile/src/host-screen/use-host-screen-controller.ts index 34efd5e71ac..8751113f037 100644 --- a/mobile/src/host-screen/use-host-screen-controller.ts +++ b/mobile/src/host-screen/use-host-screen-controller.ts @@ -14,9 +14,8 @@ import { useReconnectAttempt, useRelayRecoveryStatus } from '../transport/client-context-connection-metrics' -import { applyWorktreeRowDisplayState } from '../worktree/worktree-host-row-identity' -import { applyWorktreeHostContextLabels } from '../worktree/worktree-host-context-labels' import { useWorkspaceSections } from '../worktree/use-workspace-sections' +import { useHostDisplayWorktrees } from './use-host-display-worktrees' import { useHostRepoMetadata } from './use-host-repo-metadata' import { useHostScreenIdentity } from './use-host-screen-identity' import { useHostScreenState } from './use-host-screen-state' @@ -102,28 +101,7 @@ export function useHostScreenController({ } const showNewWorktree = resolvedRouteActionState.showNewWorktree - const displayWorktrees = useMemo(() => { - // Why: live `worktrees` is authoritative only while connected; under the amber - // mount default, connecting/handshaking must keep the pre-reconnect list too. - const base = connState === 'connected' ? state.worktrees : state.lastKnownWorktrees - return applyWorktreeHostContextLabels( - applyWorktreeRowDisplayState(base, state.sleptIds, state.optimisticActiveWorktreeIdentity), - { - repoHostIdByRepoId: state.repoHostIdByRepoId, - hostLabelById: state.hostLabelById, - hostPlatform: state.hostPlatform - } - ) - }, [ - connState, - state.worktrees, - state.lastKnownWorktrees, - state.sleptIds, - state.optimisticActiveWorktreeIdentity, - state.repoHostIdByRepoId, - state.hostLabelById, - state.hostPlatform - ]) + const displayWorktrees = useHostDisplayWorktrees(connState, state) const sectionsResult = useWorkspaceSections({ displayWorktrees, sortMode: state.sortMode, diff --git a/mobile/src/host-screen/use-host-screen-identity.ts b/mobile/src/host-screen/use-host-screen-identity.ts index 25cc71eeade..6f675b8457f 100644 --- a/mobile/src/host-screen/use-host-screen-identity.ts +++ b/mobile/src/host-screen/use-host-screen-identity.ts @@ -18,6 +18,7 @@ export function useHostScreenIdentity(args: { setCatalogError, setError, setHostLabelById, + setHostHealthById, setHostName, setHostPlatform, setHostStoredDescriptor, @@ -62,6 +63,7 @@ export function useHostScreenIdentity(args: { setRepoIconsByName(new Map()) setRepoHostIdByRepoId(new Map()) setHostLabelById(new Map()) + setHostHealthById(new Map()) setHostPlatform(null) setShowPinnedInGroups(false) repoMetadataFetchedAtRef.current = 0 diff --git a/mobile/src/host-screen/use-host-screen-state.ts b/mobile/src/host-screen/use-host-screen-state.ts index 6625119f205..6cdc4100bc7 100644 --- a/mobile/src/host-screen/use-host-screen-state.ts +++ b/mobile/src/host-screen/use-host-screen-state.ts @@ -1,5 +1,6 @@ import { useRef, useState } from 'react' import type { ExecutionHostId } from '../../../src/shared/execution-host' +import type { ExecutionHostHealth } from '../../../src/shared/execution-host-health' import type { WorkspaceStatusDefinition } from '../../../src/shared/worktree/types' import { getCachedWorktrees } from '../cache/worktree-cache' import { createInitialHostRouteActionState } from '../host-route-action-state' @@ -71,6 +72,9 @@ export function useHostScreenState(hostId: string | undefined, action: string | new Map() ) const [hostLabelById, setHostLabelById] = useState>(new Map()) + const [hostHealthById, setHostHealthById] = useState>( + new Map() + ) const [hostPlatform, setHostPlatform] = useState(null) const [showSortPicker, setShowSortPicker] = useState(false) const [showGroupPicker, setShowGroupPicker] = useState(false) @@ -113,6 +117,7 @@ export function useHostScreenState(hostId: string | undefined, action: string | filters, groupMode, hostLabelById, + hostHealthById, hostName, hostPlatform, hostStoredDescriptor, @@ -138,6 +143,7 @@ export function useHostScreenState(hostId: string | undefined, action: string | setFilters, setGroupMode, setHostLabelById, + setHostHealthById, setHostName, setHostPlatform, setHostStoredDescriptor, diff --git a/mobile/src/tasks/workspace-source-reply-schema.test.ts b/mobile/src/tasks/workspace-source-reply-schema.test.ts index 74ccecfbd04..a8ae643afeb 100644 --- a/mobile/src/tasks/workspace-source-reply-schema.test.ts +++ b/mobile/src/tasks/workspace-source-reply-schema.test.ts @@ -7,9 +7,9 @@ import { repoSetupHooksSchema, repoSparsePresetListSchema, repoSparsePresetSaveSchema, - SSH_CONNECTION_STATUS, sshConnectionStateSchema } from './workspace-source-reply-schema' +import { SSH_CONNECTION_STATUS } from '../transport/ssh-connection-status-arms' // Pins the SSH status degrade, the error tri-state beside it, and the preset requirement. diff --git a/mobile/src/tasks/workspace-source-reply-schema.ts b/mobile/src/tasks/workspace-source-reply-schema.ts index 54b0174f65f..f383e30f92d 100644 --- a/mobile/src/tasks/workspace-source-reply-schema.ts +++ b/mobile/src/tasks/workspace-source-reply-schema.ts @@ -1,11 +1,6 @@ import { z } from 'zod' -import type { SshConnectionStatus } from '../../../src/shared/ssh-types' -import { - hostUnionArms, - openEnum, - salvagedOptional, - salvagingArray -} from '../../../src/shared/zod-salvage' +import { openEnum, salvagedOptional, salvagingArray } from '../../../src/shared/zod-salvage' +import { SSH_CONNECTION_STATUS } from '../transport/ssh-connection-status-arms' // The repo and SSH reads the workspace-create drawer runs. Checked against // src/main/runtime/rpc/methods/ssh.ts:30-46 (getPublicSshState, SshConnectionState in @@ -13,18 +8,6 @@ import { // answer a bare `string[]`), and repo.ts:87-103/:184-192 (the sparse preset envelopes, the ref // search and the orca.yaml hooks). -// Pinned to the host's own union through hostUnionArms: an arm added or dropped host-side fails tsc. -export const SSH_CONNECTION_STATUS = hostUnionArms({ - disconnected: true, - connecting: true, - 'auth-failed': true, - 'deploying-relay': true, - connected: true, - reconnecting: true, - 'reconnection-failed': true, - error: true -}) - const sourceText = (name: string) => salvagedOptional(name, z.string()) /** diff --git a/mobile/src/transport/ssh-connection-status-arms.ts b/mobile/src/transport/ssh-connection-status-arms.ts new file mode 100644 index 00000000000..519c603e8b7 --- /dev/null +++ b/mobile/src/transport/ssh-connection-status-arms.ts @@ -0,0 +1,20 @@ +import type { SshConnectionStatus } from '../../../src/shared/ssh-types' +import { hostUnionArms } from '../../../src/shared/zod-salvage' + +// Pinned to the host's own union through hostUnionArms: an arm added or dropped host-side fails tsc. +export const SSH_CONNECTION_STATUS = hostUnionArms({ + disconnected: true, + connecting: true, + 'auth-failed': true, + 'deploying-relay': true, + connected: true, + reconnecting: true, + 'reconnection-failed': true, + error: true +}) + +const KNOWN_SSH_CONNECTION_STATUSES: ReadonlySet = new Set(SSH_CONNECTION_STATUS) + +export function isSshConnectionStatus(status: string): status is SshConnectionStatus { + return KNOWN_SSH_CONNECTION_STATUSES.has(status) +} diff --git a/mobile/src/worktree/host-worktree-refresh.test.ts b/mobile/src/worktree/host-worktree-refresh.test.ts index d0b8cf01dab..669e312e52c 100644 --- a/mobile/src/worktree/host-worktree-refresh.test.ts +++ b/mobile/src/worktree/host-worktree-refresh.test.ts @@ -113,6 +113,21 @@ describe('startHostWorktreeRefresh', () => { expect(fetchRepoMetadata).toHaveBeenCalledWith({ force: true, queueIfInFlight: true }) }) + it('re-reads repo metadata, which carries SSH host health, on sshStateChanged', () => { + start() + fetchWorktrees.mockClear() + fetchRepoMetadata.mockClear() + + eventListener?.({ + type: 'sshStateChanged', + targetId: 'ssh-1', + state: { targetId: 'ssh-1', status: 'reconnecting' } + }) + + expect(fetchWorktrees).not.toHaveBeenCalled() + expect(fetchRepoMetadata).toHaveBeenCalledWith({ force: true, queueIfInFlight: true }) + }) + it('refreshes worktrees on worktreesChanged and both snapshots after stream replay', () => { start() fetchWorktrees.mockClear() diff --git a/mobile/src/worktree/host-worktree-refresh.ts b/mobile/src/worktree/host-worktree-refresh.ts index 525fc335b8f..e090d835f57 100644 --- a/mobile/src/worktree/host-worktree-refresh.ts +++ b/mobile/src/worktree/host-worktree-refresh.ts @@ -74,6 +74,9 @@ export function startHostWorktreeRefresh({ void fetchRepoMetadata({ force: true, queueIfInFlight: true }) } else if (event.type === 'worktreesChanged') { void fetchWorktrees() + } else if (event.type === 'sshStateChanged') { + // Why: host badges carry SSH health, read with the labels from ssh.listTargetSummaries. + void fetchRepoMetadata({ force: true, queueIfInFlight: true }) } } ) diff --git a/mobile/src/worktree/workspace-list-types.ts b/mobile/src/worktree/workspace-list-types.ts index 255e429e892..556bd1a7dc1 100644 --- a/mobile/src/worktree/workspace-list-types.ts +++ b/mobile/src/worktree/workspace-list-types.ts @@ -13,6 +13,8 @@ export type Worktree = { hostContextLabel?: string /** Resolved host for the display label; present when legacy rows omit hostId. */ hostContextHostId?: ExecutionHostId + /** Display-only; the host's health word beside its label, absent while the host is healthy. */ + hostContextHealthLabel?: string repo: string branch: string displayName: string diff --git a/mobile/src/worktree/worktree-host-context-labels.test.ts b/mobile/src/worktree/worktree-host-context-labels.test.ts index a921ceebb90..0faf9b947fb 100644 --- a/mobile/src/worktree/worktree-host-context-labels.test.ts +++ b/mobile/src/worktree/worktree-host-context-labels.test.ts @@ -86,7 +86,8 @@ describe('getWorktreeHostContextLabels', () => { const sources = { repoHostIdByRepoId: new Map(), hostLabelById: new Map([[sshHostId, 'openclaw']]), - hostPlatform: 'darwin' as const + hostPlatform: 'darwin' as const, + hostHealthById: new Map() } it('returns nothing for a single-host list', () => { @@ -95,6 +96,28 @@ describe('getWorktreeHostContextLabels', () => { expect(applyWorktreeHostContextLabels(rows, sources)).toBe(rows) }) + it('names an unhealthy host even in a single-host list, and only that host', () => { + const rows = [worktree({ hostId: sshHostId, worktreeId: 'a' })] + const unhealthy = applyWorktreeHostContextLabels(rows, { + ...sources, + hostHealthById: new Map([[sshHostId, 'connecting']]) + }) + expect(unhealthy.map((row) => [row.hostContextLabel, row.hostContextHealthLabel])).toEqual([ + ['openclaw', 'Connecting'] + ]) + expect( + applyWorktreeHostContextLabels(rows, { + ...sources, + hostHealthById: new Map([[sshHostId, 'available']]) + }) + ).toBe(rows) + const otherHostUnhealthy = applyWorktreeHostContextLabels(rows, { + ...sources, + hostHealthById: new Map([['ssh:x', 'error']]) + }) + expect(otherHostUnhealthy[0].hostContextLabel).toBeUndefined() + }) + it('names every row by host once the list spans hosts', () => { const rows = [ worktree({ hostId: 'local', worktreeId: 'a' }), @@ -111,6 +134,34 @@ describe('getWorktreeHostContextLabels', () => { ]) }) + it('adds the health word only for hosts the desktop reports as unhealthy', () => { + const rows = [ + worktree({ hostId: 'local', worktreeId: 'a' }), + worktree({ hostId: sshHostId, worktreeId: 'b' }), + worktree({ hostId: 'ssh:healthy', worktreeId: 'c' }), + worktree({ hostId: 'runtime:env-1', worktreeId: 'd' }), + worktree({ repoId: 'repo-ssh', worktreeId: 'e' }) + ] + const labeled = applyWorktreeHostContextLabels(rows, { + ...sources, + repoHostIdByRepoId: buildRepoHostIdByRepoId([ + { id: 'repo-ssh', connectionId: 'ssh-1785104650217-eduhep' } + ]), + hostHealthById: new Map([ + ['local', 'local'], + [sshHostId, 'disconnected'], + ['ssh:healthy', 'available'] + ]) + }) + expect(labeled.map((row) => row.hostContextHealthLabel)).toEqual([ + undefined, + 'Disconnected', + undefined, + undefined, + 'Disconnected' + ]) + }) + it('names the local host from the paired host platform, not the phone', () => { const rows = [ worktree({ hostId: 'local', worktreeId: 'a' }), diff --git a/mobile/src/worktree/worktree-host-context-labels.ts b/mobile/src/worktree/worktree-host-context-labels.ts index 406e6d8ce03..f8f1b877afe 100644 --- a/mobile/src/worktree/worktree-host-context-labels.ts +++ b/mobile/src/worktree/worktree-host-context-labels.ts @@ -4,13 +4,18 @@ import { normalizeExecutionHostId, type ExecutionHostId } from '../../../src/shared/execution-host' -import { getMixedHostContextLabels as getSharedMixedHostContextLabels } from '../../../src/shared/worktree/host-context-labels' +import { + getHostContextLabel, + getMixedHostContextLabels as getSharedMixedHostContextLabels +} from '../../../src/shared/worktree/host-context-labels' import { composeWorktreeHostIdentity } from '../../../src/shared/worktree/host-qualified-identity' export { buildHostLabelById, getHostContextLabel } from '../../../src/shared/worktree/host-context-labels' +import type { ExecutionHostHealth } from '../../../src/shared/execution-host-health' import type { Worktree } from './workspace-list-types' +import { getHostHealthBadgeLabel } from './worktree-host-health' export type HostLabelSources = { /** Host id per repo id from repo.list; rows from hosts that predate `hostId` fall back to it. */ @@ -19,6 +24,8 @@ export type HostLabelSources = { hostLabelById: ReadonlyMap /** The paired host's own platform; the phone's platform must never name the desktop. */ hostPlatform: NodeJS.Platform | null + /** Health per host the desktop reports one for; an absent host keeps the bare label. */ + hostHealthById: ReadonlyMap } export function buildRepoHostIdByRepoId( @@ -72,20 +79,27 @@ export function applyWorktreeHostContextLabels( sources: HostLabelSources ): Worktree[] { const labels = getWorktreeHostContextLabels(worktrees, sources) - if (!labels) { + // Why: the desktop card shows SSH status on any host count, so an unhealthy host is named even alone. + const anyUnhealthy = [...sources.hostHealthById.values()].some(getHostHealthBadgeLabel) + if (!labels && !anyUnhealthy) { return worktrees } return worktrees.map((worktree) => { - const hostContextLabel = labels.get( - getResolvedWorktreeRowIdentity(worktree, sources.repoHostIdByRepoId) + const hostContextHostId = resolveWorktreeHostId(worktree, sources.repoHostIdByRepoId) + const hostContextHealthLabel = getHostHealthBadgeLabel( + sources.hostHealthById.get(hostContextHostId) ) + const hostContextLabel = labels + ? labels.get(getResolvedWorktreeRowIdentity(worktree, sources.repoHostIdByRepoId)) + : hostContextHealthLabel && getHostContextLabel(hostContextHostId, sources) if (!hostContextLabel) { return worktree } return { ...worktree, hostContextLabel, - hostContextHostId: resolveWorktreeHostId(worktree, sources.repoHostIdByRepoId) + hostContextHostId, + ...(hostContextHealthLabel ? { hostContextHealthLabel } : {}) } }) } diff --git a/mobile/src/worktree/worktree-host-health.test.ts b/mobile/src/worktree/worktree-host-health.test.ts new file mode 100644 index 00000000000..312cd871892 --- /dev/null +++ b/mobile/src/worktree/worktree-host-health.test.ts @@ -0,0 +1,46 @@ +import { describe, expect, it } from 'vitest' +import { buildSshHostHealthById, getHostHealthBadgeLabel } from './worktree-host-health' + +describe('buildSshHostHealthById', () => { + it('derives each SSH host health from its lifecycle, keyed by canonical host id', () => { + const health = buildSshHostHealthById([ + { id: 'up', connected: true, connectionStatus: 'connected' }, + { id: 'relay', connected: false, connectionStatus: 'deploying-relay' }, + { id: 'retry', connected: false, connectionStatus: 'reconnecting' }, + { id: 'down', connected: false, connectionStatus: 'disconnected' }, + { id: 'auth', connected: false, connectionStatus: 'auth-failed' }, + { id: 'never', connected: false }, + { id: 'ssh:canonical', connected: false, connectionStatus: 'connecting' } + ]) + expect(Object.fromEntries(health)).toEqual({ + 'ssh:up': 'available', + 'ssh:relay': 'connecting', + 'ssh:retry': 'connecting', + 'ssh:down': 'disconnected', + 'ssh:auth': 'error', + // Same verdict as the desktop sidebar for a target with no lifecycle state yet. + 'ssh:never': 'disconnected', + 'ssh:canonical': 'connecting' + }) + }) + + it('reports nothing for a desktop that predates lifecycle fields or a status it cannot read', () => { + const health = buildSshHostHealthById([ + { id: 'old-desktop' }, + { id: 'future', connected: false, connectionStatus: 'hibernating' } + ]) + expect(health.size).toBe(0) + }) +}) + +describe('getHostHealthBadgeLabel', () => { + it('uses the desktop sidebar words for unhealthy hosts only', () => { + expect(getHostHealthBadgeLabel('disconnected')).toBe('Disconnected') + expect(getHostHealthBadgeLabel('connecting')).toBe('Connecting') + expect(getHostHealthBadgeLabel('error')).toBe('Needs attention') + expect(getHostHealthBadgeLabel('blocked')).toBe('Update needed') + expect(getHostHealthBadgeLabel('available')).toBeUndefined() + expect(getHostHealthBadgeLabel('local')).toBeUndefined() + expect(getHostHealthBadgeLabel(undefined)).toBeUndefined() + }) +}) diff --git a/mobile/src/worktree/worktree-host-health.ts b/mobile/src/worktree/worktree-host-health.ts new file mode 100644 index 00000000000..9618ca58678 --- /dev/null +++ b/mobile/src/worktree/worktree-host-health.ts @@ -0,0 +1,39 @@ +import type { ExecutionHostId } from '../../../src/shared/execution-host' +import { + getExecutionHostHealthLabel, + getSshConnectionHealth, + type ExecutionHostHealth +} from '../../../src/shared/execution-host-health' +import { sshTargetSummaryHostId } from '../../../src/shared/worktree/host-context-labels' +import { isSshConnectionStatus } from '../transport/ssh-connection-status-arms' + +/** + * Health per SSH host, derived exactly as the desktop sidebar derives it. A desktop that predates + * `connected` reports no lifecycle, and an unknown status is no verdict: both leave the host out. + */ +export function buildSshHostHealthById( + sshTargets: readonly { id: string; connected?: boolean; connectionStatus?: string }[] +): Map { + const health = new Map() + for (const target of sshTargets) { + const status = target.connectionStatus + if ( + target.connected === undefined || + (status !== undefined && !isSshConnectionStatus(status)) + ) { + continue + } + health.set(sshTargetSummaryHostId(target.id), getSshConnectionHealth(status)) + } + return health +} + +/** The word a row's host badge adds; a healthy or local host keeps the bare name. */ +export function getHostHealthBadgeLabel( + health: ExecutionHostHealth | undefined +): string | undefined { + if (health === undefined || health === 'local' || health === 'available') { + return undefined + } + return getExecutionHostHealthLabel(health) +} diff --git a/src/renderer/src/components/sidebar/sidebar-host-options.ts b/src/renderer/src/components/sidebar/sidebar-host-options.ts index ffd80395e51..deb4977a253 100644 --- a/src/renderer/src/components/sidebar/sidebar-host-options.ts +++ b/src/renderer/src/components/sidebar/sidebar-host-options.ts @@ -11,6 +11,7 @@ import { buildExecutionHostRegistry, type ExecutionHostHealth } from '../../../../shared/execution-host-registry' +import { getExecutionHostHealthLabel } from '../../../../shared/execution-host-health' import { expandEquivalentExecutionHostIds, pickerExecutionHosts @@ -162,22 +163,7 @@ export function getCheckedHostRows( } export function getSidebarHostHealthLabel(health: SidebarHostScopeOption['health']): string { - switch (health) { - case 'local': - return 'Local' - case 'available': - return 'Connected' - case 'connecting': - return 'Connecting' - case 'blocked': - return 'Update needed' - case 'disconnected': - return 'Disconnected' - case 'error': - return 'Needs attention' - case 'mixed': - return 'Mixed' - } + return health === 'mixed' ? 'Mixed' : getExecutionHostHealthLabel(health) } /** diff --git a/src/shared/execution-host-health.ts b/src/shared/execution-host-health.ts new file mode 100644 index 00000000000..4d8b12eb255 --- /dev/null +++ b/src/shared/execution-host-health.ts @@ -0,0 +1,47 @@ +import type { SshConnectionStatus } from './ssh-types' + +export type ExecutionHostHealth = + | 'local' + | 'available' + | 'connecting' + | 'blocked' + | 'disconnected' + | 'error' + +/** An SSH host's health; a target with no lifecycle state has never connected. */ +export function getSshConnectionHealth( + status: SshConnectionStatus | undefined +): ExecutionHostHealth { + switch (status) { + case 'connected': + return 'available' + case 'connecting': + case 'deploying-relay': + case 'reconnecting': + return 'connecting' + case 'auth-failed': + case 'error': + case 'reconnection-failed': + return 'error' + case 'disconnected': + case undefined: + return 'disconnected' + } +} + +export function getExecutionHostHealthLabel(health: ExecutionHostHealth): string { + switch (health) { + case 'local': + return 'Local' + case 'available': + return 'Connected' + case 'connecting': + return 'Connecting' + case 'blocked': + return 'Update needed' + case 'disconnected': + return 'Disconnected' + case 'error': + return 'Needs attention' + } +} diff --git a/src/shared/execution-host-registry.ts b/src/shared/execution-host-registry.ts index 68c7e780b13..8bdc713793f 100644 --- a/src/shared/execution-host-registry.ts +++ b/src/shared/execution-host-registry.ts @@ -20,14 +20,9 @@ import type { RuntimeEnvironmentSource } from './runtime-environments' import type { GlobalSettings } from './global-settings-types' import type { Repo } from './repo-types' import { annotateManagedOrcadExecutionHosts } from './managed-orcad-execution-host' +import { getSshConnectionHealth, type ExecutionHostHealth } from './execution-host-health' -export type ExecutionHostHealth = - | 'local' - | 'available' - | 'connecting' - | 'blocked' - | 'disconnected' - | 'error' +export type { ExecutionHostHealth } from './execution-host-health' export type ExecutionHostRegistryEntry = { id: ExecutionHostId @@ -113,24 +108,6 @@ function runtimeControlHealth( } } -function sshHealth(state: SshConnectionState | undefined): ExecutionHostHealth { - switch (state?.status) { - case 'connected': - return 'available' - case 'connecting': - case 'deploying-relay': - case 'reconnecting': - return 'connecting' - case 'auth-failed': - case 'error': - case 'reconnection-failed': - return 'error' - case 'disconnected': - case undefined: - return 'disconnected' - } -} - function setHost( hosts: Map, entry: ExecutionHostRegistryEntry @@ -302,7 +279,7 @@ export function buildExecutionHostRegistry(args: { kind: 'ssh', label: args.sshTargetLabels?.get(targetId) || targetId, detail: 'SSH', - health: sshHealth(state), + health: getSshConnectionHealth(state?.status), connectionStatus: state?.status }) } diff --git a/src/shared/worktree/host-context-labels.ts b/src/shared/worktree/host-context-labels.ts index d3ab9ed2ed7..2171f00d76a 100644 --- a/src/shared/worktree/host-context-labels.ts +++ b/src/shared/worktree/host-context-labels.ts @@ -40,10 +40,14 @@ export function getHostContextLabel( } /** - * Build labels for SSH targets and apply persisted display-name overrides. - * Target summaries have appeared both as raw target ids and canonical `ssh:` ids - * across protocol versions, so accept either representation. + * The host id an SSH target summary names. Summaries have carried both raw target ids and + * canonical `ssh:` ids across protocol versions, so accept either representation. */ +export function sshTargetSummaryHostId(targetId: string): ExecutionHostId { + return normalizeExecutionHostId(targetId) ?? toSshExecutionHostId(targetId) +} + +/** Build labels for SSH targets and apply persisted display-name overrides. */ export function buildHostLabelById(args: { sshTargets: readonly { id: string; label: string }[] hostSettingOverrides: unknown @@ -54,10 +58,7 @@ export function buildHostLabelById(args: { if (!target.id.trim() || !label) { continue } - const hostId = normalizeExecutionHostId(target.id) ?? toSshExecutionHostId(target.id) - if (hostId) { - labels.set(hostId, label) - } + labels.set(sshTargetSummaryHostId(target.id), label) } const overrides = args.hostSettingOverrides && typeof args.hostSettingOverrides === 'object'