mirror of
https://github.com/stablyai/orca.git
synced 2026-11-13 09:27:29 +00:00
perf(mobile): show the workspace list from memory and refresh it on desktop changes (#39453)
* perf(mobile): show the workspace list from memory and refresh it on desktop changes * fix(mobile): import the host list queries type without an import() annotation * fix(mobile): keep host labels on a not-connected read and update the phone feed e2e helper * fix(mobile): keep the event router listening when its stream ends while opening * fix(mobile): reuse the shared read-view base and remount for the cache-return recording
This commit is contained in:
@@ -4,129 +4,53 @@
|
||||
"family": "host-worktree-refresh",
|
||||
"namedDeltas": [],
|
||||
"values": {
|
||||
"121f5eaa52a4": {
|
||||
"name": "fetchWorktrees",
|
||||
"ordinal": 7,
|
||||
"value": {
|
||||
"options": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
}
|
||||
"322d549d1882": {
|
||||
"refreshWorktrees": 0,
|
||||
"running": true,
|
||||
"syncViewSettings": 2
|
||||
},
|
||||
"18399efbe63d": {
|
||||
"name": "fetchRepoMetadata",
|
||||
"ordinal": 3,
|
||||
"value": {
|
||||
"options": {
|
||||
"force": true,
|
||||
"queueIfInFlight": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"1d8fe76c29c7": {
|
||||
"name": "runtime.clientEvents.unsubscribe#1",
|
||||
"ordinal": 10,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"runtime.clientEvents.unsubscribe\",\"params\":{\"subscriptionId\":\"sub-1\"}}"
|
||||
},
|
||||
"1e1067bda316": {
|
||||
"name": "fetchWorktrees",
|
||||
"354b03a10a5d": {
|
||||
"name": "syncViewSettings",
|
||||
"ordinal": 5,
|
||||
"value": {
|
||||
"options": {
|
||||
"$rpc": "undefined"
|
||||
"resend": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"1e5c2d0839f0": {
|
||||
"fetchRepoMetadata": 3,
|
||||
"fetchWorktrees": 4,
|
||||
"running": true
|
||||
},
|
||||
"306fcb72ddc5": {
|
||||
"name": "fetchRepoMetadata",
|
||||
"ordinal": 8,
|
||||
"value": {
|
||||
"options": {
|
||||
"force": true,
|
||||
"queueIfInFlight": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"3582e201880e": {
|
||||
"6ab454a38bb0": {
|
||||
"name": "runtime.clientEvents.unsubscribe#2",
|
||||
"ordinal": 13,
|
||||
"ordinal": 6,
|
||||
"json": "{\"id\":\"frame-4\",\"deviceToken\":\"recording-device\",\"method\":\"runtime.clientEvents.unsubscribe\",\"params\":{\"subscriptionId\":\"sub-2\"}}"
|
||||
},
|
||||
"4a27571a011a": {
|
||||
"name": "fetchWorktrees",
|
||||
"85612713e3f0": {
|
||||
"name": "runtime.clientEvents.unsubscribe#1",
|
||||
"ordinal": 4,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"runtime.clientEvents.unsubscribe\",\"params\":{\"subscriptionId\":\"sub-1\"}}"
|
||||
},
|
||||
"85ed79b276e4": {
|
||||
"refreshWorktrees": 0,
|
||||
"running": true,
|
||||
"syncViewSettings": 1
|
||||
},
|
||||
"a507a5cf0d43": {
|
||||
"name": "syncViewSettings",
|
||||
"ordinal": 2,
|
||||
"value": {
|
||||
"options": {
|
||||
"$rpc": "undefined"
|
||||
"resend": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"4e7ae49574b8": {
|
||||
"name": "fetchWorktrees",
|
||||
"ordinal": 4,
|
||||
"value": {
|
||||
"options": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
}
|
||||
},
|
||||
"5073e6d40272": {
|
||||
"name": "fetchRepoMetadata",
|
||||
"ordinal": 12,
|
||||
"value": {
|
||||
"options": {
|
||||
"force": true,
|
||||
"queueIfInFlight": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"6d9fd24a7491": {
|
||||
"fetchRepoMetadata": 1,
|
||||
"fetchWorktrees": 2,
|
||||
"running": true
|
||||
},
|
||||
"8297145c6199": {
|
||||
"fetchRepoMetadata": 1,
|
||||
"fetchWorktrees": 1,
|
||||
"running": true
|
||||
},
|
||||
"8fa922dbb034": {
|
||||
"name": "fetchWorktrees",
|
||||
"ordinal": 11,
|
||||
"value": {
|
||||
"options": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
}
|
||||
},
|
||||
"91e9a84a208f": {
|
||||
"fetchRepoMetadata": 4,
|
||||
"fetchWorktrees": 5,
|
||||
"running": false
|
||||
},
|
||||
"96ebe4a1b522": {
|
||||
"name": "fetchRepoMetadata",
|
||||
"ordinal": 6,
|
||||
"value": {
|
||||
"options": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
}
|
||||
},
|
||||
"b6a25eca1497": {
|
||||
"cc261d80eae0": {
|
||||
"name": "runtime.clientEvents.subscribe#2",
|
||||
"ordinal": 9,
|
||||
"ordinal": 3,
|
||||
"json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"runtime.clientEvents.subscribe\",\"params\":null}"
|
||||
},
|
||||
"e54e98a537b8": {
|
||||
"fetchRepoMetadata": 2,
|
||||
"fetchWorktrees": 3,
|
||||
"running": true
|
||||
"e3e3030326f0": {
|
||||
"refreshWorktrees": 0,
|
||||
"running": false,
|
||||
"syncViewSettings": 2
|
||||
},
|
||||
"eb79a9b3682a": {
|
||||
"status": "fulfilled",
|
||||
@@ -136,11 +60,6 @@
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
"edb34b7fcc08": {
|
||||
"fetchRepoMetadata": 4,
|
||||
"fetchWorktrees": 5,
|
||||
"running": true
|
||||
},
|
||||
"eef1ee3c2773": {
|
||||
"name": "runtime.clientEvents.subscribe#1",
|
||||
"ordinal": 1,
|
||||
@@ -166,8 +85,8 @@
|
||||
"settlements": {
|
||||
"start": "eb79a9b3682a"
|
||||
},
|
||||
"state": "8297145c6199",
|
||||
"effects": ["4a27571a011a", "18399efbe63d"]
|
||||
"state": "85ed79b276e4",
|
||||
"effects": ["a507a5cf0d43"]
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -178,8 +97,8 @@
|
||||
"settlements": {
|
||||
"start": "eb79a9b3682a"
|
||||
},
|
||||
"state": "8297145c6199",
|
||||
"effects": ["4a27571a011a", "18399efbe63d"]
|
||||
"state": "85ed79b276e4",
|
||||
"effects": ["a507a5cf0d43"]
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -190,8 +109,8 @@
|
||||
"settlements": {
|
||||
"start": "eb79a9b3682a"
|
||||
},
|
||||
"state": "6d9fd24a7491",
|
||||
"effects": ["4a27571a011a", "18399efbe63d", "4e7ae49574b8"]
|
||||
"state": "85ed79b276e4",
|
||||
"effects": ["a507a5cf0d43"]
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -202,14 +121,8 @@
|
||||
"settlements": {
|
||||
"start": "eb79a9b3682a"
|
||||
},
|
||||
"state": "e54e98a537b8",
|
||||
"effects": [
|
||||
"4a27571a011a",
|
||||
"18399efbe63d",
|
||||
"4e7ae49574b8",
|
||||
"1e1067bda316",
|
||||
"96ebe4a1b522"
|
||||
]
|
||||
"state": "85ed79b276e4",
|
||||
"effects": ["a507a5cf0d43"]
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -220,84 +133,48 @@
|
||||
"settlements": {
|
||||
"start": "eb79a9b3682a"
|
||||
},
|
||||
"state": "1e5c2d0839f0",
|
||||
"effects": [
|
||||
"4a27571a011a",
|
||||
"18399efbe63d",
|
||||
"4e7ae49574b8",
|
||||
"1e1067bda316",
|
||||
"96ebe4a1b522",
|
||||
"121f5eaa52a4",
|
||||
"306fcb72ddc5"
|
||||
]
|
||||
"state": "85ed79b276e4",
|
||||
"effects": ["a507a5cf0d43"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "re-subscribed",
|
||||
"observation": {
|
||||
"sender": [],
|
||||
"payloads": ["eef1ee3c2773", "b6a25eca1497", "1d8fe76c29c7"],
|
||||
"payloads": ["eef1ee3c2773", "cc261d80eae0", "85612713e3f0"],
|
||||
"settlements": {
|
||||
"start": "eb79a9b3682a",
|
||||
"cutover": "f2af146a9f12"
|
||||
},
|
||||
"state": "1e5c2d0839f0",
|
||||
"effects": [
|
||||
"4a27571a011a",
|
||||
"18399efbe63d",
|
||||
"4e7ae49574b8",
|
||||
"1e1067bda316",
|
||||
"96ebe4a1b522",
|
||||
"121f5eaa52a4",
|
||||
"306fcb72ddc5"
|
||||
]
|
||||
"state": "85ed79b276e4",
|
||||
"effects": ["a507a5cf0d43"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "replayed",
|
||||
"observation": {
|
||||
"sender": [],
|
||||
"payloads": ["eef1ee3c2773", "b6a25eca1497", "1d8fe76c29c7"],
|
||||
"payloads": ["eef1ee3c2773", "cc261d80eae0", "85612713e3f0"],
|
||||
"settlements": {
|
||||
"start": "eb79a9b3682a",
|
||||
"cutover": "f2af146a9f12"
|
||||
},
|
||||
"state": "edb34b7fcc08",
|
||||
"effects": [
|
||||
"4a27571a011a",
|
||||
"18399efbe63d",
|
||||
"4e7ae49574b8",
|
||||
"1e1067bda316",
|
||||
"96ebe4a1b522",
|
||||
"121f5eaa52a4",
|
||||
"306fcb72ddc5",
|
||||
"8fa922dbb034",
|
||||
"5073e6d40272"
|
||||
]
|
||||
"state": "322d549d1882",
|
||||
"effects": ["a507a5cf0d43", "354b03a10a5d"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "stopped",
|
||||
"observation": {
|
||||
"sender": [],
|
||||
"payloads": ["eef1ee3c2773", "b6a25eca1497", "1d8fe76c29c7", "3582e201880e"],
|
||||
"payloads": ["eef1ee3c2773", "cc261d80eae0", "85612713e3f0", "6ab454a38bb0"],
|
||||
"settlements": {
|
||||
"start": "eb79a9b3682a",
|
||||
"cutover": "f2af146a9f12",
|
||||
"stop": "f2af146a9f12"
|
||||
},
|
||||
"state": "91e9a84a208f",
|
||||
"effects": [
|
||||
"4a27571a011a",
|
||||
"18399efbe63d",
|
||||
"4e7ae49574b8",
|
||||
"1e1067bda316",
|
||||
"96ebe4a1b522",
|
||||
"121f5eaa52a4",
|
||||
"306fcb72ddc5",
|
||||
"8fa922dbb034",
|
||||
"5073e6d40272"
|
||||
]
|
||||
"state": "e3e3030326f0",
|
||||
"effects": ["a507a5cf0d43", "354b03a10a5d"]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
+222
-609
File diff suppressed because it is too large
Load Diff
+174
-544
File diff suppressed because it is too large
Load Diff
+142
-516
File diff suppressed because it is too large
Load Diff
+120
-425
@@ -4,62 +4,43 @@
|
||||
"family": "host-worktree-refresh",
|
||||
"namedDeltas": [],
|
||||
"values": {
|
||||
"121f5eaa52a4": {
|
||||
"name": "fetchWorktrees",
|
||||
"ordinal": 7,
|
||||
"value": {
|
||||
"options": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
}
|
||||
"322d549d1882": {
|
||||
"refreshWorktrees": 0,
|
||||
"running": true,
|
||||
"syncViewSettings": 2
|
||||
},
|
||||
"18399efbe63d": {
|
||||
"name": "fetchRepoMetadata",
|
||||
"ordinal": 3,
|
||||
"value": {
|
||||
"options": {
|
||||
"force": true,
|
||||
"queueIfInFlight": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"1d8fe76c29c7": {
|
||||
"name": "runtime.clientEvents.unsubscribe#1",
|
||||
"ordinal": 10,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"runtime.clientEvents.unsubscribe\",\"params\":{\"subscriptionId\":\"sub-1\"}}"
|
||||
},
|
||||
"1e1067bda316": {
|
||||
"name": "fetchWorktrees",
|
||||
"354b03a10a5d": {
|
||||
"name": "syncViewSettings",
|
||||
"ordinal": 5,
|
||||
"value": {
|
||||
"options": {
|
||||
"$rpc": "undefined"
|
||||
"resend": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"1e5c2d0839f0": {
|
||||
"fetchRepoMetadata": 3,
|
||||
"fetchWorktrees": 4,
|
||||
"running": true
|
||||
},
|
||||
"306fcb72ddc5": {
|
||||
"name": "fetchRepoMetadata",
|
||||
"ordinal": 8,
|
||||
"value": {
|
||||
"options": {
|
||||
"force": true,
|
||||
"queueIfInFlight": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"3582e201880e": {
|
||||
"6ab454a38bb0": {
|
||||
"name": "runtime.clientEvents.unsubscribe#2",
|
||||
"ordinal": 13,
|
||||
"ordinal": 6,
|
||||
"json": "{\"id\":\"frame-4\",\"deviceToken\":\"recording-device\",\"method\":\"runtime.clientEvents.unsubscribe\",\"params\":{\"subscriptionId\":\"sub-2\"}}"
|
||||
},
|
||||
"453121dc6257": {
|
||||
"7fc7f9b819e5": {
|
||||
"refreshWorktrees": 0,
|
||||
"running": false,
|
||||
"syncViewSettings": 1
|
||||
},
|
||||
"85612713e3f0": {
|
||||
"name": "runtime.clientEvents.unsubscribe#1",
|
||||
"ordinal": 4,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"runtime.clientEvents.unsubscribe\",\"params\":{\"subscriptionId\":\"sub-1\"}}"
|
||||
},
|
||||
"85ed79b276e4": {
|
||||
"refreshWorktrees": 0,
|
||||
"running": true,
|
||||
"syncViewSettings": 1
|
||||
},
|
||||
"a328c933a31f": {
|
||||
"name": "streams-registered-at-teardown",
|
||||
"ordinal": 11,
|
||||
"ordinal": 5,
|
||||
"value": [
|
||||
{
|
||||
"cancelled": true,
|
||||
@@ -68,81 +49,24 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"4a27571a011a": {
|
||||
"name": "fetchWorktrees",
|
||||
"a507a5cf0d43": {
|
||||
"name": "syncViewSettings",
|
||||
"ordinal": 2,
|
||||
"value": {
|
||||
"options": {
|
||||
"$rpc": "undefined"
|
||||
"resend": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"4e7ae49574b8": {
|
||||
"name": "fetchWorktrees",
|
||||
"ordinal": 4,
|
||||
"value": {
|
||||
"options": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
}
|
||||
},
|
||||
"5073e6d40272": {
|
||||
"name": "fetchRepoMetadata",
|
||||
"ordinal": 12,
|
||||
"value": {
|
||||
"options": {
|
||||
"force": true,
|
||||
"queueIfInFlight": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"6d9fd24a7491": {
|
||||
"fetchRepoMetadata": 1,
|
||||
"fetchWorktrees": 2,
|
||||
"running": true
|
||||
},
|
||||
"8297145c6199": {
|
||||
"fetchRepoMetadata": 1,
|
||||
"fetchWorktrees": 1,
|
||||
"running": true
|
||||
},
|
||||
"8fa922dbb034": {
|
||||
"name": "fetchWorktrees",
|
||||
"ordinal": 11,
|
||||
"value": {
|
||||
"options": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
}
|
||||
},
|
||||
"91e9a84a208f": {
|
||||
"fetchRepoMetadata": 4,
|
||||
"fetchWorktrees": 5,
|
||||
"running": false
|
||||
},
|
||||
"96ebe4a1b522": {
|
||||
"name": "fetchRepoMetadata",
|
||||
"ordinal": 6,
|
||||
"value": {
|
||||
"options": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
}
|
||||
},
|
||||
"b6a25eca1497": {
|
||||
"cc261d80eae0": {
|
||||
"name": "runtime.clientEvents.subscribe#2",
|
||||
"ordinal": 9,
|
||||
"ordinal": 3,
|
||||
"json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"runtime.clientEvents.subscribe\",\"params\":null}"
|
||||
},
|
||||
"e14cb0033202": {
|
||||
"fetchRepoMetadata": 3,
|
||||
"fetchWorktrees": 4,
|
||||
"running": false
|
||||
},
|
||||
"e54e98a537b8": {
|
||||
"fetchRepoMetadata": 2,
|
||||
"fetchWorktrees": 3,
|
||||
"running": true
|
||||
"e3e3030326f0": {
|
||||
"refreshWorktrees": 0,
|
||||
"running": false,
|
||||
"syncViewSettings": 2
|
||||
},
|
||||
"eb79a9b3682a": {
|
||||
"status": "fulfilled",
|
||||
@@ -152,11 +76,6 @@
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
"edb34b7fcc08": {
|
||||
"fetchRepoMetadata": 4,
|
||||
"fetchWorktrees": 5,
|
||||
"running": true
|
||||
},
|
||||
"eef1ee3c2773": {
|
||||
"name": "runtime.clientEvents.subscribe#1",
|
||||
"ordinal": 1,
|
||||
@@ -182,8 +101,8 @@
|
||||
"settlements": {
|
||||
"start": "eb79a9b3682a"
|
||||
},
|
||||
"state": "8297145c6199",
|
||||
"effects": ["4a27571a011a", "18399efbe63d"]
|
||||
"state": "85ed79b276e4",
|
||||
"effects": ["a507a5cf0d43"]
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -194,8 +113,8 @@
|
||||
"settlements": {
|
||||
"start": "eb79a9b3682a"
|
||||
},
|
||||
"state": "8297145c6199",
|
||||
"effects": ["4a27571a011a", "18399efbe63d"]
|
||||
"state": "85ed79b276e4",
|
||||
"effects": ["a507a5cf0d43"]
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -206,8 +125,8 @@
|
||||
"settlements": {
|
||||
"start": "eb79a9b3682a"
|
||||
},
|
||||
"state": "6d9fd24a7491",
|
||||
"effects": ["4a27571a011a", "18399efbe63d", "4e7ae49574b8"]
|
||||
"state": "85ed79b276e4",
|
||||
"effects": ["a507a5cf0d43"]
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -218,14 +137,8 @@
|
||||
"settlements": {
|
||||
"start": "eb79a9b3682a"
|
||||
},
|
||||
"state": "e54e98a537b8",
|
||||
"effects": [
|
||||
"4a27571a011a",
|
||||
"18399efbe63d",
|
||||
"4e7ae49574b8",
|
||||
"1e1067bda316",
|
||||
"96ebe4a1b522"
|
||||
]
|
||||
"state": "85ed79b276e4",
|
||||
"effects": ["a507a5cf0d43"]
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -236,565 +149,347 @@
|
||||
"settlements": {
|
||||
"start": "eb79a9b3682a"
|
||||
},
|
||||
"state": "1e5c2d0839f0",
|
||||
"effects": [
|
||||
"4a27571a011a",
|
||||
"18399efbe63d",
|
||||
"4e7ae49574b8",
|
||||
"1e1067bda316",
|
||||
"96ebe4a1b522",
|
||||
"121f5eaa52a4",
|
||||
"306fcb72ddc5"
|
||||
]
|
||||
"state": "85ed79b276e4",
|
||||
"effects": ["a507a5cf0d43"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "host-worktree-refresh-stream.prelude:re-subscribed",
|
||||
"observation": {
|
||||
"sender": [],
|
||||
"payloads": ["eef1ee3c2773", "b6a25eca1497", "1d8fe76c29c7"],
|
||||
"payloads": ["eef1ee3c2773", "cc261d80eae0", "85612713e3f0"],
|
||||
"settlements": {
|
||||
"start": "eb79a9b3682a",
|
||||
"cutover": "f2af146a9f12"
|
||||
},
|
||||
"state": "1e5c2d0839f0",
|
||||
"effects": [
|
||||
"4a27571a011a",
|
||||
"18399efbe63d",
|
||||
"4e7ae49574b8",
|
||||
"1e1067bda316",
|
||||
"96ebe4a1b522",
|
||||
"121f5eaa52a4",
|
||||
"306fcb72ddc5"
|
||||
]
|
||||
"state": "85ed79b276e4",
|
||||
"effects": ["a507a5cf0d43"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "host-worktree-refresh-stream.prelude:cleanup",
|
||||
"observation": {
|
||||
"sender": [],
|
||||
"payloads": ["eef1ee3c2773", "b6a25eca1497", "1d8fe76c29c7"],
|
||||
"payloads": ["eef1ee3c2773", "cc261d80eae0", "85612713e3f0"],
|
||||
"settlements": {
|
||||
"start": "eb79a9b3682a",
|
||||
"cutover": "f2af146a9f12"
|
||||
},
|
||||
"state": "1e5c2d0839f0",
|
||||
"effects": [
|
||||
"4a27571a011a",
|
||||
"18399efbe63d",
|
||||
"4e7ae49574b8",
|
||||
"1e1067bda316",
|
||||
"96ebe4a1b522",
|
||||
"121f5eaa52a4",
|
||||
"306fcb72ddc5",
|
||||
"453121dc6257"
|
||||
]
|
||||
"state": "85ed79b276e4",
|
||||
"effects": ["a507a5cf0d43", "a328c933a31f"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "host-worktree-refresh-stream.normal:replayed",
|
||||
"observation": {
|
||||
"sender": [],
|
||||
"payloads": ["eef1ee3c2773", "b6a25eca1497", "1d8fe76c29c7"],
|
||||
"payloads": ["eef1ee3c2773", "cc261d80eae0", "85612713e3f0"],
|
||||
"settlements": {
|
||||
"start": "eb79a9b3682a",
|
||||
"cutover": "f2af146a9f12"
|
||||
},
|
||||
"state": "edb34b7fcc08",
|
||||
"effects": [
|
||||
"4a27571a011a",
|
||||
"18399efbe63d",
|
||||
"4e7ae49574b8",
|
||||
"1e1067bda316",
|
||||
"96ebe4a1b522",
|
||||
"121f5eaa52a4",
|
||||
"306fcb72ddc5",
|
||||
"8fa922dbb034",
|
||||
"5073e6d40272"
|
||||
]
|
||||
"state": "322d549d1882",
|
||||
"effects": ["a507a5cf0d43", "354b03a10a5d"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "host-worktree-refresh-stream.normal:stopped",
|
||||
"observation": {
|
||||
"sender": [],
|
||||
"payloads": ["eef1ee3c2773", "b6a25eca1497", "1d8fe76c29c7", "3582e201880e"],
|
||||
"payloads": ["eef1ee3c2773", "cc261d80eae0", "85612713e3f0", "6ab454a38bb0"],
|
||||
"settlements": {
|
||||
"start": "eb79a9b3682a",
|
||||
"cutover": "f2af146a9f12",
|
||||
"stop": "f2af146a9f12"
|
||||
},
|
||||
"state": "91e9a84a208f",
|
||||
"effects": [
|
||||
"4a27571a011a",
|
||||
"18399efbe63d",
|
||||
"4e7ae49574b8",
|
||||
"1e1067bda316",
|
||||
"96ebe4a1b522",
|
||||
"121f5eaa52a4",
|
||||
"306fcb72ddc5",
|
||||
"8fa922dbb034",
|
||||
"5073e6d40272"
|
||||
]
|
||||
"state": "e3e3030326f0",
|
||||
"effects": ["a507a5cf0d43", "354b03a10a5d"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "host-worktree-refresh-stream.result-absent:replayed",
|
||||
"observation": {
|
||||
"sender": [],
|
||||
"payloads": ["eef1ee3c2773", "b6a25eca1497", "1d8fe76c29c7"],
|
||||
"payloads": ["eef1ee3c2773", "cc261d80eae0", "85612713e3f0"],
|
||||
"settlements": {
|
||||
"start": "eb79a9b3682a",
|
||||
"cutover": "f2af146a9f12"
|
||||
},
|
||||
"state": "1e5c2d0839f0",
|
||||
"effects": [
|
||||
"4a27571a011a",
|
||||
"18399efbe63d",
|
||||
"4e7ae49574b8",
|
||||
"1e1067bda316",
|
||||
"96ebe4a1b522",
|
||||
"121f5eaa52a4",
|
||||
"306fcb72ddc5"
|
||||
]
|
||||
"state": "85ed79b276e4",
|
||||
"effects": ["a507a5cf0d43"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "host-worktree-refresh-stream.result-absent:stopped",
|
||||
"observation": {
|
||||
"sender": [],
|
||||
"payloads": ["eef1ee3c2773", "b6a25eca1497", "1d8fe76c29c7"],
|
||||
"payloads": ["eef1ee3c2773", "cc261d80eae0", "85612713e3f0"],
|
||||
"settlements": {
|
||||
"start": "eb79a9b3682a",
|
||||
"cutover": "f2af146a9f12",
|
||||
"stop": "f2af146a9f12"
|
||||
},
|
||||
"state": "e14cb0033202",
|
||||
"effects": [
|
||||
"4a27571a011a",
|
||||
"18399efbe63d",
|
||||
"4e7ae49574b8",
|
||||
"1e1067bda316",
|
||||
"96ebe4a1b522",
|
||||
"121f5eaa52a4",
|
||||
"306fcb72ddc5"
|
||||
]
|
||||
"state": "7fc7f9b819e5",
|
||||
"effects": ["a507a5cf0d43"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "host-worktree-refresh-stream.result-absent:cleanup",
|
||||
"observation": {
|
||||
"sender": [],
|
||||
"payloads": ["eef1ee3c2773", "b6a25eca1497", "1d8fe76c29c7"],
|
||||
"payloads": ["eef1ee3c2773", "cc261d80eae0", "85612713e3f0"],
|
||||
"settlements": {
|
||||
"start": "eb79a9b3682a",
|
||||
"cutover": "f2af146a9f12",
|
||||
"stop": "f2af146a9f12"
|
||||
},
|
||||
"state": "e14cb0033202",
|
||||
"effects": [
|
||||
"4a27571a011a",
|
||||
"18399efbe63d",
|
||||
"4e7ae49574b8",
|
||||
"1e1067bda316",
|
||||
"96ebe4a1b522",
|
||||
"121f5eaa52a4",
|
||||
"306fcb72ddc5",
|
||||
"453121dc6257"
|
||||
]
|
||||
"state": "7fc7f9b819e5",
|
||||
"effects": ["a507a5cf0d43", "a328c933a31f"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "host-worktree-refresh-stream.result-null:replayed",
|
||||
"observation": {
|
||||
"sender": [],
|
||||
"payloads": ["eef1ee3c2773", "b6a25eca1497", "1d8fe76c29c7"],
|
||||
"payloads": ["eef1ee3c2773", "cc261d80eae0", "85612713e3f0"],
|
||||
"settlements": {
|
||||
"start": "eb79a9b3682a",
|
||||
"cutover": "f2af146a9f12"
|
||||
},
|
||||
"state": "1e5c2d0839f0",
|
||||
"effects": [
|
||||
"4a27571a011a",
|
||||
"18399efbe63d",
|
||||
"4e7ae49574b8",
|
||||
"1e1067bda316",
|
||||
"96ebe4a1b522",
|
||||
"121f5eaa52a4",
|
||||
"306fcb72ddc5"
|
||||
]
|
||||
"state": "85ed79b276e4",
|
||||
"effects": ["a507a5cf0d43"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "host-worktree-refresh-stream.result-null:stopped",
|
||||
"observation": {
|
||||
"sender": [],
|
||||
"payloads": ["eef1ee3c2773", "b6a25eca1497", "1d8fe76c29c7"],
|
||||
"payloads": ["eef1ee3c2773", "cc261d80eae0", "85612713e3f0"],
|
||||
"settlements": {
|
||||
"start": "eb79a9b3682a",
|
||||
"cutover": "f2af146a9f12",
|
||||
"stop": "f2af146a9f12"
|
||||
},
|
||||
"state": "e14cb0033202",
|
||||
"effects": [
|
||||
"4a27571a011a",
|
||||
"18399efbe63d",
|
||||
"4e7ae49574b8",
|
||||
"1e1067bda316",
|
||||
"96ebe4a1b522",
|
||||
"121f5eaa52a4",
|
||||
"306fcb72ddc5"
|
||||
]
|
||||
"state": "7fc7f9b819e5",
|
||||
"effects": ["a507a5cf0d43"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "host-worktree-refresh-stream.result-null:cleanup",
|
||||
"observation": {
|
||||
"sender": [],
|
||||
"payloads": ["eef1ee3c2773", "b6a25eca1497", "1d8fe76c29c7"],
|
||||
"payloads": ["eef1ee3c2773", "cc261d80eae0", "85612713e3f0"],
|
||||
"settlements": {
|
||||
"start": "eb79a9b3682a",
|
||||
"cutover": "f2af146a9f12",
|
||||
"stop": "f2af146a9f12"
|
||||
},
|
||||
"state": "e14cb0033202",
|
||||
"effects": [
|
||||
"4a27571a011a",
|
||||
"18399efbe63d",
|
||||
"4e7ae49574b8",
|
||||
"1e1067bda316",
|
||||
"96ebe4a1b522",
|
||||
"121f5eaa52a4",
|
||||
"306fcb72ddc5",
|
||||
"453121dc6257"
|
||||
]
|
||||
"state": "7fc7f9b819e5",
|
||||
"effects": ["a507a5cf0d43", "a328c933a31f"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "host-worktree-refresh-stream.inner-ok-missing:replayed",
|
||||
"observation": {
|
||||
"sender": [],
|
||||
"payloads": ["eef1ee3c2773", "b6a25eca1497", "1d8fe76c29c7"],
|
||||
"payloads": ["eef1ee3c2773", "cc261d80eae0", "85612713e3f0"],
|
||||
"settlements": {
|
||||
"start": "eb79a9b3682a",
|
||||
"cutover": "f2af146a9f12"
|
||||
},
|
||||
"state": "1e5c2d0839f0",
|
||||
"effects": [
|
||||
"4a27571a011a",
|
||||
"18399efbe63d",
|
||||
"4e7ae49574b8",
|
||||
"1e1067bda316",
|
||||
"96ebe4a1b522",
|
||||
"121f5eaa52a4",
|
||||
"306fcb72ddc5"
|
||||
]
|
||||
"state": "85ed79b276e4",
|
||||
"effects": ["a507a5cf0d43"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "host-worktree-refresh-stream.inner-ok-missing:stopped",
|
||||
"observation": {
|
||||
"sender": [],
|
||||
"payloads": ["eef1ee3c2773", "b6a25eca1497", "1d8fe76c29c7"],
|
||||
"payloads": ["eef1ee3c2773", "cc261d80eae0", "85612713e3f0"],
|
||||
"settlements": {
|
||||
"start": "eb79a9b3682a",
|
||||
"cutover": "f2af146a9f12",
|
||||
"stop": "f2af146a9f12"
|
||||
},
|
||||
"state": "e14cb0033202",
|
||||
"effects": [
|
||||
"4a27571a011a",
|
||||
"18399efbe63d",
|
||||
"4e7ae49574b8",
|
||||
"1e1067bda316",
|
||||
"96ebe4a1b522",
|
||||
"121f5eaa52a4",
|
||||
"306fcb72ddc5"
|
||||
]
|
||||
"state": "7fc7f9b819e5",
|
||||
"effects": ["a507a5cf0d43"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "host-worktree-refresh-stream.inner-ok-missing:cleanup",
|
||||
"observation": {
|
||||
"sender": [],
|
||||
"payloads": ["eef1ee3c2773", "b6a25eca1497", "1d8fe76c29c7"],
|
||||
"payloads": ["eef1ee3c2773", "cc261d80eae0", "85612713e3f0"],
|
||||
"settlements": {
|
||||
"start": "eb79a9b3682a",
|
||||
"cutover": "f2af146a9f12",
|
||||
"stop": "f2af146a9f12"
|
||||
},
|
||||
"state": "e14cb0033202",
|
||||
"effects": [
|
||||
"4a27571a011a",
|
||||
"18399efbe63d",
|
||||
"4e7ae49574b8",
|
||||
"1e1067bda316",
|
||||
"96ebe4a1b522",
|
||||
"121f5eaa52a4",
|
||||
"306fcb72ddc5",
|
||||
"453121dc6257"
|
||||
]
|
||||
"state": "7fc7f9b819e5",
|
||||
"effects": ["a507a5cf0d43", "a328c933a31f"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "host-worktree-refresh-stream.inner-false-string-error:replayed",
|
||||
"observation": {
|
||||
"sender": [],
|
||||
"payloads": ["eef1ee3c2773", "b6a25eca1497", "1d8fe76c29c7"],
|
||||
"payloads": ["eef1ee3c2773", "cc261d80eae0", "85612713e3f0"],
|
||||
"settlements": {
|
||||
"start": "eb79a9b3682a",
|
||||
"cutover": "f2af146a9f12"
|
||||
},
|
||||
"state": "1e5c2d0839f0",
|
||||
"effects": [
|
||||
"4a27571a011a",
|
||||
"18399efbe63d",
|
||||
"4e7ae49574b8",
|
||||
"1e1067bda316",
|
||||
"96ebe4a1b522",
|
||||
"121f5eaa52a4",
|
||||
"306fcb72ddc5"
|
||||
]
|
||||
"state": "85ed79b276e4",
|
||||
"effects": ["a507a5cf0d43"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "host-worktree-refresh-stream.inner-false-string-error:stopped",
|
||||
"observation": {
|
||||
"sender": [],
|
||||
"payloads": ["eef1ee3c2773", "b6a25eca1497", "1d8fe76c29c7"],
|
||||
"payloads": ["eef1ee3c2773", "cc261d80eae0", "85612713e3f0"],
|
||||
"settlements": {
|
||||
"start": "eb79a9b3682a",
|
||||
"cutover": "f2af146a9f12",
|
||||
"stop": "f2af146a9f12"
|
||||
},
|
||||
"state": "e14cb0033202",
|
||||
"effects": [
|
||||
"4a27571a011a",
|
||||
"18399efbe63d",
|
||||
"4e7ae49574b8",
|
||||
"1e1067bda316",
|
||||
"96ebe4a1b522",
|
||||
"121f5eaa52a4",
|
||||
"306fcb72ddc5"
|
||||
]
|
||||
"state": "7fc7f9b819e5",
|
||||
"effects": ["a507a5cf0d43"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "host-worktree-refresh-stream.inner-false-string-error:cleanup",
|
||||
"observation": {
|
||||
"sender": [],
|
||||
"payloads": ["eef1ee3c2773", "b6a25eca1497", "1d8fe76c29c7"],
|
||||
"payloads": ["eef1ee3c2773", "cc261d80eae0", "85612713e3f0"],
|
||||
"settlements": {
|
||||
"start": "eb79a9b3682a",
|
||||
"cutover": "f2af146a9f12",
|
||||
"stop": "f2af146a9f12"
|
||||
},
|
||||
"state": "e14cb0033202",
|
||||
"effects": [
|
||||
"4a27571a011a",
|
||||
"18399efbe63d",
|
||||
"4e7ae49574b8",
|
||||
"1e1067bda316",
|
||||
"96ebe4a1b522",
|
||||
"121f5eaa52a4",
|
||||
"306fcb72ddc5",
|
||||
"453121dc6257"
|
||||
]
|
||||
"state": "7fc7f9b819e5",
|
||||
"effects": ["a507a5cf0d43", "a328c933a31f"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "host-worktree-refresh-stream.inner-false-object-error:replayed",
|
||||
"observation": {
|
||||
"sender": [],
|
||||
"payloads": ["eef1ee3c2773", "b6a25eca1497", "1d8fe76c29c7"],
|
||||
"payloads": ["eef1ee3c2773", "cc261d80eae0", "85612713e3f0"],
|
||||
"settlements": {
|
||||
"start": "eb79a9b3682a",
|
||||
"cutover": "f2af146a9f12"
|
||||
},
|
||||
"state": "1e5c2d0839f0",
|
||||
"effects": [
|
||||
"4a27571a011a",
|
||||
"18399efbe63d",
|
||||
"4e7ae49574b8",
|
||||
"1e1067bda316",
|
||||
"96ebe4a1b522",
|
||||
"121f5eaa52a4",
|
||||
"306fcb72ddc5"
|
||||
]
|
||||
"state": "85ed79b276e4",
|
||||
"effects": ["a507a5cf0d43"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "host-worktree-refresh-stream.inner-false-object-error:stopped",
|
||||
"observation": {
|
||||
"sender": [],
|
||||
"payloads": ["eef1ee3c2773", "b6a25eca1497", "1d8fe76c29c7"],
|
||||
"payloads": ["eef1ee3c2773", "cc261d80eae0", "85612713e3f0"],
|
||||
"settlements": {
|
||||
"start": "eb79a9b3682a",
|
||||
"cutover": "f2af146a9f12",
|
||||
"stop": "f2af146a9f12"
|
||||
},
|
||||
"state": "e14cb0033202",
|
||||
"effects": [
|
||||
"4a27571a011a",
|
||||
"18399efbe63d",
|
||||
"4e7ae49574b8",
|
||||
"1e1067bda316",
|
||||
"96ebe4a1b522",
|
||||
"121f5eaa52a4",
|
||||
"306fcb72ddc5"
|
||||
]
|
||||
"state": "7fc7f9b819e5",
|
||||
"effects": ["a507a5cf0d43"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "host-worktree-refresh-stream.inner-false-object-error:cleanup",
|
||||
"observation": {
|
||||
"sender": [],
|
||||
"payloads": ["eef1ee3c2773", "b6a25eca1497", "1d8fe76c29c7"],
|
||||
"payloads": ["eef1ee3c2773", "cc261d80eae0", "85612713e3f0"],
|
||||
"settlements": {
|
||||
"start": "eb79a9b3682a",
|
||||
"cutover": "f2af146a9f12",
|
||||
"stop": "f2af146a9f12"
|
||||
},
|
||||
"state": "e14cb0033202",
|
||||
"effects": [
|
||||
"4a27571a011a",
|
||||
"18399efbe63d",
|
||||
"4e7ae49574b8",
|
||||
"1e1067bda316",
|
||||
"96ebe4a1b522",
|
||||
"121f5eaa52a4",
|
||||
"306fcb72ddc5",
|
||||
"453121dc6257"
|
||||
]
|
||||
"state": "7fc7f9b819e5",
|
||||
"effects": ["a507a5cf0d43", "a328c933a31f"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "host-worktree-refresh-stream.outer-refused:replayed",
|
||||
"observation": {
|
||||
"sender": [],
|
||||
"payloads": ["eef1ee3c2773", "b6a25eca1497", "1d8fe76c29c7"],
|
||||
"payloads": ["eef1ee3c2773", "cc261d80eae0", "85612713e3f0"],
|
||||
"settlements": {
|
||||
"start": "eb79a9b3682a",
|
||||
"cutover": "f2af146a9f12"
|
||||
},
|
||||
"state": "1e5c2d0839f0",
|
||||
"effects": [
|
||||
"4a27571a011a",
|
||||
"18399efbe63d",
|
||||
"4e7ae49574b8",
|
||||
"1e1067bda316",
|
||||
"96ebe4a1b522",
|
||||
"121f5eaa52a4",
|
||||
"306fcb72ddc5"
|
||||
]
|
||||
"state": "85ed79b276e4",
|
||||
"effects": ["a507a5cf0d43"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "host-worktree-refresh-stream.outer-refused:stopped",
|
||||
"observation": {
|
||||
"sender": [],
|
||||
"payloads": ["eef1ee3c2773", "b6a25eca1497", "1d8fe76c29c7"],
|
||||
"payloads": ["eef1ee3c2773", "cc261d80eae0", "85612713e3f0"],
|
||||
"settlements": {
|
||||
"start": "eb79a9b3682a",
|
||||
"cutover": "f2af146a9f12",
|
||||
"stop": "f2af146a9f12"
|
||||
},
|
||||
"state": "e14cb0033202",
|
||||
"effects": [
|
||||
"4a27571a011a",
|
||||
"18399efbe63d",
|
||||
"4e7ae49574b8",
|
||||
"1e1067bda316",
|
||||
"96ebe4a1b522",
|
||||
"121f5eaa52a4",
|
||||
"306fcb72ddc5"
|
||||
]
|
||||
"state": "7fc7f9b819e5",
|
||||
"effects": ["a507a5cf0d43"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "host-worktree-refresh-stream.outer-refused-no-message:replayed",
|
||||
"observation": {
|
||||
"sender": [],
|
||||
"payloads": ["eef1ee3c2773", "b6a25eca1497", "1d8fe76c29c7"],
|
||||
"payloads": ["eef1ee3c2773", "cc261d80eae0", "85612713e3f0"],
|
||||
"settlements": {
|
||||
"start": "eb79a9b3682a",
|
||||
"cutover": "f2af146a9f12"
|
||||
},
|
||||
"state": "1e5c2d0839f0",
|
||||
"effects": [
|
||||
"4a27571a011a",
|
||||
"18399efbe63d",
|
||||
"4e7ae49574b8",
|
||||
"1e1067bda316",
|
||||
"96ebe4a1b522",
|
||||
"121f5eaa52a4",
|
||||
"306fcb72ddc5"
|
||||
]
|
||||
"state": "85ed79b276e4",
|
||||
"effects": ["a507a5cf0d43"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "host-worktree-refresh-stream.outer-refused-no-message:stopped",
|
||||
"observation": {
|
||||
"sender": [],
|
||||
"payloads": ["eef1ee3c2773", "b6a25eca1497", "1d8fe76c29c7"],
|
||||
"payloads": ["eef1ee3c2773", "cc261d80eae0", "85612713e3f0"],
|
||||
"settlements": {
|
||||
"start": "eb79a9b3682a",
|
||||
"cutover": "f2af146a9f12",
|
||||
"stop": "f2af146a9f12"
|
||||
},
|
||||
"state": "e14cb0033202",
|
||||
"effects": [
|
||||
"4a27571a011a",
|
||||
"18399efbe63d",
|
||||
"4e7ae49574b8",
|
||||
"1e1067bda316",
|
||||
"96ebe4a1b522",
|
||||
"121f5eaa52a4",
|
||||
"306fcb72ddc5"
|
||||
]
|
||||
"state": "7fc7f9b819e5",
|
||||
"effects": ["a507a5cf0d43"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "host-worktree-refresh-stream.method-not-found:replayed",
|
||||
"observation": {
|
||||
"sender": [],
|
||||
"payloads": ["eef1ee3c2773", "b6a25eca1497", "1d8fe76c29c7"],
|
||||
"payloads": ["eef1ee3c2773", "cc261d80eae0", "85612713e3f0"],
|
||||
"settlements": {
|
||||
"start": "eb79a9b3682a",
|
||||
"cutover": "f2af146a9f12"
|
||||
},
|
||||
"state": "1e5c2d0839f0",
|
||||
"effects": [
|
||||
"4a27571a011a",
|
||||
"18399efbe63d",
|
||||
"4e7ae49574b8",
|
||||
"1e1067bda316",
|
||||
"96ebe4a1b522",
|
||||
"121f5eaa52a4",
|
||||
"306fcb72ddc5"
|
||||
]
|
||||
"state": "85ed79b276e4",
|
||||
"effects": ["a507a5cf0d43"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "host-worktree-refresh-stream.method-not-found:stopped",
|
||||
"observation": {
|
||||
"sender": [],
|
||||
"payloads": ["eef1ee3c2773", "b6a25eca1497", "1d8fe76c29c7"],
|
||||
"payloads": ["eef1ee3c2773", "cc261d80eae0", "85612713e3f0"],
|
||||
"settlements": {
|
||||
"start": "eb79a9b3682a",
|
||||
"cutover": "f2af146a9f12",
|
||||
"stop": "f2af146a9f12"
|
||||
},
|
||||
"state": "e14cb0033202",
|
||||
"effects": [
|
||||
"4a27571a011a",
|
||||
"18399efbe63d",
|
||||
"4e7ae49574b8",
|
||||
"1e1067bda316",
|
||||
"96ebe4a1b522",
|
||||
"121f5eaa52a4",
|
||||
"306fcb72ddc5"
|
||||
]
|
||||
"state": "7fc7f9b819e5",
|
||||
"effects": ["a507a5cf0d43"]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
+391
-446
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+471
-475
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,218 @@
|
||||
"family": "settings.repo-metadata",
|
||||
"namedDeltas": [],
|
||||
"values": {
|
||||
"1006746b8523": {
|
||||
"0c5e27e25c9d": {
|
||||
"name": "repo.list#2",
|
||||
"ordinal": 24,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "repo.list"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"afterSnapshotId": {
|
||||
"$rpc": "null"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 60000
|
||||
}
|
||||
},
|
||||
"1681ec001eef": {
|
||||
"name": "host.platform#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "host.platform"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"23e43ee36880": {
|
||||
"name": "ssh.listTargetSummaries#1",
|
||||
"ordinal": 3,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "ssh.listTargetSummaries"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-2",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"targets": [
|
||||
{
|
||||
"id": "ssh-1",
|
||||
"label": "SSH"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"26d63e9c9a04": {
|
||||
"name": "settings.get#2",
|
||||
"ordinal": 26,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 60000
|
||||
}
|
||||
},
|
||||
"2c7d0be09df6": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 4,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"settings": {
|
||||
"defaultTuiAgent": "codex",
|
||||
"disabledTuiAgents": ["claude"],
|
||||
"hostSettingOverrides": {},
|
||||
"prBotAuthorOverrides": ["bot-user"],
|
||||
"visibleTaskProviders": ["github", "linear"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"2ce49d24efc2": {
|
||||
"name": "desktopRepos",
|
||||
"ordinal": 19,
|
||||
"value": [
|
||||
{
|
||||
"displayName": "Local",
|
||||
"executionHostId": "local",
|
||||
"id": "repo-1",
|
||||
"path": "repo-1"
|
||||
},
|
||||
{
|
||||
"displayName": "Remote",
|
||||
"executionHostId": "ssh:ssh-1",
|
||||
"id": "repo-2",
|
||||
"path": "repo-2"
|
||||
}
|
||||
]
|
||||
},
|
||||
"339b07cb7b7e": {
|
||||
"name": "repoHostIdByRepoId",
|
||||
"ordinal": 20,
|
||||
"value": [
|
||||
["repo-1", "local"],
|
||||
["repo-2", "ssh:ssh-1"]
|
||||
]
|
||||
},
|
||||
"36f133fb62a2": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 4,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"4256aff920c3": {
|
||||
"name": "repo.list#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
@@ -23,7 +234,7 @@
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -53,121 +264,35 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"18762ff064ed": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 6,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"settings": {
|
||||
"defaultTuiAgent": "codex",
|
||||
"disabledTuiAgents": ["claude"],
|
||||
"hostSettingOverrides": {},
|
||||
"prBotAuthorOverrides": ["bot-user"],
|
||||
"visibleTaskProviders": ["github", "linear"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"1a0e2c3ee678": {
|
||||
"4da870f5dca2": {
|
||||
"name": "repo.list#2",
|
||||
"ordinal": 15,
|
||||
"ordinal": 33,
|
||||
"json": "{\"id\":\"frame-5\",\"deviceToken\":\"recording-device\",\"method\":\"repo.list\",\"params\":{\"afterSnapshotId\":null}}"
|
||||
},
|
||||
"2544b7a5da61": {
|
||||
"name": "repo.list#2",
|
||||
"ordinal": 14,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "repo.list"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"afterSnapshotId": {
|
||||
"$rpc": "null"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 60000
|
||||
}
|
||||
"530c928eaf8c": {
|
||||
"name": "hostPlatform",
|
||||
"ordinal": 18,
|
||||
"value": "linux"
|
||||
},
|
||||
"299d571abf13": {
|
||||
"name": "ssh.listTargetSummaries#1",
|
||||
"ordinal": 5,
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
"566e2e9dcb22": {
|
||||
"name": "repoHostIdByRepoId",
|
||||
"ordinal": 15,
|
||||
"value": [
|
||||
["repo-1", "local"],
|
||||
["repo-2", "ssh:ssh-1"]
|
||||
]
|
||||
},
|
||||
"5248b37ec085": {
|
||||
"name": "host.platform#1",
|
||||
"6329121e40fe": {
|
||||
"name": "repoHostIdByRepoId",
|
||||
"ordinal": 7,
|
||||
"value": [
|
||||
["repo-1", "local"],
|
||||
["repo-2", "ssh:ssh-1"]
|
||||
]
|
||||
},
|
||||
"63eb42c118e7": {
|
||||
"name": "host.platform#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
@@ -176,13 +301,13 @@
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -199,24 +324,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"661b4721aec6": {
|
||||
"name": "desktopRepos",
|
||||
"ordinal": 3,
|
||||
"value": [
|
||||
{
|
||||
"displayName": "Local",
|
||||
"executionHostId": "local",
|
||||
"id": "repo-1",
|
||||
"path": "repo-1"
|
||||
},
|
||||
{
|
||||
"displayName": "Remote",
|
||||
"executionHostId": "ssh:ssh-1",
|
||||
"id": "repo-2",
|
||||
"path": "repo-2"
|
||||
}
|
||||
]
|
||||
},
|
||||
"68916f125c3c": {
|
||||
"desktopRepos": [
|
||||
{
|
||||
@@ -260,76 +367,89 @@
|
||||
["repo-2", "ssh:ssh-1"]
|
||||
]
|
||||
},
|
||||
"7c508088602c": {
|
||||
"name": "host.platform#1",
|
||||
"ordinal": 7,
|
||||
"args": [
|
||||
"6dfbe49d3992": {
|
||||
"name": "hostHealthById",
|
||||
"ordinal": 21,
|
||||
"value": []
|
||||
},
|
||||
"7ea9898b5804": {
|
||||
"name": "desktopRepos",
|
||||
"ordinal": 14,
|
||||
"value": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "host.platform"
|
||||
"displayName": "Local",
|
||||
"executionHostId": "local",
|
||||
"id": "repo-1",
|
||||
"path": "repo-1"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
"displayName": "Remote",
|
||||
"executionHostId": "ssh:ssh-1",
|
||||
"id": "repo-2",
|
||||
"path": "repo-2"
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
"8b618db22116": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 9,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
|
||||
},
|
||||
"902c0b2ed3ce": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 6,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
"9196fd1b065a": {
|
||||
"name": "settings.get#2",
|
||||
"ordinal": 35,
|
||||
"json": "{\"id\":\"frame-7\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
|
||||
},
|
||||
"91e7f4ef9a77": {
|
||||
"name": "hostPlatform",
|
||||
"ordinal": 13,
|
||||
"value": "linux"
|
||||
},
|
||||
"9270aeb7d9c6": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
"99d1cce8a6d0": {
|
||||
"name": "hostHealthById",
|
||||
"ordinal": 16,
|
||||
"value": []
|
||||
},
|
||||
"a004da5aa7f3": {
|
||||
"name": "hostLabelById",
|
||||
"ordinal": 17,
|
||||
"value": [["ssh:ssh-1", "SSH"]]
|
||||
},
|
||||
"ba68ba6e96f2": {
|
||||
"name": "ssh.listTargetSummaries#2",
|
||||
"ordinal": 25,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "ssh.listTargetSummaries"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 60000
|
||||
}
|
||||
},
|
||||
"ba8a19e3e2b1": {
|
||||
"status": "pending",
|
||||
"startedAt": 60000
|
||||
},
|
||||
"bec3a38203a5": {
|
||||
"name": "hostPlatform",
|
||||
"ordinal": 32,
|
||||
"value": "linux"
|
||||
},
|
||||
"c1246f5792ee": {
|
||||
"name": "ssh.listTargetSummaries#1",
|
||||
"ordinal": 8,
|
||||
@@ -340,23 +460,115 @@
|
||||
"ordinal": 10,
|
||||
"json": "{\"id\":\"frame-4\",\"deviceToken\":\"recording-device\",\"method\":\"host.platform\"}"
|
||||
},
|
||||
"c9ba0a0019e6": {
|
||||
"name": "hostPlatform",
|
||||
"ordinal": 23,
|
||||
"value": "linux"
|
||||
},
|
||||
"cd4a4ae5eeff": {
|
||||
"name": "repoHostIdByRepoId",
|
||||
"ordinal": 29,
|
||||
"value": [
|
||||
["repo-1", "local"],
|
||||
["repo-2", "ssh:ssh-1"]
|
||||
]
|
||||
},
|
||||
"cd9f1e701ac7": {
|
||||
"name": "desktopRepos",
|
||||
"ordinal": 6,
|
||||
"value": [
|
||||
{
|
||||
"displayName": "Local",
|
||||
"executionHostId": "local",
|
||||
"id": "repo-1",
|
||||
"path": "repo-1"
|
||||
},
|
||||
{
|
||||
"displayName": "Remote",
|
||||
"executionHostId": "ssh:ssh-1",
|
||||
"id": "repo-2",
|
||||
"path": "repo-2"
|
||||
}
|
||||
]
|
||||
},
|
||||
"d48e553742c5": {
|
||||
"name": "host.platform#2",
|
||||
"ordinal": 27,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "host.platform"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 60000
|
||||
}
|
||||
},
|
||||
"d9c464d0dc78": {
|
||||
"name": "hostLabelById",
|
||||
"ordinal": 12,
|
||||
"value": [["ssh:ssh-1", "SSH"]]
|
||||
},
|
||||
"da05e6ed1c69": {
|
||||
"name": "desktopRepos",
|
||||
"ordinal": 28,
|
||||
"value": [
|
||||
{
|
||||
"displayName": "Local",
|
||||
"executionHostId": "local",
|
||||
"id": "repo-1",
|
||||
"path": "repo-1"
|
||||
},
|
||||
{
|
||||
"displayName": "Remote",
|
||||
"executionHostId": "ssh:ssh-1",
|
||||
"id": "repo-2",
|
||||
"path": "repo-2"
|
||||
}
|
||||
]
|
||||
},
|
||||
"db19db55c4b8": {
|
||||
"name": "hostLabelById",
|
||||
"ordinal": 31,
|
||||
"value": [["ssh:ssh-1", "SSH"]]
|
||||
},
|
||||
"e2881f748a46": {
|
||||
"name": "hostHealthById",
|
||||
"ordinal": 11,
|
||||
"value": []
|
||||
},
|
||||
"e49da3e1abca": {
|
||||
"name": "repoHostIdByRepoId",
|
||||
"ordinal": 4,
|
||||
"value": [
|
||||
["repo-1", "local"],
|
||||
["repo-2", "ssh:ssh-1"]
|
||||
]
|
||||
"e330efe298db": {
|
||||
"name": "host.platform#2",
|
||||
"ordinal": 36,
|
||||
"json": "{\"id\":\"frame-8\",\"deviceToken\":\"recording-device\",\"method\":\"host.platform\"}"
|
||||
},
|
||||
"e4c373c1fb2b": {
|
||||
"name": "ssh.listTargetSummaries#2",
|
||||
"ordinal": 34,
|
||||
"json": "{\"id\":\"frame-6\",\"deviceToken\":\"recording-device\",\"method\":\"ssh.listTargetSummaries\"}"
|
||||
},
|
||||
"e54603beee59": {
|
||||
"name": "hostLabelById",
|
||||
"ordinal": 22,
|
||||
"value": [["ssh:ssh-1", "SSH"]]
|
||||
},
|
||||
"eb02368caabd": {
|
||||
"name": "hostHealthById",
|
||||
"ordinal": 30,
|
||||
"value": []
|
||||
},
|
||||
"eb79a9b3682a": {
|
||||
"status": "fulfilled",
|
||||
@@ -386,20 +598,20 @@
|
||||
{
|
||||
"id": "settings-pending",
|
||||
"observation": {
|
||||
"sender": ["1006746b8523", "299d571abf13", "902c0b2ed3ce", "7c508088602c"],
|
||||
"sender": ["4256aff920c3", "23e43ee36880", "36f133fb62a2", "1681ec001eef"],
|
||||
"payloads": ["f2274efe8aa6", "c1246f5792ee", "8b618db22116", "c1d6fc758acc"],
|
||||
"settlements": {
|
||||
"mount": "eb79a9b3682a",
|
||||
"load": "9270aeb7d9c6"
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "68916f125c3c",
|
||||
"effects": ["661b4721aec6", "e49da3e1abca"]
|
||||
"effects": ["cd9f1e701ac7", "6329121e40fe"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settled",
|
||||
"observation": {
|
||||
"sender": ["1006746b8523", "299d571abf13", "18762ff064ed", "5248b37ec085"],
|
||||
"sender": ["4256aff920c3", "23e43ee36880", "2c7d0be09df6", "63eb42c118e7"],
|
||||
"payloads": ["f2274efe8aa6", "c1246f5792ee", "8b618db22116", "c1d6fc758acc"],
|
||||
"settlements": {
|
||||
"mount": "eb79a9b3682a",
|
||||
@@ -407,8 +619,8 @@
|
||||
},
|
||||
"state": "6d43117882f3",
|
||||
"effects": [
|
||||
"661b4721aec6",
|
||||
"e49da3e1abca",
|
||||
"cd9f1e701ac7",
|
||||
"6329121e40fe",
|
||||
"e2881f748a46",
|
||||
"d9c464d0dc78",
|
||||
"91e7f4ef9a77"
|
||||
@@ -418,7 +630,7 @@
|
||||
{
|
||||
"id": "cache-hit",
|
||||
"observation": {
|
||||
"sender": ["1006746b8523", "299d571abf13", "18762ff064ed", "5248b37ec085"],
|
||||
"sender": ["4256aff920c3", "23e43ee36880", "2c7d0be09df6", "63eb42c118e7"],
|
||||
"payloads": ["f2274efe8aa6", "c1246f5792ee", "8b618db22116", "c1d6fc758acc"],
|
||||
"settlements": {
|
||||
"mount": "eb79a9b3682a",
|
||||
@@ -427,18 +639,23 @@
|
||||
},
|
||||
"state": "6d43117882f3",
|
||||
"effects": [
|
||||
"661b4721aec6",
|
||||
"e49da3e1abca",
|
||||
"cd9f1e701ac7",
|
||||
"6329121e40fe",
|
||||
"e2881f748a46",
|
||||
"d9c464d0dc78",
|
||||
"91e7f4ef9a77"
|
||||
"91e7f4ef9a77",
|
||||
"7ea9898b5804",
|
||||
"566e2e9dcb22",
|
||||
"99d1cce8a6d0",
|
||||
"a004da5aa7f3",
|
||||
"530c928eaf8c"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "cache-warm",
|
||||
"observation": {
|
||||
"sender": ["1006746b8523", "299d571abf13", "18762ff064ed", "5248b37ec085"],
|
||||
"sender": ["4256aff920c3", "23e43ee36880", "2c7d0be09df6", "63eb42c118e7"],
|
||||
"payloads": ["f2274efe8aa6", "c1246f5792ee", "8b618db22116", "c1d6fc758acc"],
|
||||
"settlements": {
|
||||
"mount": "eb79a9b3682a",
|
||||
@@ -448,11 +665,21 @@
|
||||
},
|
||||
"state": "6d43117882f3",
|
||||
"effects": [
|
||||
"661b4721aec6",
|
||||
"e49da3e1abca",
|
||||
"cd9f1e701ac7",
|
||||
"6329121e40fe",
|
||||
"e2881f748a46",
|
||||
"d9c464d0dc78",
|
||||
"91e7f4ef9a77"
|
||||
"91e7f4ef9a77",
|
||||
"7ea9898b5804",
|
||||
"566e2e9dcb22",
|
||||
"99d1cce8a6d0",
|
||||
"a004da5aa7f3",
|
||||
"530c928eaf8c",
|
||||
"2ce49d24efc2",
|
||||
"339b07cb7b7e",
|
||||
"6dfbe49d3992",
|
||||
"e54603beee59",
|
||||
"c9ba0a0019e6"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -460,18 +687,24 @@
|
||||
"id": "cache-expired",
|
||||
"observation": {
|
||||
"sender": [
|
||||
"1006746b8523",
|
||||
"299d571abf13",
|
||||
"18762ff064ed",
|
||||
"5248b37ec085",
|
||||
"2544b7a5da61"
|
||||
"4256aff920c3",
|
||||
"23e43ee36880",
|
||||
"2c7d0be09df6",
|
||||
"63eb42c118e7",
|
||||
"0c5e27e25c9d",
|
||||
"ba68ba6e96f2",
|
||||
"26d63e9c9a04",
|
||||
"d48e553742c5"
|
||||
],
|
||||
"payloads": [
|
||||
"f2274efe8aa6",
|
||||
"c1246f5792ee",
|
||||
"8b618db22116",
|
||||
"c1d6fc758acc",
|
||||
"1a0e2c3ee678"
|
||||
"4da870f5dca2",
|
||||
"e4c373c1fb2b",
|
||||
"9196fd1b065a",
|
||||
"e330efe298db"
|
||||
],
|
||||
"settlements": {
|
||||
"mount": "eb79a9b3682a",
|
||||
@@ -482,11 +715,26 @@
|
||||
},
|
||||
"state": "6d43117882f3",
|
||||
"effects": [
|
||||
"661b4721aec6",
|
||||
"e49da3e1abca",
|
||||
"cd9f1e701ac7",
|
||||
"6329121e40fe",
|
||||
"e2881f748a46",
|
||||
"d9c464d0dc78",
|
||||
"91e7f4ef9a77"
|
||||
"91e7f4ef9a77",
|
||||
"7ea9898b5804",
|
||||
"566e2e9dcb22",
|
||||
"99d1cce8a6d0",
|
||||
"a004da5aa7f3",
|
||||
"530c928eaf8c",
|
||||
"2ce49d24efc2",
|
||||
"339b07cb7b7e",
|
||||
"6dfbe49d3992",
|
||||
"e54603beee59",
|
||||
"c9ba0a0019e6",
|
||||
"da05e6ed1c69",
|
||||
"cd4a4ae5eeff",
|
||||
"eb02368caabd",
|
||||
"db19db55c4b8",
|
||||
"bec3a38203a5"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,138 @@
|
||||
"family": "settings.repo-metadata",
|
||||
"namedDeltas": [],
|
||||
"values": {
|
||||
"1006746b8523": {
|
||||
"1681ec001eef": {
|
||||
"name": "host.platform#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "host.platform"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"23e43ee36880": {
|
||||
"name": "ssh.listTargetSummaries#1",
|
||||
"ordinal": 3,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "ssh.listTargetSummaries"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-2",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"targets": [
|
||||
{
|
||||
"id": "ssh-1",
|
||||
"label": "SSH"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"2c7d0be09df6": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 4,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"settings": {
|
||||
"defaultTuiAgent": "codex",
|
||||
"disabledTuiAgents": ["claude"],
|
||||
"hostSettingOverrides": {},
|
||||
"prBotAuthorOverrides": ["bot-user"],
|
||||
"visibleTaskProviders": ["github", "linear"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"36f133fb62a2": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 4,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"4256aff920c3": {
|
||||
"name": "repo.list#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
@@ -23,7 +154,7 @@
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -53,88 +184,17 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"18762ff064ed": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 6,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"settings": {
|
||||
"defaultTuiAgent": "codex",
|
||||
"disabledTuiAgents": ["claude"],
|
||||
"hostSettingOverrides": {},
|
||||
"prBotAuthorOverrides": ["bot-user"],
|
||||
"visibleTaskProviders": ["github", "linear"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"299d571abf13": {
|
||||
"name": "ssh.listTargetSummaries#1",
|
||||
"ordinal": 5,
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"5248b37ec085": {
|
||||
"name": "host.platform#1",
|
||||
"6329121e40fe": {
|
||||
"name": "repoHostIdByRepoId",
|
||||
"ordinal": 7,
|
||||
"value": [
|
||||
["repo-1", "local"],
|
||||
["repo-2", "ssh:ssh-1"]
|
||||
]
|
||||
},
|
||||
"63eb42c118e7": {
|
||||
"name": "host.platform#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
@@ -143,13 +203,13 @@
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -166,24 +226,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"661b4721aec6": {
|
||||
"name": "desktopRepos",
|
||||
"ordinal": 3,
|
||||
"value": [
|
||||
{
|
||||
"displayName": "Local",
|
||||
"executionHostId": "local",
|
||||
"id": "repo-1",
|
||||
"path": "repo-1"
|
||||
},
|
||||
{
|
||||
"displayName": "Remote",
|
||||
"executionHostId": "ssh:ssh-1",
|
||||
"id": "repo-2",
|
||||
"path": "repo-2"
|
||||
}
|
||||
]
|
||||
},
|
||||
"68916f125c3c": {
|
||||
"desktopRepos": [
|
||||
{
|
||||
@@ -227,72 +269,16 @@
|
||||
["repo-2", "ssh:ssh-1"]
|
||||
]
|
||||
},
|
||||
"7c508088602c": {
|
||||
"name": "host.platform#1",
|
||||
"ordinal": 7,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "host.platform"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"8b618db22116": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 9,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
|
||||
},
|
||||
"902c0b2ed3ce": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 6,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"91e7f4ef9a77": {
|
||||
"name": "hostPlatform",
|
||||
"ordinal": 13,
|
||||
"value": "linux"
|
||||
},
|
||||
"9270aeb7d9c6": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
},
|
||||
"c1246f5792ee": {
|
||||
"name": "ssh.listTargetSummaries#1",
|
||||
"ordinal": 8,
|
||||
@@ -303,6 +289,24 @@
|
||||
"ordinal": 10,
|
||||
"json": "{\"id\":\"frame-4\",\"deviceToken\":\"recording-device\",\"method\":\"host.platform\"}"
|
||||
},
|
||||
"cd9f1e701ac7": {
|
||||
"name": "desktopRepos",
|
||||
"ordinal": 6,
|
||||
"value": [
|
||||
{
|
||||
"displayName": "Local",
|
||||
"executionHostId": "local",
|
||||
"id": "repo-1",
|
||||
"path": "repo-1"
|
||||
},
|
||||
{
|
||||
"displayName": "Remote",
|
||||
"executionHostId": "ssh:ssh-1",
|
||||
"id": "repo-2",
|
||||
"path": "repo-2"
|
||||
}
|
||||
]
|
||||
},
|
||||
"d9c464d0dc78": {
|
||||
"name": "hostLabelById",
|
||||
"ordinal": 12,
|
||||
@@ -313,14 +317,6 @@
|
||||
"ordinal": 11,
|
||||
"value": []
|
||||
},
|
||||
"e49da3e1abca": {
|
||||
"name": "repoHostIdByRepoId",
|
||||
"ordinal": 4,
|
||||
"value": [
|
||||
["repo-1", "local"],
|
||||
["repo-2", "ssh:ssh-1"]
|
||||
]
|
||||
},
|
||||
"eb79a9b3682a": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
@@ -341,20 +337,20 @@
|
||||
{
|
||||
"id": "settings-pending",
|
||||
"observation": {
|
||||
"sender": ["1006746b8523", "299d571abf13", "902c0b2ed3ce", "7c508088602c"],
|
||||
"sender": ["4256aff920c3", "23e43ee36880", "36f133fb62a2", "1681ec001eef"],
|
||||
"payloads": ["f2274efe8aa6", "c1246f5792ee", "8b618db22116", "c1d6fc758acc"],
|
||||
"settlements": {
|
||||
"mount": "eb79a9b3682a",
|
||||
"load": "9270aeb7d9c6"
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "68916f125c3c",
|
||||
"effects": ["661b4721aec6", "e49da3e1abca"]
|
||||
"effects": ["cd9f1e701ac7", "6329121e40fe"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settled",
|
||||
"observation": {
|
||||
"sender": ["1006746b8523", "299d571abf13", "18762ff064ed", "5248b37ec085"],
|
||||
"sender": ["4256aff920c3", "23e43ee36880", "2c7d0be09df6", "63eb42c118e7"],
|
||||
"payloads": ["f2274efe8aa6", "c1246f5792ee", "8b618db22116", "c1d6fc758acc"],
|
||||
"settlements": {
|
||||
"mount": "eb79a9b3682a",
|
||||
@@ -362,8 +358,8 @@
|
||||
},
|
||||
"state": "6d43117882f3",
|
||||
"effects": [
|
||||
"661b4721aec6",
|
||||
"e49da3e1abca",
|
||||
"cd9f1e701ac7",
|
||||
"6329121e40fe",
|
||||
"e2881f748a46",
|
||||
"d9c464d0dc78",
|
||||
"91e7f4ef9a77"
|
||||
|
||||
@@ -49,14 +49,40 @@
|
||||
["repo-3", "ssh:ssh-1"]
|
||||
]
|
||||
},
|
||||
"1681ec001eef": {
|
||||
"name": "host.platform#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "host.platform"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"1c76208c9beb": {
|
||||
"name": "hostPlatform",
|
||||
"ordinal": 13,
|
||||
"value": "darwin"
|
||||
},
|
||||
"299d571abf13": {
|
||||
"23e43ee36880": {
|
||||
"name": "ssh.listTargetSummaries#1",
|
||||
"ordinal": 5,
|
||||
"ordinal": 3,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
@@ -65,13 +91,13 @@
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -93,45 +119,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"466c0317a24a": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 6,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"settings": {
|
||||
"defaultTuiAgent": "codex",
|
||||
"disabledTuiAgents": [],
|
||||
"hostSettingOverrides": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"4e7d34bca898": {
|
||||
"2cf345170cd3": {
|
||||
"name": "repo.list#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
@@ -150,7 +138,7 @@
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -200,40 +188,9 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"7c508088602c": {
|
||||
"name": "host.platform#1",
|
||||
"ordinal": 7,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "host.platform"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"8b618db22116": {
|
||||
"36f133fb62a2": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 9,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
|
||||
},
|
||||
"902c0b2ed3ce": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 6,
|
||||
"ordinal": 4,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
@@ -242,13 +199,13 @@
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -257,6 +214,84 @@
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"5d8efd873525": {
|
||||
"name": "desktopRepos",
|
||||
"ordinal": 6,
|
||||
"value": [
|
||||
{
|
||||
"badgeColor": "#123456",
|
||||
"displayName": "Lucide",
|
||||
"executionHostId": "local",
|
||||
"id": "repo-1",
|
||||
"path": "repo-1",
|
||||
"repoIcon": {
|
||||
"name": "Rocket",
|
||||
"type": "lucide"
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayName": "Emoji",
|
||||
"executionHostId": "ssh:ssh-1",
|
||||
"id": "repo-2",
|
||||
"path": "repo-2",
|
||||
"repoIcon": {
|
||||
"emoji": "🐳",
|
||||
"type": "emoji"
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayName": "Avatar",
|
||||
"executionHostId": "ssh:ssh-1",
|
||||
"id": "repo-3",
|
||||
"path": "repo-3",
|
||||
"repoIcon": {
|
||||
"label": "acme/orca",
|
||||
"source": "github",
|
||||
"src": "https://github.com/acme.png?size=64",
|
||||
"type": "image"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"6134cf1a642b": {
|
||||
"name": "host.platform#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "host.platform"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-4",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"platform": "darwin"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"8b618db22116": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 9,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
|
||||
},
|
||||
"910617d262f4": {
|
||||
"desktopRepos": [
|
||||
{
|
||||
@@ -299,47 +334,9 @@
|
||||
["repo-3", "ssh:ssh-1"]
|
||||
]
|
||||
},
|
||||
"9270aeb7d9c6": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
},
|
||||
"9bc91d02ce24": {
|
||||
"name": "host.platform#1",
|
||||
"ordinal": 7,
|
||||
"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-4",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"platform": "darwin"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"b4ad28ffe331": {
|
||||
"b536bda0aa88": {
|
||||
"name": "repoHostIdByRepoId",
|
||||
"ordinal": 4,
|
||||
"ordinal": 7,
|
||||
"value": [
|
||||
["repo-1", "local"],
|
||||
["repo-2", "ssh:ssh-1"],
|
||||
@@ -366,6 +363,44 @@
|
||||
"ordinal": 11,
|
||||
"value": []
|
||||
},
|
||||
"e5a64dcd2008": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 4,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"settings": {
|
||||
"defaultTuiAgent": "codex",
|
||||
"disabledTuiAgents": [],
|
||||
"hostSettingOverrides": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"eb79a9b3682a": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
@@ -378,45 +413,6 @@
|
||||
"name": "repo.list#1",
|
||||
"ordinal": 2,
|
||||
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"repo.list\",\"params\":{\"afterSnapshotId\":null}}"
|
||||
},
|
||||
"f9217d79f53e": {
|
||||
"name": "desktopRepos",
|
||||
"ordinal": 3,
|
||||
"value": [
|
||||
{
|
||||
"badgeColor": "#123456",
|
||||
"displayName": "Lucide",
|
||||
"executionHostId": "local",
|
||||
"id": "repo-1",
|
||||
"path": "repo-1",
|
||||
"repoIcon": {
|
||||
"name": "Rocket",
|
||||
"type": "lucide"
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayName": "Emoji",
|
||||
"executionHostId": "ssh:ssh-1",
|
||||
"id": "repo-2",
|
||||
"path": "repo-2",
|
||||
"repoIcon": {
|
||||
"emoji": "🐳",
|
||||
"type": "emoji"
|
||||
}
|
||||
},
|
||||
{
|
||||
"displayName": "Avatar",
|
||||
"executionHostId": "ssh:ssh-1",
|
||||
"id": "repo-3",
|
||||
"path": "repo-3",
|
||||
"repoIcon": {
|
||||
"label": "acme/orca",
|
||||
"source": "github",
|
||||
"src": "https://github.com/acme.png?size=64",
|
||||
"type": "image"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"recording": {
|
||||
@@ -425,20 +421,20 @@
|
||||
{
|
||||
"id": "settings-pending",
|
||||
"observation": {
|
||||
"sender": ["4e7d34bca898", "299d571abf13", "902c0b2ed3ce", "7c508088602c"],
|
||||
"sender": ["2cf345170cd3", "23e43ee36880", "36f133fb62a2", "1681ec001eef"],
|
||||
"payloads": ["f2274efe8aa6", "c1246f5792ee", "8b618db22116", "c1d6fc758acc"],
|
||||
"settlements": {
|
||||
"mount": "eb79a9b3682a",
|
||||
"load": "9270aeb7d9c6"
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "910617d262f4",
|
||||
"effects": ["f9217d79f53e", "b4ad28ffe331"]
|
||||
"effects": ["5d8efd873525", "b536bda0aa88"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settled",
|
||||
"observation": {
|
||||
"sender": ["4e7d34bca898", "299d571abf13", "466c0317a24a", "9bc91d02ce24"],
|
||||
"sender": ["2cf345170cd3", "23e43ee36880", "e5a64dcd2008", "6134cf1a642b"],
|
||||
"payloads": ["f2274efe8aa6", "c1246f5792ee", "8b618db22116", "c1d6fc758acc"],
|
||||
"settlements": {
|
||||
"mount": "eb79a9b3682a",
|
||||
@@ -446,8 +442,8 @@
|
||||
},
|
||||
"state": "0fd72e1aba93",
|
||||
"effects": [
|
||||
"f9217d79f53e",
|
||||
"b4ad28ffe331",
|
||||
"5d8efd873525",
|
||||
"b536bda0aa88",
|
||||
"e2881f748a46",
|
||||
"d9c464d0dc78",
|
||||
"1c76208c9beb"
|
||||
|
||||
@@ -4,14 +4,45 @@
|
||||
"family": "settings.repo-metadata",
|
||||
"namedDeltas": [],
|
||||
"values": {
|
||||
"00d007a90b2c": {
|
||||
"02e77ef2be85": {
|
||||
"name": "hostPlatform",
|
||||
"ordinal": 26,
|
||||
"ordinal": 24,
|
||||
"value": "linux"
|
||||
},
|
||||
"1006746b8523": {
|
||||
"name": "repo.list#1",
|
||||
"ordinal": 1,
|
||||
"13beaf7a27e8": {
|
||||
"name": "ssh.listTargetSummaries#2",
|
||||
"ordinal": 19,
|
||||
"json": "{\"id\":\"frame-6\",\"deviceToken\":\"recording-device\",\"method\":\"ssh.listTargetSummaries\"}"
|
||||
},
|
||||
"1681ec001eef": {
|
||||
"name": "host.platform#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "host.platform"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"1d5e25c772b7": {
|
||||
"name": "repo.list#2",
|
||||
"ordinal": 14,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
@@ -28,7 +59,33 @@
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"23e43ee36880": {
|
||||
"name": "ssh.listTargetSummaries#1",
|
||||
"ordinal": 3,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "ssh.listTargetSummaries"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -37,30 +94,22 @@
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-1",
|
||||
"id": "frame-2",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"repos": [
|
||||
"targets": [
|
||||
{
|
||||
"connectionId": {
|
||||
"$rpc": "null"
|
||||
},
|
||||
"displayName": "Local",
|
||||
"id": "repo-1"
|
||||
},
|
||||
{
|
||||
"connectionId": "ssh-1",
|
||||
"displayName": "Remote",
|
||||
"id": "repo-2"
|
||||
"id": "ssh-1",
|
||||
"label": "SSH"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"18762ff064ed": {
|
||||
"2c7d0be09df6": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 6,
|
||||
"ordinal": 4,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
@@ -69,13 +118,13 @@
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -98,103 +147,33 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"1a0e2c3ee678": {
|
||||
"name": "repo.list#2",
|
||||
"ordinal": 15,
|
||||
"json": "{\"id\":\"frame-5\",\"deviceToken\":\"recording-device\",\"method\":\"repo.list\",\"params\":{\"afterSnapshotId\":null}}"
|
||||
},
|
||||
"1e4a3aa71af5": {
|
||||
"name": "host.platform#2",
|
||||
"ordinal": 20,
|
||||
"36f133fb62a2": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 4,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "host.platform"
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-8",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"platform": "linux"
|
||||
}
|
||||
}
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"299d571abf13": {
|
||||
"name": "ssh.listTargetSummaries#1",
|
||||
"ordinal": 5,
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"306fcf2b8536": {
|
||||
"name": "desktopRepos",
|
||||
"ordinal": 16,
|
||||
"value": [
|
||||
{
|
||||
"displayName": "Local",
|
||||
"executionHostId": "local",
|
||||
"id": "repo-1",
|
||||
"path": "repo-1"
|
||||
},
|
||||
{
|
||||
"displayName": "Remote",
|
||||
"executionHostId": "ssh:ssh-1",
|
||||
"id": "repo-2",
|
||||
"path": "repo-2"
|
||||
}
|
||||
]
|
||||
},
|
||||
"429bb347383c": {
|
||||
"410308add3d5": {
|
||||
"name": "repo.list#2",
|
||||
"ordinal": 14,
|
||||
"args": [
|
||||
@@ -213,7 +192,7 @@
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -243,9 +222,102 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"5248b37ec085": {
|
||||
"name": "host.platform#1",
|
||||
"4256aff920c3": {
|
||||
"name": "repo.list#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "repo.list"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"afterSnapshotId": {
|
||||
"$rpc": "null"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-1",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"repos": [
|
||||
{
|
||||
"connectionId": {
|
||||
"$rpc": "null"
|
||||
},
|
||||
"displayName": "Local",
|
||||
"id": "repo-1"
|
||||
},
|
||||
{
|
||||
"connectionId": "ssh-1",
|
||||
"displayName": "Remote",
|
||||
"id": "repo-2"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"4f7ea9e9937e": {
|
||||
"name": "ssh.listTargetSummaries#2",
|
||||
"ordinal": 15,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "ssh.listTargetSummaries"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"555823fff23f": {
|
||||
"name": "hostLabelById",
|
||||
"ordinal": 23,
|
||||
"value": [["ssh:ssh-1", "SSH"]]
|
||||
},
|
||||
"5c18f152fb65": {
|
||||
"name": "hostHealthById",
|
||||
"ordinal": 22,
|
||||
"value": []
|
||||
},
|
||||
"6329121e40fe": {
|
||||
"name": "repoHostIdByRepoId",
|
||||
"ordinal": 7,
|
||||
"value": [
|
||||
["repo-1", "local"],
|
||||
["repo-2", "ssh:ssh-1"]
|
||||
]
|
||||
},
|
||||
"63eb42c118e7": {
|
||||
"name": "host.platform#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
@@ -254,13 +326,13 @@
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -277,24 +349,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"661b4721aec6": {
|
||||
"name": "desktopRepos",
|
||||
"ordinal": 3,
|
||||
"value": [
|
||||
{
|
||||
"displayName": "Local",
|
||||
"executionHostId": "local",
|
||||
"id": "repo-1",
|
||||
"path": "repo-1"
|
||||
},
|
||||
{
|
||||
"displayName": "Remote",
|
||||
"executionHostId": "ssh:ssh-1",
|
||||
"id": "repo-2",
|
||||
"path": "repo-2"
|
||||
}
|
||||
]
|
||||
},
|
||||
"68916f125c3c": {
|
||||
"desktopRepos": [
|
||||
{
|
||||
@@ -315,11 +369,6 @@
|
||||
["repo-2", "ssh:ssh-1"]
|
||||
]
|
||||
},
|
||||
"69f575e15e15": {
|
||||
"name": "settings.get#2",
|
||||
"ordinal": 22,
|
||||
"json": "{\"id\":\"frame-7\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
|
||||
},
|
||||
"6d43117882f3": {
|
||||
"desktopRepos": [
|
||||
{
|
||||
@@ -343,68 +392,9 @@
|
||||
["repo-2", "ssh:ssh-1"]
|
||||
]
|
||||
},
|
||||
"6fd92b34ee34": {
|
||||
"name": "repo.list#2",
|
||||
"ordinal": 14,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "repo.list"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"afterSnapshotId": {
|
||||
"$rpc": "null"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"7c508088602c": {
|
||||
"name": "host.platform#1",
|
||||
"ordinal": 7,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "host.platform"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"8b618db22116": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 9,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
|
||||
},
|
||||
"902c0b2ed3ce": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 6,
|
||||
"7f10275fa104": {
|
||||
"name": "settings.get#2",
|
||||
"ordinal": 16,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
@@ -413,21 +403,35 @@
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"error": {
|
||||
"code": "refused",
|
||||
"message": "settings refused"
|
||||
},
|
||||
"id": "frame-7",
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"8b618db22116": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 9,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
|
||||
},
|
||||
"91e7f4ef9a77": {
|
||||
"name": "hostPlatform",
|
||||
"ordinal": 13,
|
||||
@@ -437,24 +441,35 @@
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
},
|
||||
"9b915e5b8831": {
|
||||
"name": "host.platform#2",
|
||||
"ordinal": 23,
|
||||
"json": "{\"id\":\"frame-8\",\"deviceToken\":\"recording-device\",\"method\":\"host.platform\"}"
|
||||
"adc72cf757d5": {
|
||||
"name": "settings.get#2",
|
||||
"ordinal": 16,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"a465a07eb4b0": {
|
||||
"name": "hostLabelById",
|
||||
"ordinal": 25,
|
||||
"value": [["ssh:ssh-1", "SSH"]]
|
||||
},
|
||||
"af3366c70550": {
|
||||
"b448db7f57b7": {
|
||||
"name": "ssh.listTargetSummaries#2",
|
||||
"ordinal": 21,
|
||||
"json": "{\"id\":\"frame-6\",\"deviceToken\":\"recording-device\",\"method\":\"ssh.listTargetSummaries\"}"
|
||||
},
|
||||
"bc879379ce13": {
|
||||
"name": "ssh.listTargetSummaries#2",
|
||||
"ordinal": 18,
|
||||
"ordinal": 15,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
@@ -463,13 +478,13 @@
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -491,14 +506,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"bd0d18cd2160": {
|
||||
"name": "repoHostIdByRepoId",
|
||||
"ordinal": 17,
|
||||
"value": [
|
||||
["repo-1", "local"],
|
||||
["repo-2", "ssh:ssh-1"]
|
||||
]
|
||||
},
|
||||
"c1246f5792ee": {
|
||||
"name": "ssh.listTargetSummaries#1",
|
||||
"ordinal": 8,
|
||||
@@ -509,6 +516,63 @@
|
||||
"ordinal": 10,
|
||||
"json": "{\"id\":\"frame-4\",\"deviceToken\":\"recording-device\",\"method\":\"host.platform\"}"
|
||||
},
|
||||
"c7ebc1837451": {
|
||||
"name": "repo.list#2",
|
||||
"ordinal": 18,
|
||||
"json": "{\"id\":\"frame-5\",\"deviceToken\":\"recording-device\",\"method\":\"repo.list\",\"params\":{\"afterSnapshotId\":null}}"
|
||||
},
|
||||
"cd9f1e701ac7": {
|
||||
"name": "desktopRepos",
|
||||
"ordinal": 6,
|
||||
"value": [
|
||||
{
|
||||
"displayName": "Local",
|
||||
"executionHostId": "local",
|
||||
"id": "repo-1",
|
||||
"path": "repo-1"
|
||||
},
|
||||
{
|
||||
"displayName": "Remote",
|
||||
"executionHostId": "ssh:ssh-1",
|
||||
"id": "repo-2",
|
||||
"path": "repo-2"
|
||||
}
|
||||
]
|
||||
},
|
||||
"cf4d44730d0f": {
|
||||
"name": "host.platform#2",
|
||||
"ordinal": 17,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "host.platform"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-8",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"platform": "linux"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"d9c464d0dc78": {
|
||||
"name": "hostLabelById",
|
||||
"ordinal": 12,
|
||||
@@ -519,53 +583,10 @@
|
||||
"ordinal": 11,
|
||||
"value": []
|
||||
},
|
||||
"e49da3e1abca": {
|
||||
"name": "repoHostIdByRepoId",
|
||||
"ordinal": 4,
|
||||
"value": [
|
||||
["repo-1", "local"],
|
||||
["repo-2", "ssh:ssh-1"]
|
||||
]
|
||||
},
|
||||
"e7d590a1a17e": {
|
||||
"name": "hostHealthById",
|
||||
"ordinal": 24,
|
||||
"value": []
|
||||
},
|
||||
"eab4940c8ca3": {
|
||||
"name": "settings.get#2",
|
||||
"ordinal": 19,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"error": {
|
||||
"code": "refused",
|
||||
"message": "settings refused"
|
||||
},
|
||||
"id": "frame-7",
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
"e930a147c91d": {
|
||||
"name": "host.platform#2",
|
||||
"ordinal": 21,
|
||||
"json": "{\"id\":\"frame-8\",\"deviceToken\":\"recording-device\",\"method\":\"host.platform\"}"
|
||||
},
|
||||
"eb79a9b3682a": {
|
||||
"status": "fulfilled",
|
||||
@@ -575,6 +596,37 @@
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
"edf1e0b1856e": {
|
||||
"name": "settings.get#2",
|
||||
"ordinal": 20,
|
||||
"json": "{\"id\":\"frame-7\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
|
||||
},
|
||||
"ef8a0c2f003b": {
|
||||
"name": "host.platform#2",
|
||||
"ordinal": 17,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "host.platform"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"f2274efe8aa6": {
|
||||
"name": "repo.list#1",
|
||||
"ordinal": 2,
|
||||
@@ -587,20 +639,20 @@
|
||||
{
|
||||
"id": "settings-pending",
|
||||
"observation": {
|
||||
"sender": ["1006746b8523", "299d571abf13", "902c0b2ed3ce", "7c508088602c"],
|
||||
"sender": ["4256aff920c3", "23e43ee36880", "36f133fb62a2", "1681ec001eef"],
|
||||
"payloads": ["f2274efe8aa6", "c1246f5792ee", "8b618db22116", "c1d6fc758acc"],
|
||||
"settlements": {
|
||||
"mount": "eb79a9b3682a",
|
||||
"load": "9270aeb7d9c6"
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "68916f125c3c",
|
||||
"effects": ["661b4721aec6", "e49da3e1abca"]
|
||||
"effects": ["cd9f1e701ac7", "6329121e40fe"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settled",
|
||||
"observation": {
|
||||
"sender": ["1006746b8523", "299d571abf13", "18762ff064ed", "5248b37ec085"],
|
||||
"sender": ["4256aff920c3", "23e43ee36880", "2c7d0be09df6", "63eb42c118e7"],
|
||||
"payloads": ["f2274efe8aa6", "c1246f5792ee", "8b618db22116", "c1d6fc758acc"],
|
||||
"settlements": {
|
||||
"mount": "eb79a9b3682a",
|
||||
@@ -608,8 +660,8 @@
|
||||
},
|
||||
"state": "6d43117882f3",
|
||||
"effects": [
|
||||
"661b4721aec6",
|
||||
"e49da3e1abca",
|
||||
"cd9f1e701ac7",
|
||||
"6329121e40fe",
|
||||
"e2881f748a46",
|
||||
"d9c464d0dc78",
|
||||
"91e7f4ef9a77"
|
||||
@@ -619,7 +671,7 @@
|
||||
{
|
||||
"id": "data-present",
|
||||
"observation": {
|
||||
"sender": ["1006746b8523", "299d571abf13", "18762ff064ed", "5248b37ec085"],
|
||||
"sender": ["4256aff920c3", "23e43ee36880", "2c7d0be09df6", "63eb42c118e7"],
|
||||
"payloads": ["f2274efe8aa6", "c1246f5792ee", "8b618db22116", "c1d6fc758acc"],
|
||||
"settlements": {
|
||||
"mount": "eb79a9b3682a",
|
||||
@@ -627,8 +679,8 @@
|
||||
},
|
||||
"state": "6d43117882f3",
|
||||
"effects": [
|
||||
"661b4721aec6",
|
||||
"e49da3e1abca",
|
||||
"cd9f1e701ac7",
|
||||
"6329121e40fe",
|
||||
"e2881f748a46",
|
||||
"d9c464d0dc78",
|
||||
"91e7f4ef9a77"
|
||||
@@ -639,18 +691,24 @@
|
||||
"id": "refresh-pending",
|
||||
"observation": {
|
||||
"sender": [
|
||||
"1006746b8523",
|
||||
"299d571abf13",
|
||||
"18762ff064ed",
|
||||
"5248b37ec085",
|
||||
"6fd92b34ee34"
|
||||
"4256aff920c3",
|
||||
"23e43ee36880",
|
||||
"2c7d0be09df6",
|
||||
"63eb42c118e7",
|
||||
"1d5e25c772b7",
|
||||
"4f7ea9e9937e",
|
||||
"adc72cf757d5",
|
||||
"ef8a0c2f003b"
|
||||
],
|
||||
"payloads": [
|
||||
"f2274efe8aa6",
|
||||
"c1246f5792ee",
|
||||
"8b618db22116",
|
||||
"c1d6fc758acc",
|
||||
"1a0e2c3ee678"
|
||||
"c7ebc1837451",
|
||||
"13beaf7a27e8",
|
||||
"edf1e0b1856e",
|
||||
"e930a147c91d"
|
||||
],
|
||||
"settlements": {
|
||||
"mount": "eb79a9b3682a",
|
||||
@@ -659,8 +717,8 @@
|
||||
},
|
||||
"state": "6d43117882f3",
|
||||
"effects": [
|
||||
"661b4721aec6",
|
||||
"e49da3e1abca",
|
||||
"cd9f1e701ac7",
|
||||
"6329121e40fe",
|
||||
"e2881f748a46",
|
||||
"d9c464d0dc78",
|
||||
"91e7f4ef9a77"
|
||||
@@ -671,24 +729,24 @@
|
||||
"id": "refused-after-data",
|
||||
"observation": {
|
||||
"sender": [
|
||||
"1006746b8523",
|
||||
"299d571abf13",
|
||||
"18762ff064ed",
|
||||
"5248b37ec085",
|
||||
"429bb347383c",
|
||||
"bc879379ce13",
|
||||
"eab4940c8ca3",
|
||||
"1e4a3aa71af5"
|
||||
"4256aff920c3",
|
||||
"23e43ee36880",
|
||||
"2c7d0be09df6",
|
||||
"63eb42c118e7",
|
||||
"410308add3d5",
|
||||
"b448db7f57b7",
|
||||
"7f10275fa104",
|
||||
"cf4d44730d0f"
|
||||
],
|
||||
"payloads": [
|
||||
"f2274efe8aa6",
|
||||
"c1246f5792ee",
|
||||
"8b618db22116",
|
||||
"c1d6fc758acc",
|
||||
"1a0e2c3ee678",
|
||||
"af3366c70550",
|
||||
"69f575e15e15",
|
||||
"9b915e5b8831"
|
||||
"c7ebc1837451",
|
||||
"13beaf7a27e8",
|
||||
"edf1e0b1856e",
|
||||
"e930a147c91d"
|
||||
],
|
||||
"settlements": {
|
||||
"mount": "eb79a9b3682a",
|
||||
@@ -697,16 +755,14 @@
|
||||
},
|
||||
"state": "6d43117882f3",
|
||||
"effects": [
|
||||
"661b4721aec6",
|
||||
"e49da3e1abca",
|
||||
"cd9f1e701ac7",
|
||||
"6329121e40fe",
|
||||
"e2881f748a46",
|
||||
"d9c464d0dc78",
|
||||
"91e7f4ef9a77",
|
||||
"306fcf2b8536",
|
||||
"bd0d18cd2160",
|
||||
"e7d590a1a17e",
|
||||
"a465a07eb4b0",
|
||||
"00d007a90b2c"
|
||||
"5c18f152fb65",
|
||||
"555823fff23f",
|
||||
"02e77ef2be85"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,98 @@
|
||||
"family": "settings.repo-metadata",
|
||||
"namedDeltas": [],
|
||||
"values": {
|
||||
"1006746b8523": {
|
||||
"1681ec001eef": {
|
||||
"name": "host.platform#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "host.platform"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"23e43ee36880": {
|
||||
"name": "ssh.listTargetSummaries#1",
|
||||
"ordinal": 3,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "ssh.listTargetSummaries"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-2",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"targets": [
|
||||
{
|
||||
"id": "ssh-1",
|
||||
"label": "SSH"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"36f133fb62a2": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 4,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"4256aff920c3": {
|
||||
"name": "repo.list#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
@@ -23,7 +114,7 @@
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -53,82 +144,9 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"299d571abf13": {
|
||||
"name": "ssh.listTargetSummaries#1",
|
||||
"ordinal": 5,
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"5248b37ec085": {
|
||||
"name": "host.platform#1",
|
||||
"ordinal": 7,
|
||||
"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-4",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"platform": "linux"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"61b27dbdf3a7": {
|
||||
"527f30910a99": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 6,
|
||||
"ordinal": 4,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
@@ -137,13 +155,13 @@
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -161,23 +179,47 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"661b4721aec6": {
|
||||
"name": "desktopRepos",
|
||||
"ordinal": 3,
|
||||
"6329121e40fe": {
|
||||
"name": "repoHostIdByRepoId",
|
||||
"ordinal": 7,
|
||||
"value": [
|
||||
["repo-1", "local"],
|
||||
["repo-2", "ssh:ssh-1"]
|
||||
]
|
||||
},
|
||||
"63eb42c118e7": {
|
||||
"name": "host.platform#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"displayName": "Local",
|
||||
"executionHostId": "local",
|
||||
"id": "repo-1",
|
||||
"path": "repo-1"
|
||||
"name": "method",
|
||||
"value": "host.platform"
|
||||
},
|
||||
{
|
||||
"displayName": "Remote",
|
||||
"executionHostId": "ssh:ssh-1",
|
||||
"id": "repo-2",
|
||||
"path": "repo-2"
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-4",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"platform": "linux"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"68916f125c3c": {
|
||||
"desktopRepos": [
|
||||
@@ -222,72 +264,16 @@
|
||||
["repo-2", "ssh:ssh-1"]
|
||||
]
|
||||
},
|
||||
"7c508088602c": {
|
||||
"name": "host.platform#1",
|
||||
"ordinal": 7,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "host.platform"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"8b618db22116": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 9,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
|
||||
},
|
||||
"902c0b2ed3ce": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 6,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"91e7f4ef9a77": {
|
||||
"name": "hostPlatform",
|
||||
"ordinal": 13,
|
||||
"value": "linux"
|
||||
},
|
||||
"9270aeb7d9c6": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
},
|
||||
"c1246f5792ee": {
|
||||
"name": "ssh.listTargetSummaries#1",
|
||||
"ordinal": 8,
|
||||
@@ -298,6 +284,24 @@
|
||||
"ordinal": 10,
|
||||
"json": "{\"id\":\"frame-4\",\"deviceToken\":\"recording-device\",\"method\":\"host.platform\"}"
|
||||
},
|
||||
"cd9f1e701ac7": {
|
||||
"name": "desktopRepos",
|
||||
"ordinal": 6,
|
||||
"value": [
|
||||
{
|
||||
"displayName": "Local",
|
||||
"executionHostId": "local",
|
||||
"id": "repo-1",
|
||||
"path": "repo-1"
|
||||
},
|
||||
{
|
||||
"displayName": "Remote",
|
||||
"executionHostId": "ssh:ssh-1",
|
||||
"id": "repo-2",
|
||||
"path": "repo-2"
|
||||
}
|
||||
]
|
||||
},
|
||||
"d9c464d0dc78": {
|
||||
"name": "hostLabelById",
|
||||
"ordinal": 12,
|
||||
@@ -308,14 +312,6 @@
|
||||
"ordinal": 11,
|
||||
"value": []
|
||||
},
|
||||
"e49da3e1abca": {
|
||||
"name": "repoHostIdByRepoId",
|
||||
"ordinal": 4,
|
||||
"value": [
|
||||
["repo-1", "local"],
|
||||
["repo-2", "ssh:ssh-1"]
|
||||
]
|
||||
},
|
||||
"eb79a9b3682a": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
@@ -336,20 +332,20 @@
|
||||
{
|
||||
"id": "settings-pending",
|
||||
"observation": {
|
||||
"sender": ["1006746b8523", "299d571abf13", "902c0b2ed3ce", "7c508088602c"],
|
||||
"sender": ["4256aff920c3", "23e43ee36880", "36f133fb62a2", "1681ec001eef"],
|
||||
"payloads": ["f2274efe8aa6", "c1246f5792ee", "8b618db22116", "c1d6fc758acc"],
|
||||
"settlements": {
|
||||
"mount": "eb79a9b3682a",
|
||||
"load": "9270aeb7d9c6"
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "68916f125c3c",
|
||||
"effects": ["661b4721aec6", "e49da3e1abca"]
|
||||
"effects": ["cd9f1e701ac7", "6329121e40fe"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settled",
|
||||
"observation": {
|
||||
"sender": ["1006746b8523", "299d571abf13", "61b27dbdf3a7", "5248b37ec085"],
|
||||
"sender": ["4256aff920c3", "23e43ee36880", "527f30910a99", "63eb42c118e7"],
|
||||
"payloads": ["f2274efe8aa6", "c1246f5792ee", "8b618db22116", "c1d6fc758acc"],
|
||||
"settlements": {
|
||||
"mount": "eb79a9b3682a",
|
||||
@@ -357,8 +353,8 @@
|
||||
},
|
||||
"state": "6d43117882f3",
|
||||
"effects": [
|
||||
"661b4721aec6",
|
||||
"e49da3e1abca",
|
||||
"cd9f1e701ac7",
|
||||
"6329121e40fe",
|
||||
"e2881f748a46",
|
||||
"d9c464d0dc78",
|
||||
"91e7f4ef9a77"
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
"family": "settings.repo-metadata",
|
||||
"namedDeltas": [],
|
||||
"values": {
|
||||
"1e027d0127f3": {
|
||||
"1681ec001eef": {
|
||||
"name": "host.platform#1",
|
||||
"ordinal": 7,
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
@@ -15,116 +15,13 @@
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "rejected",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"error": {
|
||||
"category": "Error",
|
||||
"message": "Connection closed",
|
||||
"isRpcDeliveryUnknown": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"1f909a505b54": {
|
||||
"name": "hostPlatform",
|
||||
"ordinal": 13,
|
||||
"value": {
|
||||
"$rpc": "null"
|
||||
}
|
||||
},
|
||||
"2207323d0bd5": {
|
||||
"name": "ssh.listTargetSummaries#1",
|
||||
"ordinal": 5,
|
||||
"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
|
||||
}
|
||||
}
|
||||
},
|
||||
"302f82cb9a68": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 6,
|
||||
"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
|
||||
}
|
||||
}
|
||||
},
|
||||
"35960f0ff1b9": {
|
||||
"name": "ssh.listTargetSummaries#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "ssh.listTargetSummaries"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -133,63 +30,17 @@
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"379c0eb6dc14": {
|
||||
"name": "hostLabelById",
|
||||
"ordinal": 12,
|
||||
"value": []
|
||||
},
|
||||
"6e0383a0869e": {
|
||||
"name": "desktopRepos",
|
||||
"ordinal": 3,
|
||||
"1de2f04dfab9": {
|
||||
"name": "repoHostIdByRepoId",
|
||||
"ordinal": 7,
|
||||
"value": [
|
||||
{
|
||||
"displayName": "Remote",
|
||||
"executionHostId": "ssh:ssh-1",
|
||||
"id": "repo-1",
|
||||
"path": "repo-1"
|
||||
},
|
||||
{
|
||||
"displayName": "Remote folder",
|
||||
"executionHostId": "ssh:ssh-1",
|
||||
"id": "repo-2",
|
||||
"path": "repo-2"
|
||||
}
|
||||
["repo-1", "ssh:ssh-1"],
|
||||
["repo-2", "ssh:ssh-1"]
|
||||
]
|
||||
},
|
||||
"7c508088602c": {
|
||||
"name": "host.platform#1",
|
||||
"ordinal": 7,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "host.platform"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"8b618db22116": {
|
||||
"36f133fb62a2": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 9,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
|
||||
},
|
||||
"902c0b2ed3ce": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 6,
|
||||
"ordinal": 4,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
@@ -198,13 +49,13 @@
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -213,21 +64,33 @@
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"9270aeb7d9c6": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
},
|
||||
"c1246f5792ee": {
|
||||
"491fca75475a": {
|
||||
"name": "ssh.listTargetSummaries#1",
|
||||
"ordinal": 8,
|
||||
"json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"ssh.listTargetSummaries\"}"
|
||||
"ordinal": 3,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "ssh.listTargetSummaries"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"c1d6fc758acc": {
|
||||
"name": "host.platform#1",
|
||||
"ordinal": 10,
|
||||
"json": "{\"id\":\"frame-4\",\"deviceToken\":\"recording-device\",\"method\":\"host.platform\"}"
|
||||
},
|
||||
"c3c6784789e2": {
|
||||
"864673b0a9cf": {
|
||||
"name": "repo.list#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
@@ -246,7 +109,7 @@
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -274,6 +137,39 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"8b618db22116": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 9,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
|
||||
},
|
||||
"9fedfbd941ff": {
|
||||
"name": "desktopRepos",
|
||||
"ordinal": 6,
|
||||
"value": [
|
||||
{
|
||||
"displayName": "Remote",
|
||||
"executionHostId": "ssh:ssh-1",
|
||||
"id": "repo-1",
|
||||
"path": "repo-1"
|
||||
},
|
||||
{
|
||||
"displayName": "Remote folder",
|
||||
"executionHostId": "ssh:ssh-1",
|
||||
"id": "repo-2",
|
||||
"path": "repo-2"
|
||||
}
|
||||
]
|
||||
},
|
||||
"c1246f5792ee": {
|
||||
"name": "ssh.listTargetSummaries#1",
|
||||
"ordinal": 8,
|
||||
"json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"ssh.listTargetSummaries\"}"
|
||||
},
|
||||
"c1d6fc758acc": {
|
||||
"name": "host.platform#1",
|
||||
"ordinal": 10,
|
||||
"json": "{\"id\":\"frame-4\",\"deviceToken\":\"recording-device\",\"method\":\"host.platform\"}"
|
||||
},
|
||||
"d23c5c4cde9a": {
|
||||
"desktopRepos": [
|
||||
{
|
||||
@@ -294,19 +190,6 @@
|
||||
["repo-2", "ssh:ssh-1"]
|
||||
]
|
||||
},
|
||||
"e2881f748a46": {
|
||||
"name": "hostHealthById",
|
||||
"ordinal": 11,
|
||||
"value": []
|
||||
},
|
||||
"e7dd8e429107": {
|
||||
"name": "repoHostIdByRepoId",
|
||||
"ordinal": 4,
|
||||
"value": [
|
||||
["repo-1", "ssh:ssh-1"],
|
||||
["repo-2", "ssh:ssh-1"]
|
||||
]
|
||||
},
|
||||
"eb79a9b3682a": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
@@ -327,33 +210,14 @@
|
||||
{
|
||||
"id": "single-host-without-label-lookups",
|
||||
"observation": {
|
||||
"sender": ["c3c6784789e2", "35960f0ff1b9", "902c0b2ed3ce", "7c508088602c"],
|
||||
"payloads": ["f2274efe8aa6", "c1246f5792ee", "8b618db22116", "c1d6fc758acc"],
|
||||
"settlements": {
|
||||
"mount": "eb79a9b3682a",
|
||||
"load": "9270aeb7d9c6"
|
||||
},
|
||||
"state": "d23c5c4cde9a",
|
||||
"effects": ["6e0383a0869e", "e7dd8e429107"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "cleanup",
|
||||
"observation": {
|
||||
"sender": ["c3c6784789e2", "2207323d0bd5", "302f82cb9a68", "1e027d0127f3"],
|
||||
"sender": ["864673b0a9cf", "491fca75475a", "36f133fb62a2", "1681ec001eef"],
|
||||
"payloads": ["f2274efe8aa6", "c1246f5792ee", "8b618db22116", "c1d6fc758acc"],
|
||||
"settlements": {
|
||||
"mount": "eb79a9b3682a",
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "d23c5c4cde9a",
|
||||
"effects": [
|
||||
"6e0383a0869e",
|
||||
"e7dd8e429107",
|
||||
"e2881f748a46",
|
||||
"379c0eb6dc14",
|
||||
"1f909a505b54"
|
||||
]
|
||||
"effects": ["9fedfbd941ff", "1de2f04dfab9"]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -4,7 +4,130 @@
|
||||
"family": "settings.repo-metadata",
|
||||
"namedDeltas": [],
|
||||
"values": {
|
||||
"1006746b8523": {
|
||||
"1681ec001eef": {
|
||||
"name": "host.platform#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "host.platform"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"23e43ee36880": {
|
||||
"name": "ssh.listTargetSummaries#1",
|
||||
"ordinal": 3,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "ssh.listTargetSummaries"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-2",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"targets": [
|
||||
{
|
||||
"id": "ssh-1",
|
||||
"label": "SSH"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"36f133fb62a2": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 4,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"383152061aaf": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 4,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "rejected",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"error": {
|
||||
"category": "Error",
|
||||
"message": "settings disconnected",
|
||||
"isRpcDeliveryUnknown": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"4256aff920c3": {
|
||||
"name": "repo.list#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
@@ -23,7 +146,7 @@
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -53,48 +176,17 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"299d571abf13": {
|
||||
"name": "ssh.listTargetSummaries#1",
|
||||
"ordinal": 5,
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"5248b37ec085": {
|
||||
"name": "host.platform#1",
|
||||
"6329121e40fe": {
|
||||
"name": "repoHostIdByRepoId",
|
||||
"ordinal": 7,
|
||||
"value": [
|
||||
["repo-1", "local"],
|
||||
["repo-2", "ssh:ssh-1"]
|
||||
]
|
||||
},
|
||||
"63eb42c118e7": {
|
||||
"name": "host.platform#1",
|
||||
"ordinal": 5,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
@@ -103,13 +195,13 @@
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
"failWhenDisconnected": true
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -126,24 +218,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"661b4721aec6": {
|
||||
"name": "desktopRepos",
|
||||
"ordinal": 3,
|
||||
"value": [
|
||||
{
|
||||
"displayName": "Local",
|
||||
"executionHostId": "local",
|
||||
"id": "repo-1",
|
||||
"path": "repo-1"
|
||||
},
|
||||
{
|
||||
"displayName": "Remote",
|
||||
"executionHostId": "ssh:ssh-1",
|
||||
"id": "repo-2",
|
||||
"path": "repo-2"
|
||||
}
|
||||
]
|
||||
},
|
||||
"68916f125c3c": {
|
||||
"desktopRepos": [
|
||||
{
|
||||
@@ -187,72 +261,16 @@
|
||||
["repo-2", "ssh:ssh-1"]
|
||||
]
|
||||
},
|
||||
"7c508088602c": {
|
||||
"name": "host.platform#1",
|
||||
"ordinal": 7,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "host.platform"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"8b618db22116": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 9,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
|
||||
},
|
||||
"902c0b2ed3ce": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 6,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"91e7f4ef9a77": {
|
||||
"name": "hostPlatform",
|
||||
"ordinal": 13,
|
||||
"value": "linux"
|
||||
},
|
||||
"9270aeb7d9c6": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
},
|
||||
"c1246f5792ee": {
|
||||
"name": "ssh.listTargetSummaries#1",
|
||||
"ordinal": 8,
|
||||
@@ -263,37 +281,23 @@
|
||||
"ordinal": 10,
|
||||
"json": "{\"id\":\"frame-4\",\"deviceToken\":\"recording-device\",\"method\":\"host.platform\"}"
|
||||
},
|
||||
"ce32fe5b8377": {
|
||||
"name": "settings.get#1",
|
||||
"cd9f1e701ac7": {
|
||||
"name": "desktopRepos",
|
||||
"ordinal": 6,
|
||||
"args": [
|
||||
"value": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
"displayName": "Local",
|
||||
"executionHostId": "local",
|
||||
"id": "repo-1",
|
||||
"path": "repo-1"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
"displayName": "Remote",
|
||||
"executionHostId": "ssh:ssh-1",
|
||||
"id": "repo-2",
|
||||
"path": "repo-2"
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "rejected",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"error": {
|
||||
"category": "Error",
|
||||
"message": "settings disconnected",
|
||||
"isRpcDeliveryUnknown": true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"d9c464d0dc78": {
|
||||
"name": "hostLabelById",
|
||||
@@ -305,14 +309,6 @@
|
||||
"ordinal": 11,
|
||||
"value": []
|
||||
},
|
||||
"e49da3e1abca": {
|
||||
"name": "repoHostIdByRepoId",
|
||||
"ordinal": 4,
|
||||
"value": [
|
||||
["repo-1", "local"],
|
||||
["repo-2", "ssh:ssh-1"]
|
||||
]
|
||||
},
|
||||
"eb79a9b3682a": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
@@ -333,20 +329,20 @@
|
||||
{
|
||||
"id": "settings-pending",
|
||||
"observation": {
|
||||
"sender": ["1006746b8523", "299d571abf13", "902c0b2ed3ce", "7c508088602c"],
|
||||
"sender": ["4256aff920c3", "23e43ee36880", "36f133fb62a2", "1681ec001eef"],
|
||||
"payloads": ["f2274efe8aa6", "c1246f5792ee", "8b618db22116", "c1d6fc758acc"],
|
||||
"settlements": {
|
||||
"mount": "eb79a9b3682a",
|
||||
"load": "9270aeb7d9c6"
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "68916f125c3c",
|
||||
"effects": ["661b4721aec6", "e49da3e1abca"]
|
||||
"effects": ["cd9f1e701ac7", "6329121e40fe"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settled",
|
||||
"observation": {
|
||||
"sender": ["1006746b8523", "299d571abf13", "ce32fe5b8377", "5248b37ec085"],
|
||||
"sender": ["4256aff920c3", "23e43ee36880", "383152061aaf", "63eb42c118e7"],
|
||||
"payloads": ["f2274efe8aa6", "c1246f5792ee", "8b618db22116", "c1d6fc758acc"],
|
||||
"settlements": {
|
||||
"mount": "eb79a9b3682a",
|
||||
@@ -354,8 +350,8 @@
|
||||
},
|
||||
"state": "6d43117882f3",
|
||||
"effects": [
|
||||
"661b4721aec6",
|
||||
"e49da3e1abca",
|
||||
"cd9f1e701ac7",
|
||||
"6329121e40fe",
|
||||
"e2881f748a46",
|
||||
"d9c464d0dc78",
|
||||
"91e7f4ef9a77"
|
||||
|
||||
Vendored
-26
@@ -1,26 +0,0 @@
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { getCachedRepos, setCachedRepos } from './repo-cache'
|
||||
|
||||
describe('repo cache', () => {
|
||||
it('returns recent host-scoped repos', () => {
|
||||
const repos = [{ id: 'repo-1' }]
|
||||
|
||||
setCachedRepos('host-1', repos)
|
||||
|
||||
expect(getCachedRepos('host-1')).toBe(repos)
|
||||
expect(getCachedRepos('host-2')).toBeNull()
|
||||
})
|
||||
|
||||
it('expires stale entries', () => {
|
||||
vi.useFakeTimers()
|
||||
try {
|
||||
setCachedRepos('host-stale', [{ id: 'repo-stale' }])
|
||||
vi.advanceTimersByTime(60_001)
|
||||
|
||||
expect(getCachedRepos('host-stale')).toBeNull()
|
||||
} finally {
|
||||
vi.useRealTimers()
|
||||
}
|
||||
})
|
||||
})
|
||||
Vendored
-34
@@ -1,34 +0,0 @@
|
||||
// Cache decorative repo metadata per host so create-workspace opens immediately while repo.list refreshes.
|
||||
|
||||
type CachedRepos = {
|
||||
repos: unknown[]
|
||||
at: number
|
||||
}
|
||||
|
||||
const cache = new Map<string, CachedRepos>()
|
||||
|
||||
const MAX_AGE_MS = 60_000
|
||||
const MAX_ENTRIES = 20
|
||||
|
||||
export function setCachedRepos(hostId: string, repos: unknown[]): void {
|
||||
cache.delete(hostId)
|
||||
cache.set(hostId, { repos, at: Date.now() })
|
||||
if (cache.size > MAX_ENTRIES) {
|
||||
const oldest = cache.keys().next().value
|
||||
if (oldest) {
|
||||
cache.delete(oldest)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getCachedRepos(hostId: string): unknown[] | null {
|
||||
const entry = cache.get(hostId)
|
||||
if (!entry) {
|
||||
return null
|
||||
}
|
||||
if (Date.now() - entry.at > MAX_AGE_MS) {
|
||||
cache.delete(hostId)
|
||||
return null
|
||||
}
|
||||
return entry.repos
|
||||
}
|
||||
@@ -53,7 +53,15 @@ vi.mock('./MobileSwitch', () => ({ MobileSwitch: 'MobileSwitch' }))
|
||||
vi.mock('./MobileAgentIcon', () => ({ MobileAgentIcon: 'MobileAgentIcon' }))
|
||||
vi.mock('./TaskProviderLogo', () => ({ TaskProviderLogo: 'TaskProviderLogo' }))
|
||||
|
||||
import { setCachedRepos } from '../cache/repo-cache'
|
||||
// The form seeds from the host list's repo.list rows in this document's query cache.
|
||||
const cachedRepos = new Map<string, unknown[]>()
|
||||
const setCachedRepos = (hostId: string, rows: unknown[]) => cachedRepos.set(hostId, rows)
|
||||
vi.mock('@tanstack/react-query', () => ({ useQueryClient: () => null }))
|
||||
vi.mock('../host-screen/host-list-queries', () => ({
|
||||
cachedHostRepos: (_queryClient: unknown, hostId: string | undefined) =>
|
||||
(hostId && cachedRepos.get(hostId)) || null
|
||||
}))
|
||||
|
||||
import { recordHostDescriptor } from '../transport/host-descriptor-store'
|
||||
import { FakeSession } from '../transport/mobile-endpoint-supervisor-test-fakes'
|
||||
import {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import type { ExecutionHostId } from '../../../src/shared/execution-host'
|
||||
import type { RpcClient } from '../transport/rpc-client'
|
||||
import { getCachedRepos, setCachedRepos } from '../cache/repo-cache'
|
||||
import { cachedHostRepos } from '../host-screen/host-list-queries'
|
||||
import { listHostRepos } from '../worktree/server-repos'
|
||||
import { useServerRepos } from '../worktree/use-server-repos'
|
||||
import { useLastVisitedWorktreeRepoId } from '../worktree/use-last-visited-worktree-repo'
|
||||
@@ -31,9 +32,9 @@ export function useNewWorkspaceRepositories(args: {
|
||||
retryLoad: () => void
|
||||
} {
|
||||
const { client, serverClients = NO_SERVER_CLIENTS, hostId, visible } = args
|
||||
const [initialRepos] = useState(() =>
|
||||
hostId ? (getCachedRepos(hostId) as MobileWorkspaceRepo[] | null) : null
|
||||
)
|
||||
const queryClient = useQueryClient()
|
||||
// The host list's repos, so the form opens with them while repo.list answers.
|
||||
const [initialRepos] = useState(() => cachedHostRepos<MobileWorkspaceRepo>(queryClient, hostId))
|
||||
const [desktopRepos, setDesktopRepos] = useState<MobileWorkspaceRepo[]>(initialRepos ?? [])
|
||||
const serverRepos = useServerRepos<MobileWorkspaceRepo>(serverClients, visible)
|
||||
const [pickedRepo, setSelectedRepo] = useState<MobileWorkspaceRepo | null>(null)
|
||||
@@ -88,9 +89,6 @@ export function useNewWorkspaceRepositories(args: {
|
||||
return
|
||||
}
|
||||
setDesktopRepos(listed)
|
||||
if (hostId) {
|
||||
setCachedRepos(hostId, listed)
|
||||
}
|
||||
})
|
||||
.catch((err: unknown) => {
|
||||
if (!stale) {
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
import { getRepoExecutionHostId, parseExecutionHostId } from '../../../src/shared/execution-host'
|
||||
import type { Query, QueryClient } from '@tanstack/react-query'
|
||||
import { defineHostQuery } from '../query/host-query'
|
||||
import { hostQueryKey, type HostQueryTarget } from '../query/host-query-key'
|
||||
import type { RpcClient } from '../transport/rpc-client'
|
||||
import {
|
||||
applyServerWorkspaces,
|
||||
fetchServerWorkspaces,
|
||||
NO_SERVER_WORKSPACES,
|
||||
ServerRowSnapshots,
|
||||
type ServerWorkspaces
|
||||
} from '../worktree/server-workspaces'
|
||||
import {
|
||||
readWorktreeCatalogSnapshot,
|
||||
type WorktreeCatalogSnapshot
|
||||
} from '../worktree/worktree-catalog-snapshot-client'
|
||||
import type { Worktree } from '../worktree/workspace-list-types'
|
||||
import { getCachedWorktrees } from '../cache/worktree-cache'
|
||||
import type { HostRepoCatalogRow } from './host-screen-reply-schema'
|
||||
import { hostPlatformRead, hostRepoCatalogSnapshotRead } from './host-screen-operations'
|
||||
import {
|
||||
acceptedMetadata,
|
||||
readHostSshTargetLabels,
|
||||
settledMetadataReply
|
||||
} from './host-ssh-target-labels-read'
|
||||
|
||||
// The host-wide lists: one per paired host, read on the desktop itself.
|
||||
export const HOST_LIST_TARGET: HostQueryTarget = { exec: 'local' }
|
||||
export const NO_HOST_LIST_INPUT = {}
|
||||
|
||||
// Agent rows change with no event of their own, so a visible list also polls (host-worktree-catalog).
|
||||
export const HOST_LIST_POLL_MS = 3000
|
||||
|
||||
export const hostWorktreeCatalogQuery = defineHostQuery<
|
||||
typeof NO_HOST_LIST_INPUT,
|
||||
WorktreeCatalogSnapshot
|
||||
>({
|
||||
family: 'worktree.ps',
|
||||
fetch: (client, _target, _input, previous) => readWorktreeCatalogSnapshot(client, previous),
|
||||
staleTime: HOST_LIST_POLL_MS,
|
||||
gcTime: 30 * 60_000,
|
||||
// A hidden page reads nothing on change events; coming back reads what it missed.
|
||||
refetchOnWindowFocus: true,
|
||||
// Folder workspace changes publish reposChanged, and worktree.ps carries their rows.
|
||||
signal: { events: ['worktreesChanged', 'reposChanged'], floorMs: HOST_LIST_POLL_MS }
|
||||
})
|
||||
|
||||
export const hostWorktreeCatalogKey = (hostId: string) =>
|
||||
hostQueryKey(hostId, hostWorktreeCatalogQuery.family, HOST_LIST_TARGET, NO_HOST_LIST_INPUT)
|
||||
|
||||
/** The host's catalog as this JS world last read it, or null; a copy, since screens may sort it in place. */
|
||||
export function cachedHostWorktrees(
|
||||
queryClient: QueryClient,
|
||||
hostId: string | undefined
|
||||
): Worktree[] | null {
|
||||
if (!hostId) {
|
||||
return null
|
||||
}
|
||||
const cached = queryClient.getQueryData<WorktreeCatalogSnapshot>(hostWorktreeCatalogKey(hostId))
|
||||
// Native Home's own catalog read until Home moves onto this query; shown only, never a delta base.
|
||||
return cached ? [...cached.worktrees] : (getCachedWorktrees(hostId) as Worktree[] | null)
|
||||
}
|
||||
|
||||
const serverRowSnapshots = {
|
||||
deltas: new ServerRowSnapshots(true),
|
||||
full: new ServerRowSnapshots(false)
|
||||
}
|
||||
|
||||
// Workspaces on the servers this desktop relays to; a server whose read fails keeps its rows.
|
||||
export const hostServerWorkspacesQuery = defineHostQuery<{ deltas: boolean }, ServerWorkspaces>({
|
||||
family: 'relayHosts.workspaces',
|
||||
fetch: async (client, _target, input, previous) => {
|
||||
const snapshots = input.deltas ? serverRowSnapshots.deltas : serverRowSnapshots.full
|
||||
const fetched = await fetchServerWorkspaces(client, snapshots)
|
||||
const held = previous ?? NO_SERVER_WORKSPACES
|
||||
return fetched ? applyServerWorkspaces(held, fetched) : held
|
||||
},
|
||||
staleTime: HOST_LIST_POLL_MS,
|
||||
gcTime: 30 * 60_000,
|
||||
refetchOnWindowFocus: true,
|
||||
signal: { events: ['worktreesChanged', 'reposChanged'], floorMs: HOST_LIST_POLL_MS }
|
||||
})
|
||||
|
||||
export type HostRepoCatalog = { snapshotId: string | null; catalog: HostRepoCatalogRow[] }
|
||||
|
||||
async function readHostRepoCatalog(
|
||||
client: RpcClient,
|
||||
previous: HostRepoCatalog | undefined
|
||||
): Promise<HostRepoCatalog> {
|
||||
const held = previous?.snapshotId ? previous : undefined
|
||||
const reply = await settledMetadataReply(() =>
|
||||
hostRepoCatalogSnapshotRead.request(client, { afterSnapshotId: held?.snapshotId ?? null })
|
||||
)
|
||||
const repos = reply && hostRepoCatalogSnapshotRead.interpret(reply)
|
||||
if (!repos?.accepted) {
|
||||
throw new Error('repo.list unavailable')
|
||||
}
|
||||
const { value } = repos
|
||||
if (value.kind === 'catalog') {
|
||||
return { snapshotId: value.snapshotId, catalog: value.repos }
|
||||
}
|
||||
// An unchanged reply for a token the cache no longer holds asks again in full.
|
||||
return value.snapshotId === held?.snapshotId ? held : readHostRepoCatalog(client, undefined)
|
||||
}
|
||||
|
||||
export const hostRepoCatalogQuery = defineHostQuery<typeof NO_HOST_LIST_INPUT, HostRepoCatalog>({
|
||||
family: 'repo.list',
|
||||
fetch: (client, _target, _input, previous) => readHostRepoCatalog(client, previous),
|
||||
// Older hosts emit no reposChanged, so a visible list still re-reads once a minute.
|
||||
staleTime: 60_000,
|
||||
gcTime: 30 * 60_000,
|
||||
refetchOnWindowFocus: true,
|
||||
signal: { events: ['reposChanged'] }
|
||||
})
|
||||
|
||||
/** Host badges are read only for mixed-host or SSH catalogs. */
|
||||
export function catalogNeedsHostLabels(catalog: readonly HostRepoCatalogRow[]): boolean {
|
||||
// Count repos, not an id-keyed map: the same repo id may exist on two hosts.
|
||||
const hostIds = new Set(catalog.map((repo) => getRepoExecutionHostId(repo)))
|
||||
return hostIds.size > 1 || [...hostIds].some((id) => parseExecutionHostId(id)?.kind === 'ssh')
|
||||
}
|
||||
|
||||
export type HostRepoLabels = Awaited<ReturnType<typeof readHostSshTargetLabels>> & {
|
||||
platform: NodeJS.Platform | null
|
||||
}
|
||||
|
||||
export const hostRepoLabelsQuery = defineHostQuery<typeof NO_HOST_LIST_INPUT, HostRepoLabels>({
|
||||
family: 'repo.hostLabels',
|
||||
fetch: async (client) => {
|
||||
const [labels, platform] = await Promise.all([
|
||||
readHostSshTargetLabels(client),
|
||||
settledMetadataReply(() => hostPlatformRead.request(client))
|
||||
])
|
||||
return { ...labels, platform: acceptedMetadata(platform, hostPlatformRead.interpret) ?? null }
|
||||
},
|
||||
staleTime: 60_000,
|
||||
gcTime: 30 * 60_000,
|
||||
refetchOnWindowFocus: true,
|
||||
// SSH health and the host's own label overrides.
|
||||
signal: { events: ['reposChanged', 'sshStateChanged'] }
|
||||
})
|
||||
|
||||
// The host list's own reads, which pull to refresh repeats; project.sections lives with its hook.
|
||||
const HOST_LIST_FAMILIES: readonly unknown[] = [
|
||||
hostWorktreeCatalogQuery.family,
|
||||
hostServerWorkspacesQuery.family,
|
||||
hostRepoCatalogQuery.family,
|
||||
hostRepoLabelsQuery.family,
|
||||
'project.sections'
|
||||
]
|
||||
|
||||
export const isHostListQuery = (query: Query): boolean =>
|
||||
HOST_LIST_FAMILIES.includes(query.queryKey[2])
|
||||
|
||||
/** The host's repo.list rows as this JS world last read them, or null. */
|
||||
export function cachedHostRepos<Repo>(
|
||||
queryClient: QueryClient,
|
||||
hostId: string | undefined
|
||||
): Repo[] | null {
|
||||
const cached = hostId
|
||||
? queryClient.getQueryData<HostRepoCatalog>(
|
||||
hostQueryKey(hostId, hostRepoCatalogQuery.family, HOST_LIST_TARGET, NO_HOST_LIST_INPUT)
|
||||
)
|
||||
: undefined
|
||||
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: repo.list rows are the host's Repo records, and the form's repo type is a view of their fields, as listHostRepos reads them.
|
||||
return cached ? (cached.catalog as unknown as Repo[]) : null
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import { NavBarButton } from '../components/nav-bar/NavBarButton'
|
||||
import { PullDownMenu } from '../components/nav-bar/PullDownMenu'
|
||||
import { StatusDot } from '../components/StatusDot'
|
||||
import { useMobileTranslation } from '../i18n/use-mobile-translation'
|
||||
import { useRefreshHint } from '../query/refresh-hint'
|
||||
import { colors, pressedStyle } from '../theme/mobile-theme'
|
||||
import { themedStyleSheet } from '../theme/themed-styles'
|
||||
import { useMobileColorScheme } from '../theme/use-mobile-color-scheme'
|
||||
@@ -96,6 +97,7 @@ export function HostScreenHeader({
|
||||
} = controller
|
||||
const moreRef = useRef<View>(null)
|
||||
const [menuOpen, setMenuOpen] = useState(false)
|
||||
const showRefreshLine = useRefreshHint(controller.catalog.revalidating)
|
||||
const disconnected = connState !== 'connected'
|
||||
const hostPath = `/h/${encodeURIComponent(hostId)}`
|
||||
|
||||
@@ -163,6 +165,7 @@ export function HostScreenHeader({
|
||||
}
|
||||
title={hostDisplay.title}
|
||||
subtitle={<HostConnectionStatus controller={controller} />}
|
||||
refreshing={showRefreshLine}
|
||||
titleOpacity={collapsingTitle?.barTitleOpacity}
|
||||
dividerOpacity={collapsingTitle?.barDividerOpacity}
|
||||
trailing={
|
||||
|
||||
@@ -1,24 +1,17 @@
|
||||
import { createElement, useState } from 'react'
|
||||
|
||||
import { normalizeVisibleExecutionHostIds } from '../../../src/shared/execution-host'
|
||||
import { act, create } from 'react-test-renderer'
|
||||
import { documentQueryClient } from '../test-support/rpc-recording/hook-mount'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { QueryClientProvider } from '@tanstack/react-query'
|
||||
import { act, create, type ReactTestRenderer } from 'react-test-renderer'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { z } from 'zod'
|
||||
|
||||
vi.mock('expo-router', () => ({ useFocusEffect: () => {} }))
|
||||
vi.mock('react-native', () => ({ AppState: {} }))
|
||||
vi.mock('../transport/host-removal-lifecycle', () => ({ removeHostAndCloseClient: async () => {} }))
|
||||
vi.mock('../host-route-exit', () => ({ leaveHostRoute: () => {} }))
|
||||
vi.mock('../cache/worktree-cache', () => ({
|
||||
getCachedWorktrees: () => null,
|
||||
setCachedWorktrees: () => {}
|
||||
}))
|
||||
vi.mock('../storage/preferences', () => ({ savePinnedIds: async () => {} }))
|
||||
vi.mock('../worktree/host-worktree-refresh', () => ({ startHostWorktreeRefresh: () => () => {} }))
|
||||
vi.mock('../transport/use-worktree-resync', () => ({
|
||||
useWorktreeResync: () => ({ refreshing: false, onRefresh: async () => {} })
|
||||
}))
|
||||
|
||||
import { MOBILE_DESKTOP_RELAY_RUNTIME_CAPABILITY } from '../../../src/shared/mobile-desktop-relay-contract'
|
||||
import { createBridgePortPair } from '../mobile-web-shell/bridge/bridge-port-pair-test-harness'
|
||||
@@ -35,6 +28,13 @@ import { useHostDisplayWorktrees, useResolvedHostPlatform } from './use-host-dis
|
||||
import { useHostScreenState } from './use-host-screen-state'
|
||||
import { useHostWorktreeActions } from './use-host-worktree-actions'
|
||||
import { useHostWorktreeCatalog } from './use-host-worktree-catalog'
|
||||
import { createOrcaQueryClient } from '../query/orca-query-client'
|
||||
import { hostWorktreeCatalogKey } from './host-list-queries'
|
||||
|
||||
const mounted: ReactTestRenderer[] = []
|
||||
afterEach(() => {
|
||||
act(() => mounted.splice(0).forEach((renderer) => renderer.unmount()))
|
||||
})
|
||||
|
||||
const RELAYS = [MOBILE_DESKTOP_RELAY_RUNTIME_CAPABILITY]
|
||||
|
||||
@@ -107,6 +107,7 @@ type Screen = {
|
||||
poll: () => Promise<void>
|
||||
hideHosts: (ids: string[]) => void
|
||||
open: (worktreeId: string) => void
|
||||
openRow: (row: LabelledRow) => void
|
||||
notice: () => string | null
|
||||
catalogError: () => string | null
|
||||
pin: (worktreeId: string) => void
|
||||
@@ -126,7 +127,7 @@ async function mountScreen(first: RpcClient, hostCapabilities: string[]): Promis
|
||||
rows: Worktree[]
|
||||
hostContextOf: WorktreeHostContextOf
|
||||
state?: ReturnType<typeof useHostScreenState>
|
||||
fetch?: () => Promise<void>
|
||||
refresh?: () => Promise<void>
|
||||
actions?: ReturnType<typeof useHostWorktreeActions>
|
||||
} = { rows: [], hostContextOf: NO_WORKTREE_HOST_CONTEXT }
|
||||
function Probe(): null {
|
||||
@@ -138,7 +139,6 @@ async function mountScreen(first: RpcClient, hostCapabilities: string[]): Promis
|
||||
client,
|
||||
connState: 'connected',
|
||||
embedded: true,
|
||||
fetchRepoMetadata: async () => {},
|
||||
hostCapabilities,
|
||||
hostId: 'host-1',
|
||||
sidebarFeed: null,
|
||||
@@ -159,7 +159,7 @@ async function mountScreen(first: RpcClient, hostCapabilities: string[]): Promis
|
||||
state
|
||||
})
|
||||
held.state = state
|
||||
held.fetch = catalog.fetchWorktrees
|
||||
held.refresh = catalog.onRefresh
|
||||
const display = useHostDisplayWorktrees(
|
||||
'connected',
|
||||
state,
|
||||
@@ -169,13 +169,18 @@ async function mountScreen(first: RpcClient, hostCapabilities: string[]): Promis
|
||||
held.hostContextOf = display.hostContextOf
|
||||
return null
|
||||
}
|
||||
const queryClient = createOrcaQueryClient()
|
||||
await act(async () => {
|
||||
create(documentQueryClient().wrap(createElement(Probe)))
|
||||
mounted.push(
|
||||
create(createElement(QueryClientProvider, { client: queryClient }, createElement(Probe)))
|
||||
)
|
||||
})
|
||||
// Reads the host list again, as pull to refresh does; the catalog's interval does the same.
|
||||
const poll = async (): Promise<void> => {
|
||||
await act(async () => {
|
||||
await held.fetch?.()
|
||||
await vi.waitFor(() => expect(held.state?.fetchWorktreesInFlightRef.current).toBe(false))
|
||||
void held.refresh?.()
|
||||
const catalog = { queryKey: hostWorktreeCatalogKey('host-1') }
|
||||
await vi.waitFor(() => expect(queryClient.isFetching(catalog)).toBe(0))
|
||||
await new Promise((resolve) => setTimeout(resolve, 0))
|
||||
})
|
||||
}
|
||||
@@ -196,6 +201,7 @@ async function mountScreen(first: RpcClient, hostCapabilities: string[]): Promis
|
||||
const item = held.rows.find((entry) => entry.worktreeId === worktreeId)
|
||||
held.actions?.openWorktreeSession(item!)
|
||||
}),
|
||||
openRow: (row) => act(() => held.actions?.openWorktreeSession(row)),
|
||||
notice: () => held.state?.serverNotice ?? null,
|
||||
catalogError: () => held.state?.catalogError ?? null,
|
||||
pin: (worktreeId) =>
|
||||
@@ -297,10 +303,12 @@ describe("a desktop's server workspaces on the phone", () => {
|
||||
|
||||
it('opens nothing for a listed server workspace once the phone can no longer reach it', async () => {
|
||||
const screen = await mountScreen(desktop(), RELAYS)
|
||||
const listedRow = screen.rows().find((entry) => entry.worktreeId === 'runtime:vm-wt')!
|
||||
const oldShell = desktop({ carriesRpcEnvelope: false })
|
||||
await screen.swapClient(oldShell)
|
||||
expect(screen.rows().map((entry) => entry.worktreeId)).toContain('runtime:vm-wt')
|
||||
screen.open('runtime:vm-wt')
|
||||
expect(screen.rows().map((entry) => entry.worktreeId)).not.toContain('runtime:vm-wt')
|
||||
// A tap on the row still drawn from before the swap.
|
||||
screen.openRow(listedRow)
|
||||
expect(screen.navigations).toEqual([])
|
||||
expect(calledMethods(oldShell)).not.toContain('worktree.activate')
|
||||
})
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isHostNotConnectedError } from '../query/host-query-errors'
|
||||
import { optionalSettingsRead } from '../transport/settings-read-operations'
|
||||
import type { RpcAcceptedResult } from '../transport/rpc-accepted-result'
|
||||
import type { RpcClient } from '../transport/rpc-client'
|
||||
@@ -10,7 +11,11 @@ export async function settledMetadataReply(
|
||||
): Promise<RpcResponse | null> {
|
||||
try {
|
||||
return await send()
|
||||
} catch {
|
||||
} catch (error) {
|
||||
// A query read sent while the host is down is not an answer; the connected edge reads it again.
|
||||
if (isHostNotConnectedError(error)) {
|
||||
throw error
|
||||
}
|
||||
// Best-effort: hosts that predate a method still list repos; labels degrade to host ids.
|
||||
return null
|
||||
}
|
||||
@@ -35,7 +40,7 @@ function readHostSettingOverrides(result: unknown): unknown {
|
||||
export async function readHostSshTargetLabels(client: RpcClient) {
|
||||
const [sshTargets, hostSettings] = await Promise.all([
|
||||
settledMetadataReply(() => hostSshTargetSummariesRead.request(client)),
|
||||
optionalSettingsRead.request(client).catch(() => null)
|
||||
settledMetadataReply(() => optionalSettingsRead.request(client))
|
||||
])
|
||||
const hostSettingsResult = hostSettings ? optionalSettingsRead.interpret(hostSettings) : null
|
||||
const sshTargetSummaries =
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { createElement } from 'react'
|
||||
import { createElement, type ReactElement } from 'react'
|
||||
import { QueryClientProvider } from '@tanstack/react-query'
|
||||
import { act, create, type ReactTestRenderer } from 'react-test-renderer'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
@@ -7,20 +8,14 @@ vi.mock('react-native', () => ({
|
||||
AppState: { currentState: 'active', addEventListener: () => ({ remove: () => {} }) }
|
||||
}))
|
||||
vi.mock('expo-router', () => ({ useFocusEffect: () => {} }))
|
||||
vi.mock('../cache/worktree-cache', () => ({
|
||||
getCachedWorktrees: () => null,
|
||||
setCachedWorktrees: () => {}
|
||||
}))
|
||||
vi.mock('../storage/preferences', () => ({ savePinnedIds: async () => {} }))
|
||||
vi.mock('../transport/use-worktree-resync', () => ({
|
||||
useWorktreeResync: () => ({ refreshing: false, onRefresh: async () => {} })
|
||||
}))
|
||||
|
||||
import { FakeSession } from '../transport/mobile-endpoint-supervisor-test-fakes'
|
||||
import type { ConnectionState, RpcResponse } from '../transport/types'
|
||||
import { useHostScreenState } from './use-host-screen-state'
|
||||
import { useHostViewSettings } from './use-host-view-settings'
|
||||
import { useHostWorktreeCatalog } from './use-host-worktree-catalog'
|
||||
import { createOrcaQueryClient } from '../query/orca-query-client'
|
||||
|
||||
function reply(result: unknown): RpcResponse {
|
||||
return { id: 'reply', ok: true, result, _meta: { runtimeId: 'runtime' } }
|
||||
@@ -54,7 +49,6 @@ function desktop() {
|
||||
const SIDEBAR_FEED = { refresh: () => {}, changed: () => {}, onInstalled: () => () => {} }
|
||||
// Keep callbacks stable as in the screen; refresh lifecycle depends on their identities.
|
||||
const NO_CAPABILITIES: string[] = []
|
||||
const fetchRepoMetadata = async (): Promise<void> => {}
|
||||
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
|
||||
@@ -78,7 +72,6 @@ async function mountScreen(client: FakeSession) {
|
||||
client,
|
||||
connState,
|
||||
embedded: true,
|
||||
fetchRepoMetadata,
|
||||
hostCapabilities: NO_CAPABILITIES,
|
||||
hostId: 'host-1',
|
||||
sidebarFeed: SIDEBAR_FEED,
|
||||
@@ -87,15 +80,18 @@ async function mountScreen(client: FakeSession) {
|
||||
})
|
||||
return null
|
||||
}
|
||||
const queryClient = createOrcaQueryClient()
|
||||
const screen = (connState: ConnectionState): ReactElement =>
|
||||
createElement(QueryClientProvider, { client: queryClient }, createElement(Probe, { connState }))
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Probe, { connState: 'connected' }))
|
||||
renderer = create(screen('connected'))
|
||||
})
|
||||
return {
|
||||
view: () => held.view!,
|
||||
state: () => held.state!,
|
||||
connect: async (connState: ConnectionState) => {
|
||||
await act(async () => {
|
||||
renderer!.update(createElement(Probe, { connState }))
|
||||
renderer!.update(screen(connState))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,17 @@
|
||||
import { createElement } from 'react'
|
||||
import { QueryClientProvider } from '@tanstack/react-query'
|
||||
import { act, create } from 'react-test-renderer'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
// The desktop's server-list feed loads react-native; its timer never runs without a subscriber.
|
||||
vi.mock('react-native', () => ({ AppState: {} }))
|
||||
vi.mock('expo-router', () => ({ useFocusEffect: () => {} }))
|
||||
vi.mock('../cache/worktree-cache', () => ({
|
||||
getCachedWorktrees: () => null,
|
||||
setCachedWorktrees: () => {}
|
||||
}))
|
||||
vi.mock('../storage/preferences', () => ({ savePinnedIds: async () => {} }))
|
||||
vi.mock('../worktree/host-worktree-refresh', () => ({ startHostWorktreeRefresh: () => () => {} }))
|
||||
vi.mock('../transport/use-worktree-resync', () => ({
|
||||
useWorktreeResync: () => ({ refreshing: false, onRefresh: async () => {} })
|
||||
}))
|
||||
|
||||
import { useHostWorktreeCatalog } from './use-host-worktree-catalog'
|
||||
import { createOrcaQueryClient } from '../query/orca-query-client'
|
||||
import { settleQueryNotifications } from '../test-support/rpc-recording/hook-mount'
|
||||
|
||||
import { FakeSession } from '../transport/mobile-endpoint-supervisor-test-fakes'
|
||||
import type { RpcResponse } from '../transport/types'
|
||||
@@ -44,7 +40,6 @@ async function fetchWith(fail: boolean) {
|
||||
client,
|
||||
connState: 'connected',
|
||||
embedded: true,
|
||||
fetchRepoMetadata: async () => {},
|
||||
hostCapabilities: [],
|
||||
hostId: 'host-1',
|
||||
sidebarFeed: null,
|
||||
@@ -57,7 +52,13 @@ async function fetchWith(fail: boolean) {
|
||||
let renderer: ReturnType<typeof create> | null = null
|
||||
try {
|
||||
await act(async () => {
|
||||
renderer = create(createElement(Probe))
|
||||
renderer = create(
|
||||
createElement(
|
||||
QueryClientProvider,
|
||||
{ client: createOrcaQueryClient() },
|
||||
createElement(Probe)
|
||||
)
|
||||
)
|
||||
})
|
||||
const get = (): NonNullable<typeof current> => {
|
||||
if (current === null) {
|
||||
@@ -65,9 +66,16 @@ async function fetchWith(fail: boolean) {
|
||||
}
|
||||
return current
|
||||
}
|
||||
// The mount's own read settles first; the failure is from an action after it.
|
||||
await settleQueryNotifications()
|
||||
act(() => get().state.setActionError('Could not delete workspace.'))
|
||||
await act(async () => get().fetch())
|
||||
expect(client.sendRequest).toHaveBeenCalledWith('worktree.ps', expect.anything())
|
||||
await settleQueryNotifications()
|
||||
expect(client.sendRequest).toHaveBeenCalledWith(
|
||||
'worktree.ps',
|
||||
expect.anything(),
|
||||
expect.anything()
|
||||
)
|
||||
return {
|
||||
actionError: get().state.actionError,
|
||||
catalogError: get().state.catalogError,
|
||||
|
||||
@@ -1,155 +0,0 @@
|
||||
import { createElement } from 'react'
|
||||
import { act, create as createRenderer } from 'react-test-renderer'
|
||||
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import type { RpcClient } from '../transport/rpc-client'
|
||||
import { useHostProjectSections } from './use-host-project-sections'
|
||||
|
||||
import { FakeSession } from '../transport/mobile-endpoint-supervisor-test-fakes'
|
||||
import type { RpcResponse } from '../transport/types'
|
||||
|
||||
const renderers = new Set<ReturnType<typeof createRenderer>>()
|
||||
function create(...args: Parameters<typeof createRenderer>): ReturnType<typeof createRenderer> {
|
||||
const renderer = createRenderer(...args)
|
||||
renderers.add(renderer)
|
||||
return renderer
|
||||
}
|
||||
function unmountRenderers(): void {
|
||||
act(() => {
|
||||
for (const renderer of renderers) {
|
||||
renderer.unmount()
|
||||
}
|
||||
})
|
||||
renderers.clear()
|
||||
}
|
||||
|
||||
const catalog = (name: string) => ({
|
||||
repos: [],
|
||||
projects: [{ id: name, displayName: name }],
|
||||
setups: [],
|
||||
projectOrderBy: 'manual',
|
||||
projectGroups: []
|
||||
})
|
||||
const reply = (name: string): RpcResponse => ({
|
||||
id: 'r',
|
||||
ok: true,
|
||||
result: { catalog: catalog(name) },
|
||||
_meta: { runtimeId: 'runtime' }
|
||||
})
|
||||
afterEach(() => {
|
||||
unmountRenderers()
|
||||
vi.useRealTimers()
|
||||
})
|
||||
|
||||
describe('useHostProjectSections', () => {
|
||||
it('fetches immediately on another host client while throttling the original client', async () => {
|
||||
vi.useFakeTimers({ toFake: ['Date'] })
|
||||
vi.setSystemTime(1_000_000)
|
||||
const first = new FakeSession('connected')
|
||||
const second = new FakeSession('connected')
|
||||
first.sendRequest.mockResolvedValue(reply('first'))
|
||||
second.sendRequest.mockResolvedValue(reply('second'))
|
||||
const clientRef: { current: RpcClient | null } = { current: first }
|
||||
const served: unknown[] = []
|
||||
let refresh = async () => {}
|
||||
function Probe({ client, hostId }: { client: RpcClient; hostId: string }) {
|
||||
refresh = useHostProjectSections({
|
||||
client,
|
||||
connState: 'connected',
|
||||
hostId,
|
||||
state: {
|
||||
clientRef,
|
||||
setServedProjectSections: (value) => served.push(value),
|
||||
setHostProjectGroups: () => {}
|
||||
}
|
||||
})
|
||||
return null
|
||||
}
|
||||
let tree!: ReturnType<typeof create>
|
||||
act(() => {
|
||||
tree = create(createElement(Probe, { client: first, hostId: 'a' }))
|
||||
})
|
||||
await act(async () => refresh())
|
||||
await act(async () => refresh())
|
||||
expect(first.sendRequest.mock.calls).toEqual([['mobileRelay.projectSections']])
|
||||
clientRef.current = second
|
||||
act(() => tree.update(createElement(Probe, { client: second, hostId: 'b' })))
|
||||
await act(async () => refresh())
|
||||
expect(second.sendRequest.mock.calls).toEqual([['mobileRelay.projectSections']])
|
||||
expect(served).toEqual([catalog('first'), catalog('second')])
|
||||
})
|
||||
|
||||
it('reads again after an invalidation during an in-flight catalog read', async () => {
|
||||
const client = new FakeSession('connected')
|
||||
let answerFirst: ((reply: RpcResponse) => void) | undefined
|
||||
client.sendRequest.mockReturnValueOnce(
|
||||
new Promise((resolve) => {
|
||||
answerFirst = resolve
|
||||
})
|
||||
)
|
||||
client.sendRequest.mockResolvedValueOnce(reply('moved'))
|
||||
const served: unknown[] = []
|
||||
let refresh: (options?: {
|
||||
force?: boolean
|
||||
queueIfInFlight?: boolean
|
||||
}) => Promise<void> = async () => {}
|
||||
function Probe() {
|
||||
refresh = useHostProjectSections({
|
||||
client,
|
||||
connState: 'connected',
|
||||
hostId: 'a',
|
||||
state: {
|
||||
clientRef: { current: client },
|
||||
setServedProjectSections: (value) => served.push(value),
|
||||
setHostProjectGroups: () => {}
|
||||
}
|
||||
})
|
||||
return null
|
||||
}
|
||||
act(() => {
|
||||
create(createElement(Probe))
|
||||
})
|
||||
let first!: Promise<void>
|
||||
act(() => {
|
||||
first = refresh({ force: true, queueIfInFlight: true })
|
||||
})
|
||||
await act(async () => refresh({ force: true, queueIfInFlight: true }))
|
||||
await act(async () => {
|
||||
if (answerFirst === undefined) {
|
||||
throw new Error('first catalog request never entered')
|
||||
}
|
||||
answerFirst(reply('before'))
|
||||
await first
|
||||
})
|
||||
expect(client.sendRequest.mock.calls).toEqual([
|
||||
['mobileRelay.projectSections'],
|
||||
['mobileRelay.projectSections']
|
||||
])
|
||||
expect(served).toEqual([catalog('before'), catalog('moved')])
|
||||
})
|
||||
|
||||
it('reads nothing while the sidebar feed carries the catalog', async () => {
|
||||
const client = new FakeSession('connected')
|
||||
let refresh = async () => {}
|
||||
function Probe() {
|
||||
refresh = useHostProjectSections({
|
||||
client,
|
||||
connState: 'connected',
|
||||
hostId: 'a',
|
||||
state: {
|
||||
clientRef: { current: client },
|
||||
setServedProjectSections: () => {},
|
||||
setHostProjectGroups: () => {}
|
||||
},
|
||||
feedCarriesCatalog: () => true
|
||||
})
|
||||
return null
|
||||
}
|
||||
act(() => {
|
||||
create(createElement(Probe))
|
||||
})
|
||||
await act(async () => refresh())
|
||||
expect(client.sendRequest).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -1,92 +1,75 @@
|
||||
import { useCallback, useRef } from 'react'
|
||||
import type { RpcClient } from '../transport/rpc-client'
|
||||
import type { ConnectionState } from '../transport/types'
|
||||
import { useEffect } from 'react'
|
||||
import type { ProjectGroup } from '../../../src/shared/project-group-types'
|
||||
import { ORCA_SIDEBAR_INPUTS_IS_MAIN } from '../../../src/shared/sidebar-inputs-switch'
|
||||
import { defineHostQuery, useHostQuery } from '../query/host-query'
|
||||
import type { RpcClient } from '../transport/rpc-client'
|
||||
import {
|
||||
hostProjectGroupsRead,
|
||||
projectSectionsRead,
|
||||
readServedProjectSections
|
||||
readServedProjectSections,
|
||||
type ServedProjectSections
|
||||
} from '../worktree/project-sections-operations'
|
||||
import { readHostMethodReply } from './host-sidebar-feed-operations'
|
||||
import { HOST_LIST_TARGET, NO_HOST_LIST_INPUT } from './host-list-queries'
|
||||
import type { HostScreenState } from './use-host-screen-state'
|
||||
|
||||
const PROJECT_SECTIONS_REFRESH_MS = 60_000
|
||||
type HostProjectSections = {
|
||||
sections: ServedProjectSections | null
|
||||
// Read only by a feed-built list whose window catalog carries no groups.
|
||||
groups: readonly ProjectGroup[] | null
|
||||
}
|
||||
|
||||
// Refresh Project catalog independently so slow/missing replies do not hold back repo labels; skip while the sidebar feed supplies it with the rows.
|
||||
const hostProjectSectionsQuery = defineHostQuery({
|
||||
family: 'project.sections',
|
||||
fetch: async (
|
||||
client,
|
||||
_target,
|
||||
_input: typeof NO_HOST_LIST_INPUT
|
||||
): Promise<HostProjectSections> => {
|
||||
const reply = await projectSectionsRead.request(client).catch(() => null)
|
||||
const sections = readServedProjectSections(reply)
|
||||
if (sections.kind !== 'answered') {
|
||||
throw new Error('project sections unavailable')
|
||||
}
|
||||
// Switch point: with no window catalog's groups, a feed-built list reads the host's own.
|
||||
if (!ORCA_SIDEBAR_INPUTS_IS_MAIN || sections.value?.projectGroups) {
|
||||
return { sections: sections.value, groups: null }
|
||||
}
|
||||
const groupReply = await hostProjectGroupsRead.request(client).catch(() => null)
|
||||
const groups = groupReply && readHostMethodReply(groupReply, hostProjectGroupsRead.interpret)
|
||||
return { sections: sections.value, groups: groups?.kind === 'answered' ? groups.value : null }
|
||||
},
|
||||
staleTime: 60_000,
|
||||
gcTime: 30 * 60_000,
|
||||
refetchOnWindowFocus: true,
|
||||
signal: { events: ['reposChanged'] }
|
||||
})
|
||||
|
||||
// The Project catalog, read apart from repo labels so a slow or missing reply holds nothing back.
|
||||
export function useHostProjectSections(args: {
|
||||
client: RpcClient | null
|
||||
connState: ConnectionState
|
||||
hostId: string | undefined
|
||||
state: Pick<HostScreenState, 'clientRef' | 'setServedProjectSections' | 'setHostProjectGroups'>
|
||||
feedCarriesCatalog?: () => boolean
|
||||
}): (options?: { force?: boolean; queueIfInFlight?: boolean }) => Promise<void> {
|
||||
const { client, connState, hostId, state, feedCarriesCatalog } = args
|
||||
const { clientRef, setServedProjectSections, setHostProjectGroups } = state
|
||||
const inFlight = useRef(new WeakSet<RpcClient>())
|
||||
// A read queued while one ran: that one may predate the change this was asked for.
|
||||
const queued = useRef(new WeakSet<RpcClient>())
|
||||
// Per client, so a reused screen for another host fetches its catalog at once.
|
||||
const fetchedAt = useRef(new WeakMap<RpcClient, number>())
|
||||
|
||||
return useCallback(
|
||||
async (options: { force?: boolean; queueIfInFlight?: boolean } = {}) => {
|
||||
if (!client || connState !== 'connected' || !hostId || feedCarriesCatalog?.()) {
|
||||
return
|
||||
}
|
||||
if (inFlight.current.has(client)) {
|
||||
if (options.queueIfInFlight) {
|
||||
queued.current.add(client)
|
||||
}
|
||||
return
|
||||
}
|
||||
if (
|
||||
!options.force &&
|
||||
Date.now() - (fetchedAt.current.get(client) ?? 0) < PROJECT_SECTIONS_REFRESH_MS
|
||||
) {
|
||||
return
|
||||
}
|
||||
const readOnce = async (): Promise<boolean> => {
|
||||
const reply = await projectSectionsRead.request(client).catch(() => null)
|
||||
if (clientRef.current !== client) {
|
||||
return false
|
||||
}
|
||||
const sections = readServedProjectSections(reply)
|
||||
if (sections.kind === 'answered') {
|
||||
fetchedAt.current.set(client, Date.now())
|
||||
setServedProjectSections(sections.value)
|
||||
// Switch point: with no window catalog's groups, a feed-built list reads the host's own.
|
||||
if (ORCA_SIDEBAR_INPUTS_IS_MAIN && !sections.value?.projectGroups) {
|
||||
const groupReply = await hostProjectGroupsRead.request(client).catch(() => null)
|
||||
const groups =
|
||||
groupReply && readHostMethodReply(groupReply, hostProjectGroupsRead.interpret)
|
||||
if (groups?.kind === 'answered' && clientRef.current === client) {
|
||||
setHostProjectGroups(groups.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
const readUntilCurrent = async (): Promise<void> => {
|
||||
queued.current.delete(client)
|
||||
if ((await readOnce()) && queued.current.has(client)) {
|
||||
await readUntilCurrent()
|
||||
}
|
||||
}
|
||||
inFlight.current.add(client)
|
||||
try {
|
||||
await readUntilCurrent()
|
||||
} finally {
|
||||
inFlight.current.delete(client)
|
||||
}
|
||||
},
|
||||
[
|
||||
client,
|
||||
clientRef,
|
||||
connState,
|
||||
feedCarriesCatalog,
|
||||
hostId,
|
||||
setServedProjectSections,
|
||||
setHostProjectGroups
|
||||
]
|
||||
state: Pick<HostScreenState, 'setServedProjectSections' | 'setHostProjectGroups'>
|
||||
subscribed: boolean
|
||||
// The sidebar feed supplies the catalog with the rows.
|
||||
feedCarriesCatalog: boolean
|
||||
}): void {
|
||||
const { client, hostId, state, subscribed, feedCarriesCatalog } = args
|
||||
const { setServedProjectSections, setHostProjectGroups } = state
|
||||
const { data, dataUpdatedAt } = useHostQuery(
|
||||
hostProjectSectionsQuery,
|
||||
{ hostId: hostId ?? '', client },
|
||||
HOST_LIST_TARGET,
|
||||
NO_HOST_LIST_INPUT,
|
||||
{ enabled: !!hostId && !feedCarriesCatalog, subscribed, refetchInterval: 60_000 }
|
||||
)
|
||||
useEffect(() => {
|
||||
if (!data) {
|
||||
return
|
||||
}
|
||||
setServedProjectSections(data.sections)
|
||||
if (data.groups) {
|
||||
setHostProjectGroups(data.groups)
|
||||
}
|
||||
}, [data, dataUpdatedAt, setServedProjectSections, setHostProjectGroups])
|
||||
}
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
import { useCallback, useRef } from 'react'
|
||||
import { getRepoExecutionHostId, parseExecutionHostId } from '../../../src/shared/execution-host'
|
||||
import { setCachedRepos } from '../cache/repo-cache'
|
||||
import { useEffect } from 'react'
|
||||
import { getRepoExecutionHostId } from '../../../src/shared/execution-host'
|
||||
import { useHostQuery } from '../query/host-query'
|
||||
import type { RpcClient } from '../transport/rpc-client'
|
||||
import type { ConnectionState } from '../transport/types'
|
||||
import { buildRepoHostIdByRepoId } from '../worktree/worktree-host-context-labels'
|
||||
import { buildSshHostHealthById } from '../worktree/worktree-host-health'
|
||||
import type { SectionRepo } from '../worktree/workspace-list-types'
|
||||
import type { HostRepoCatalogRow } from './host-screen-reply-schema'
|
||||
import { hostPlatformRead, hostRepoCatalogSnapshotRead } from './host-screen-operations'
|
||||
import {
|
||||
acceptedMetadata,
|
||||
readHostSshTargetLabels,
|
||||
settledMetadataReply
|
||||
} from './host-ssh-target-labels-read'
|
||||
catalogNeedsHostLabels,
|
||||
HOST_LIST_TARGET,
|
||||
hostRepoCatalogQuery,
|
||||
hostRepoLabelsQuery,
|
||||
NO_HOST_LIST_INPUT
|
||||
} from './host-list-queries'
|
||||
import type { HostRepoCatalogRow } from './host-screen-reply-schema'
|
||||
import type { HostScreenState } from './use-host-screen-state'
|
||||
|
||||
const REPO_METADATA_REFRESH_MS = 60_000
|
||||
|
||||
function desktopSectionRepo(repo: HostRepoCatalogRow): SectionRepo {
|
||||
return {
|
||||
id: repo.id,
|
||||
@@ -28,118 +26,51 @@ function desktopSectionRepo(repo: HostRepoCatalogRow): SectionRepo {
|
||||
}
|
||||
}
|
||||
|
||||
// The host's repos and their host labels; repo metadata is decorative, so a failed read keeps what is shown.
|
||||
export function useHostRepoMetadata(args: {
|
||||
client: RpcClient | null
|
||||
connState: ConnectionState
|
||||
hostId: string | undefined
|
||||
state: HostScreenState
|
||||
}) {
|
||||
const { client, connState, hostId, state } = args
|
||||
subscribed: boolean
|
||||
}): void {
|
||||
const { client, hostId, state, subscribed } = args
|
||||
const {
|
||||
clientRef,
|
||||
fetchRepoMetadataInFlightRef,
|
||||
fetchRepoMetadataPendingRef,
|
||||
repoMetadataFetchedAtRef,
|
||||
setHostLabelById,
|
||||
setHostHealthById,
|
||||
setDesktopRepos,
|
||||
setHostPlatform,
|
||||
setRepoHostIdByRepoId
|
||||
} = state
|
||||
// The catalog behind the last snapshot id, per client: an unchanged reply reuses it.
|
||||
const repoSnapshotRef = useRef<{
|
||||
client: RpcClient
|
||||
snapshotId: string
|
||||
catalog: HostRepoCatalogRow[]
|
||||
} | null>(null)
|
||||
|
||||
const fetchRepoMetadata = useCallback(
|
||||
async (options: { force?: boolean; queueIfInFlight?: boolean } = {}) => {
|
||||
if (!client || connState !== 'connected' || !hostId) {
|
||||
return
|
||||
}
|
||||
if (fetchRepoMetadataInFlightRef.current.has(client)) {
|
||||
if (options.queueIfInFlight) {
|
||||
fetchRepoMetadataPendingRef.current.add(client)
|
||||
}
|
||||
return
|
||||
}
|
||||
const now = Date.now()
|
||||
if (!options.force && now - repoMetadataFetchedAtRef.current < REPO_METADATA_REFRESH_MS) {
|
||||
return
|
||||
}
|
||||
fetchRepoMetadataInFlightRef.current.add(client)
|
||||
const requestClient = client,
|
||||
requestHostId = hostId
|
||||
try {
|
||||
do {
|
||||
fetchRepoMetadataPendingRef.current.delete(requestClient)
|
||||
const held =
|
||||
repoSnapshotRef.current?.client === requestClient ? repoSnapshotRef.current : null
|
||||
const repoReply = await settledMetadataReply(() =>
|
||||
hostRepoCatalogSnapshotRead.request(requestClient, {
|
||||
afterSnapshotId: held?.snapshotId ?? null
|
||||
})
|
||||
)
|
||||
if (clientRef.current !== requestClient || hostId !== requestHostId) {
|
||||
return
|
||||
}
|
||||
const repos = repoReply && hostRepoCatalogSnapshotRead.interpret(repoReply)
|
||||
if (!repos || !repos.accepted) {
|
||||
return
|
||||
}
|
||||
const reply = repos.value
|
||||
const catalog =
|
||||
reply.kind === 'catalog'
|
||||
? reply.repos
|
||||
: reply.snapshotId === held?.snapshotId
|
||||
? held.catalog
|
||||
: null
|
||||
// An unchanged reply for a token this client no longer holds asks again in full next time.
|
||||
if (!catalog) {
|
||||
repoSnapshotRef.current = null
|
||||
return
|
||||
}
|
||||
repoMetadataFetchedAtRef.current = Date.now()
|
||||
if (reply.kind === 'catalog') {
|
||||
repoSnapshotRef.current = reply.snapshotId
|
||||
? { client: requestClient, snapshotId: reply.snapshotId, catalog }
|
||||
: null
|
||||
}
|
||||
// Reapply on unchanged too: a host switch clears the shown repos while the token survives.
|
||||
setCachedRepos(requestHostId, catalog)
|
||||
setDesktopRepos(catalog.map(desktopSectionRepo))
|
||||
setRepoHostIdByRepoId(buildRepoHostIdByRepoId(catalog))
|
||||
// Fetch labels only for mixed-host or potentially unhealthy SSH rows. Count repos, not the id-keyed map: the same repo id may exist on two hosts.
|
||||
const hostIds = new Set(catalog.map((repo) => getRepoExecutionHostId(repo)))
|
||||
const hasSshHost = [...hostIds].some((id) => parseExecutionHostId(id)?.kind === 'ssh')
|
||||
if (hostIds.size > 1 || hasSshHost) {
|
||||
const [{ sshTargetSummaries, hostLabelById }, hostPlatform] = await Promise.all([
|
||||
readHostSshTargetLabels(requestClient),
|
||||
settledMetadataReply(() => hostPlatformRead.request(requestClient))
|
||||
])
|
||||
if (clientRef.current !== requestClient || hostId !== requestHostId) {
|
||||
return
|
||||
}
|
||||
setHostHealthById(buildSshHostHealthById(sshTargetSummaries))
|
||||
setHostLabelById(hostLabelById)
|
||||
setHostPlatform(acceptedMetadata(hostPlatform, hostPlatformRead.interpret) ?? null)
|
||||
} else {
|
||||
// Clear labels for departed hosts so stale metadata cannot badge remaining rows.
|
||||
setHostHealthById(new Map())
|
||||
setHostLabelById(new Map())
|
||||
}
|
||||
} while (fetchRepoMetadataPendingRef.current.has(requestClient))
|
||||
} catch {
|
||||
// Repo metadata is decorative; the next refresh can retry.
|
||||
} finally {
|
||||
fetchRepoMetadataInFlightRef.current.delete(requestClient)
|
||||
}
|
||||
},
|
||||
[client, connState, hostId]
|
||||
const host = { hostId: hostId ?? '', client }
|
||||
const options = { enabled: !!hostId, subscribed, refetchInterval: 60_000 }
|
||||
const repos = useHostQuery(
|
||||
hostRepoCatalogQuery,
|
||||
host,
|
||||
HOST_LIST_TARGET,
|
||||
NO_HOST_LIST_INPUT,
|
||||
options
|
||||
)
|
||||
|
||||
return fetchRepoMetadata
|
||||
const needsLabels = repos.data !== undefined && catalogNeedsHostLabels(repos.data.catalog)
|
||||
const labels = useHostQuery(hostRepoLabelsQuery, host, HOST_LIST_TARGET, NO_HOST_LIST_INPUT, {
|
||||
...options,
|
||||
enabled: options.enabled && needsLabels
|
||||
})
|
||||
// Reapplied on every read: a host switch clears the shown repos while the cache keeps them.
|
||||
useEffect(() => {
|
||||
if (repos.data) {
|
||||
setDesktopRepos(repos.data.catalog.map(desktopSectionRepo))
|
||||
setRepoHostIdByRepoId(buildRepoHostIdByRepoId(repos.data.catalog))
|
||||
}
|
||||
}, [repos.data, repos.dataUpdatedAt])
|
||||
useEffect(() => {
|
||||
if (needsLabels && labels.data) {
|
||||
setHostHealthById(buildSshHostHealthById(labels.data.sshTargetSummaries))
|
||||
setHostLabelById(labels.data.hostLabelById)
|
||||
setHostPlatform(labels.data.platform)
|
||||
} else if (repos.data && !needsLabels) {
|
||||
// Clear labels for departed hosts so stale metadata cannot badge remaining rows.
|
||||
setHostHealthById(new Map())
|
||||
setHostLabelById(new Map())
|
||||
}
|
||||
}, [needsLabels, labels.data, labels.dataUpdatedAt, repos.data])
|
||||
}
|
||||
|
||||
export type FetchHostRepoMetadata = ReturnType<typeof useHostRepoMetadata>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useMemo, useState } from 'react'
|
||||
import { useMobileTranslation } from '../i18n/use-mobile-translation'
|
||||
import { useLocalSearchParams, usePathname } from 'expo-router'
|
||||
import { useRouteHandoff } from '../navigation/route-handoff'
|
||||
@@ -22,7 +22,7 @@ import { useHostDisplayWorktrees, useResolvedHostPlatform } from './use-host-dis
|
||||
import { useHostListSections } from './use-host-list-sections'
|
||||
import { useSwitchedHostSidebarFeed } from './use-host-sidebar-feed'
|
||||
import { useHostProjectSections } from './use-host-project-sections'
|
||||
import { useHostRepoMetadata, type FetchHostRepoMetadata } from './use-host-repo-metadata'
|
||||
import { useHostRepoMetadata } from './use-host-repo-metadata'
|
||||
import { peekStoredHost, useHostScreenIdentity } from './use-host-screen-identity'
|
||||
import { useHostScreenState } from './use-host-screen-state'
|
||||
import { useHostViewSettings } from './use-host-view-settings'
|
||||
@@ -79,42 +79,26 @@ export function useHostScreenController({
|
||||
)
|
||||
|
||||
useHostScreenIdentity({ client, hostId, state })
|
||||
const fetchRepoMetadataOnly = useHostRepoMetadata({ client, connState, hostId, state })
|
||||
const sidebarFeed = useSwitchedHostSidebarFeed(client, hostId)
|
||||
// Read through a ref so the metadata refresh keeps its identity across installs.
|
||||
const feedRef = useRef(sidebarFeed?.feed)
|
||||
useEffect(() => {
|
||||
feedRef.current = sidebarFeed?.feed
|
||||
}, [sidebarFeed?.feed])
|
||||
const feedCarriesCatalog = useCallback(
|
||||
() => feedRef.current?.kind === 'ready' && feedRef.current.domains.projectCatalog !== null,
|
||||
[]
|
||||
)
|
||||
const fetchProjectSections = useHostProjectSections({
|
||||
client,
|
||||
connState,
|
||||
hostId,
|
||||
state,
|
||||
feedCarriesCatalog
|
||||
})
|
||||
const fetchRepoMetadata = useCallback<FetchHostRepoMetadata>(
|
||||
(options) => {
|
||||
void fetchProjectSections(options)
|
||||
return fetchRepoMetadataOnly(options)
|
||||
},
|
||||
[fetchProjectSections, fetchRepoMetadataOnly]
|
||||
)
|
||||
const catalog = useHostWorktreeCatalog({
|
||||
client,
|
||||
connState,
|
||||
embedded,
|
||||
fetchRepoMetadata,
|
||||
hostCapabilities,
|
||||
hostId,
|
||||
sidebarFeed: sidebarFeed?.triggers ?? null,
|
||||
state,
|
||||
syncViewSettingsFromDesktop: settings.syncViewSettingsFromDesktop
|
||||
})
|
||||
useHostRepoMetadata({ client, hostId, state, subscribed: catalog.subscribed })
|
||||
const feed = sidebarFeed?.feed ?? null
|
||||
useHostProjectSections({
|
||||
client,
|
||||
hostId,
|
||||
state,
|
||||
subscribed: catalog.subscribed,
|
||||
feedCarriesCatalog: feed?.kind === 'ready' && feed.domains.projectCatalog !== null
|
||||
})
|
||||
const actions = useHostWorktreeActions({
|
||||
client,
|
||||
connState,
|
||||
@@ -134,7 +118,6 @@ export function useHostScreenController({
|
||||
state,
|
||||
hostPlatform
|
||||
)
|
||||
const feed = sidebarFeed?.feed ?? null
|
||||
const sectionsResult = useHostListSections({
|
||||
client,
|
||||
hostId,
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { useEffect } from 'react'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import { translate } from '../i18n/mobile-i18n'
|
||||
import { ORCA_SIDEBAR_INPUTS_IS_MAIN } from '../../../src/shared/sidebar-inputs-switch'
|
||||
import { getCachedWorktrees } from '../cache/worktree-cache'
|
||||
import { loadPinnedIds } from '../storage/preferences'
|
||||
import { loadHosts, peekHosts, updateLastConnected } from '../transport/host-store'
|
||||
import type { HostProfile } from '../transport/types'
|
||||
import type { RpcClient } from '../transport/rpc-client'
|
||||
import type { Worktree } from '../worktree/workspace-list-sections'
|
||||
import type { HostScreenState } from './use-host-screen-state'
|
||||
import { storedHostDescriptor } from './host-stored-identity'
|
||||
import { INITIAL_MOBILE_VIEW } from '../worktree/workspace-view-settings'
|
||||
import { cachedHostWorktrees } from './host-list-queries'
|
||||
|
||||
// The host as last read this launch, so the title's first frame shows its name, not "Host".
|
||||
export function peekStoredHost(hostId: string | undefined): HostProfile | null {
|
||||
@@ -22,9 +22,9 @@ export function useHostScreenIdentity(args: {
|
||||
state: HostScreenState
|
||||
}): void {
|
||||
const { client, hostId, state } = args
|
||||
const queryClient = useQueryClient()
|
||||
const {
|
||||
clientRef,
|
||||
repoMetadataFetchedAtRef,
|
||||
setCatalogError,
|
||||
setError,
|
||||
setHostLabelById,
|
||||
@@ -82,9 +82,8 @@ export function useHostScreenIdentity(args: {
|
||||
setHostHealthById(new Map())
|
||||
setHostPlatform(null)
|
||||
setShowPinnedInGroups(false)
|
||||
repoMetadataFetchedAtRef.current = 0
|
||||
// Expo Router can reuse the screen for a new hostId; reseed because the state initializer runs only once.
|
||||
const freshCache = hostId ? (getCachedWorktrees(hostId) as Worktree[] | null) : null
|
||||
const freshCache = cachedHostWorktrees(queryClient, hostId)
|
||||
setCatalogError(null)
|
||||
if (freshCache) {
|
||||
setWorktrees(freshCache)
|
||||
|
||||
@@ -1,33 +1,26 @@
|
||||
import { useCallback, useRef, useState } from 'react'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import type { ExecutionHostId } from '../../../src/shared/execution-host'
|
||||
import type { ExecutionHostHealth } from '../../../src/shared/execution-host-health'
|
||||
import { getCachedWorktrees } from '../cache/worktree-cache'
|
||||
import { storedHostDescriptor, type HostStoredDescriptor } from './host-stored-identity'
|
||||
import type { RpcClient } from '../transport/rpc-client'
|
||||
import type { HostProfile } from '../transport/types'
|
||||
import { WorktreeCatalogSnapshotClient } from '../worktree/worktree-catalog-snapshot-client'
|
||||
import { INITIAL_MOBILE_VIEW, type MobileViewState } from '../worktree/workspace-view-settings'
|
||||
import type { Worktree } from '../worktree/workspace-list-sections'
|
||||
import type { ServedProjectSections } from '../worktree/project-sections-operations'
|
||||
import type { SectionRepo } from '../worktree/workspace-list-types'
|
||||
import { NO_SERVER_WORKSPACES, type ServerWorkspaces } from '../worktree/server-workspaces'
|
||||
import type { ProjectGroup } from '../../../src/shared/project-group-types'
|
||||
import { cachedHostWorktrees } from './host-list-queries'
|
||||
|
||||
export function useHostScreenState(
|
||||
hostId: string | undefined,
|
||||
storedHost: HostProfile | null = null
|
||||
) {
|
||||
const [initialCache] = useState(() =>
|
||||
hostId ? (getCachedWorktrees(hostId) as Worktree[] | null) : null
|
||||
)
|
||||
const queryClient = useQueryClient()
|
||||
// The rows this world last read for the host, so a reopened list paints them in its first frame.
|
||||
const [initialCache] = useState(() => cachedHostWorktrees(queryClient, hostId))
|
||||
const clientRef = useRef<RpcClient | null>(null)
|
||||
const fetchWorktreesInFlightRef = useRef(false)
|
||||
// Use a ref: React may discard memoized values and silently reset the owned snapshot token.
|
||||
const worktreeCatalogRef = useRef(new WorktreeCatalogSnapshotClient())
|
||||
const fetchRepoMetadataInFlightRef = useRef(new WeakSet<RpcClient>())
|
||||
const fetchRepoMetadataPendingRef = useRef(new WeakSet<RpcClient>())
|
||||
const repoMetadataFetchedAtRef = useRef(0)
|
||||
const newWorktreeModalVisibleRef = useRef(false)
|
||||
const [worktrees, setWorktrees] = useState<Worktree[]>(initialCache ?? [])
|
||||
const [worktreesLoaded, setWorktreesLoaded] = useState(initialCache != null)
|
||||
// Retain catalog failure codes so broken hosts do not render as empty; clear on success (STA-3123).
|
||||
@@ -68,11 +61,7 @@ export function useHostScreenState(
|
||||
const [confirmDelete, setConfirmDelete] = useState<Worktree | null>(null)
|
||||
const [confirmRemoveHost, setConfirmRemoveHost] = useState(false)
|
||||
const [showNewWorktree, setShowNewWorktree] = useState(false)
|
||||
// Why the ref too: the catalog poll reads it from callbacks that outlive this render.
|
||||
const setNewWorktreeVisible = useCallback((visible: boolean) => {
|
||||
newWorktreeModalVisibleRef.current = visible
|
||||
setShowNewWorktree(visible)
|
||||
}, [])
|
||||
const setNewWorktreeVisible = useCallback((visible: boolean) => setShowNewWorktree(visible), [])
|
||||
const [sleptIds, setSleptIds] = useState<Set<string>>(new Set())
|
||||
const [pinnedIds, setPinnedIds] = useState<Set<string>>(new Set())
|
||||
const [showPinnedInGroups, setShowPinnedInGroups] = useState(false)
|
||||
@@ -87,22 +76,17 @@ export function useHostScreenState(
|
||||
hostProjectGroups,
|
||||
actionError,
|
||||
error,
|
||||
fetchRepoMetadataInFlightRef,
|
||||
fetchRepoMetadataPendingRef,
|
||||
fetchWorktreesInFlightRef,
|
||||
hostLabelById,
|
||||
hostHealthById,
|
||||
hostName,
|
||||
hostPlatform,
|
||||
hostStoredDescriptor,
|
||||
lastKnownWorktrees,
|
||||
newWorktreeModalVisibleRef,
|
||||
optimisticActiveWorktreeIdentity,
|
||||
pinnedIds,
|
||||
repoHostIdByRepoId,
|
||||
servedProjectSections,
|
||||
desktopRepos,
|
||||
repoMetadataFetchedAtRef,
|
||||
search,
|
||||
serverNotice,
|
||||
serverWorkspaces,
|
||||
@@ -140,7 +124,6 @@ export function useHostScreenState(
|
||||
view,
|
||||
viewRef,
|
||||
sleptIds,
|
||||
worktreeCatalogRef,
|
||||
worktrees,
|
||||
worktreesLoaded
|
||||
}
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import { MOBILE_RELAY_HOST_WORKTREE_DELTAS_RUNTIME_CAPABILITY } from '../../../src/shared/mobile-desktop-relay-contract'
|
||||
import { readWorktreeFirstPage } from '../worktree/worktree-catalog-first-page'
|
||||
import type { Worktree } from '../worktree/workspace-list-types'
|
||||
import { useCallback, useEffect, useRef } from 'react'
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { AppState } from 'react-native'
|
||||
import { ORCA_SIDEBAR_INPUTS_IS_MAIN } from '../../../src/shared/sidebar-inputs-switch'
|
||||
import { useFocusEffect } from 'expo-router'
|
||||
import { setCachedWorktrees } from '../cache/worktree-cache'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import type { RpcClient } from '../transport/rpc-client'
|
||||
import type { ConnectionState } from '../transport/types'
|
||||
import { RpcIncompatibleReplyError } from '../transport/rpc-incompatible-reply-error'
|
||||
import { useWorktreeResync } from '../transport/use-worktree-resync'
|
||||
import { startHostWorktreeRefresh } from '../worktree/host-worktree-refresh'
|
||||
import type { HostSidebarFeedTriggers } from './host-sidebar-feed-connection'
|
||||
import { areWorktreeListsEqual } from '../worktree/worktree-list-snapshot'
|
||||
@@ -18,21 +17,40 @@ import {
|
||||
retainLiveSleptWorktreeIdentities
|
||||
} from '../worktree/worktree-host-row-identity'
|
||||
import { savePinnedIds } from '../storage/preferences'
|
||||
import type { FetchHostRepoMetadata } from './use-host-repo-metadata'
|
||||
import type { HostScreenState } from './use-host-screen-state'
|
||||
import { relaysToServers } from '../transport/execution-host-scoped-rpc-client'
|
||||
import { NO_SERVER_WORKSPACES } from '../worktree/server-workspaces'
|
||||
import { WorktreeCatalogReadError } from '../worktree/worktree-catalog-snapshot-client'
|
||||
import { useHostQuery } from '../query/host-query'
|
||||
import { hostReadClient } from '../query/host-read-client'
|
||||
import { hostScopeKey } from '../query/host-query-key'
|
||||
import { isHostNotConnectedError } from '../query/host-query-errors'
|
||||
import { refetchHostQueryAfterInFlight, useHostEventRouter } from '../query/host-event-router'
|
||||
import {
|
||||
applyServerWorkspaces,
|
||||
fetchServerWorkspaces,
|
||||
ServerRowSnapshots,
|
||||
NO_SERVER_WORKSPACES
|
||||
} from '../worktree/server-workspaces'
|
||||
isHostListQuery,
|
||||
HOST_LIST_POLL_MS,
|
||||
HOST_LIST_TARGET,
|
||||
hostServerWorkspacesQuery,
|
||||
hostWorktreeCatalogKey,
|
||||
hostWorktreeCatalogQuery,
|
||||
NO_HOST_LIST_INPUT
|
||||
} from './host-list-queries'
|
||||
|
||||
// Shown verbatim so broken remote hosts stay diagnosable instead of looking empty (STA-3123).
|
||||
function catalogErrorCode(error: unknown): string | null {
|
||||
if (isHostNotConnectedError(error)) {
|
||||
return null
|
||||
}
|
||||
if (error instanceof WorktreeCatalogReadError) {
|
||||
return error.code
|
||||
}
|
||||
return error instanceof RpcIncompatibleReplyError ? 'invalid_response' : 'network_error'
|
||||
}
|
||||
|
||||
export function useHostWorktreeCatalog(args: {
|
||||
client: RpcClient | null
|
||||
connState: ConnectionState
|
||||
embedded: boolean
|
||||
fetchRepoMetadata: FetchHostRepoMetadata
|
||||
hostCapabilities: readonly string[]
|
||||
hostId: string | undefined
|
||||
sidebarFeed: HostSidebarFeedTriggers | null
|
||||
@@ -43,7 +61,6 @@ export function useHostWorktreeCatalog(args: {
|
||||
client,
|
||||
connState,
|
||||
embedded,
|
||||
fetchRepoMetadata,
|
||||
hostCapabilities,
|
||||
hostId,
|
||||
sidebarFeed,
|
||||
@@ -53,8 +70,6 @@ export function useHostWorktreeCatalog(args: {
|
||||
const {
|
||||
catalogError,
|
||||
clientRef,
|
||||
fetchWorktreesInFlightRef,
|
||||
newWorktreeModalVisibleRef,
|
||||
setActionError,
|
||||
setCatalogError,
|
||||
setLastKnownWorktrees,
|
||||
@@ -64,188 +79,156 @@ export function useHostWorktreeCatalog(args: {
|
||||
setSleptIds,
|
||||
setWorktrees,
|
||||
setWorktreesLoaded,
|
||||
worktreeCatalogRef,
|
||||
showNewWorktree,
|
||||
worktrees,
|
||||
worktreesLoaded
|
||||
} = state
|
||||
// Keyed by client: a fetch still running on a replaced client must not skip the new one's.
|
||||
const serverFetchInFlightRef = useRef<RpcClient | null>(null)
|
||||
const firstPageRef = useRef<Worktree[] | null>(null)
|
||||
const serverRowSnapshotsRef = useRef<ServerRowSnapshots | null>(null)
|
||||
// A first page only helps a never-loaded empty screen whose full read is not already failing.
|
||||
const firstPageUseful = !worktreesLoaded && worktrees.length === 0 && catalogError === null
|
||||
const firstPageUsefulRef = useRef(firstPageUseful)
|
||||
useEffect(() => {
|
||||
firstPageUsefulRef.current = firstPageUseful
|
||||
}, [firstPageUseful])
|
||||
|
||||
// Fetch server rows separately so a slow server cannot delay desktop rows.
|
||||
const fetchServerRows = useCallback(
|
||||
async (requestClient: RpcClient) => {
|
||||
if (!relaysToServers(requestClient, hostCapabilities)) {
|
||||
// An older desktop or shell: today's view, and no rows that could not open.
|
||||
setServerWorkspaces(NO_SERVER_WORKSPACES)
|
||||
return
|
||||
}
|
||||
if (serverFetchInFlightRef.current === requestClient) {
|
||||
return
|
||||
}
|
||||
serverFetchInFlightRef.current = requestClient
|
||||
try {
|
||||
const deltas = hostCapabilities.includes(
|
||||
MOBILE_RELAY_HOST_WORKTREE_DELTAS_RUNTIME_CAPABILITY
|
||||
)
|
||||
if (serverRowSnapshotsRef.current?.deltas !== deltas) {
|
||||
serverRowSnapshotsRef.current = new ServerRowSnapshots(deltas)
|
||||
}
|
||||
const fetched = await fetchServerWorkspaces(requestClient, serverRowSnapshotsRef.current)
|
||||
if (fetched && clientRef.current === requestClient) {
|
||||
setServerWorkspaces((previous) => applyServerWorkspaces(previous, fetched))
|
||||
}
|
||||
} catch {
|
||||
// Keeps the rows shown; the next poll retries.
|
||||
} finally {
|
||||
if (serverFetchInFlightRef.current === requestClient) {
|
||||
serverFetchInFlightRef.current = null
|
||||
}
|
||||
}
|
||||
},
|
||||
[hostCapabilities]
|
||||
)
|
||||
|
||||
// A re-read asked for during a flight: that flight may predate the change that asked.
|
||||
const fetchWorktreesQueuedRef = useRef(false)
|
||||
const fetchWorktrees = useCallback(
|
||||
async (options: { allowDuringModal?: boolean; queueIfInFlight?: boolean } = {}) => {
|
||||
if (!client || connState !== 'connected' || !hostId) {
|
||||
return
|
||||
}
|
||||
if (!options.allowDuringModal && newWorktreeModalVisibleRef.current) {
|
||||
return
|
||||
}
|
||||
// Coalesce overlapping polls so slow remote hosts cannot accumulate worktree.ps requests.
|
||||
if (fetchWorktreesInFlightRef.current) {
|
||||
fetchWorktreesQueuedRef.current ||= options.queueIfInFlight === true
|
||||
return
|
||||
}
|
||||
fetchWorktreesInFlightRef.current = true
|
||||
const requestClient = client
|
||||
const requestHostId = hostId
|
||||
void fetchServerRows(requestClient)
|
||||
// With nothing to show yet, draw the host's first rows while the full catalog arrives.
|
||||
let fullSettled = false
|
||||
if (firstPageUsefulRef.current) {
|
||||
void readWorktreeFirstPage(requestClient)
|
||||
.then((rows) => {
|
||||
const current = clientRef.current === requestClient && hostId === requestHostId
|
||||
if (rows && current && !fullSettled) {
|
||||
firstPageRef.current = rows
|
||||
setWorktrees((current) => (current.length === 0 ? rows : current))
|
||||
}
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
// A failed full read must not leave the first page looking like the whole host.
|
||||
const dropFirstPage = () =>
|
||||
setWorktrees((current) => (current === firstPageRef.current ? [] : current))
|
||||
|
||||
try {
|
||||
const fetched = await worktreeCatalogRef.current
|
||||
.fetch(requestClient, requestHostId)
|
||||
.finally(() => {
|
||||
fullSettled = true
|
||||
})
|
||||
if (clientRef.current !== requestClient || hostId !== requestHostId) {
|
||||
return
|
||||
}
|
||||
if (!options.allowDuringModal && newWorktreeModalVisibleRef.current) {
|
||||
return
|
||||
}
|
||||
// Report catalog failure rather than zero workspaces so broken remote hosts remain diagnosable (STA-3123).
|
||||
if (fetched.kind === 'request_failed') {
|
||||
setCatalogError(fetched.code)
|
||||
dropFirstPage()
|
||||
return
|
||||
}
|
||||
if (fetched.pending.admission.kind === 'invalid') {
|
||||
setCatalogError('invalid_response')
|
||||
dropFirstPage()
|
||||
}
|
||||
// Even unchanged replies reassert host truth over optimistic local edits.
|
||||
const confirmed = worktreeCatalogRef.current.admit(fetched.pending)
|
||||
if (confirmed) {
|
||||
// Confirmed rows may keep the first page's array; a later failed poll must not clear them.
|
||||
firstPageRef.current = null
|
||||
setCatalogError(null)
|
||||
// A confirmed catalog proves a transient action failure is no longer current.
|
||||
setActionError('')
|
||||
// Reuse identical snapshot arrays to avoid rebuilding SectionList/sorts on the tap path.
|
||||
setWorktrees((current) =>
|
||||
areWorktreeListsEqual(current, confirmed) ? current : confirmed
|
||||
)
|
||||
setLastKnownWorktrees((current) =>
|
||||
areWorktreeListsEqual(current, confirmed) ? current : confirmed
|
||||
)
|
||||
setWorktreesLoaded(true)
|
||||
// Replace the home-written cache with confirmed data so reconnect/remount cannot restore stale rows (#8498).
|
||||
if (hostId) {
|
||||
setCachedWorktrees(hostId, confirmed, { proven: true })
|
||||
}
|
||||
// Drop the optimistic active override once the host reports it active, so later desktop changes win.
|
||||
setOptimisticActiveWorktreeIdentity((pending) =>
|
||||
clearConfirmedActiveWorktreeIdentity(pending, confirmed)
|
||||
)
|
||||
|
||||
// Clear optimistic sleep overrides once the server confirms inactive (liveTerminalCount === 0).
|
||||
setSleptIds((prev) => retainLiveSleptWorktreeIdentities(prev, confirmed))
|
||||
|
||||
// Sync server pins only without the feed; feed-built lists keep no local pin cache.
|
||||
const serverPinned = new Set(confirmed.filter((w) => w.isPinned).map((w) => w.worktreeId))
|
||||
if (!ORCA_SIDEBAR_INPUTS_IS_MAIN) {
|
||||
setPinnedIds((prev) => {
|
||||
if (
|
||||
serverPinned.size === prev.size &&
|
||||
[...serverPinned].every((id) => prev.has(id))
|
||||
) {
|
||||
return prev
|
||||
}
|
||||
if (hostId) {
|
||||
void savePinnedIds(hostId, serverPinned)
|
||||
}
|
||||
return serverPinned
|
||||
})
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
if (clientRef.current === requestClient && hostId === requestHostId) {
|
||||
// Distinguish refused host payloads from connectivity failures; this code is shown verbatim (STA-3123).
|
||||
setCatalogError(
|
||||
error instanceof RpcIncompatibleReplyError ? 'invalid_response' : 'network_error'
|
||||
)
|
||||
dropFirstPage()
|
||||
}
|
||||
} finally {
|
||||
fetchWorktreesInFlightRef.current = false
|
||||
if (fetchWorktreesQueuedRef.current) {
|
||||
fetchWorktreesQueuedRef.current = false
|
||||
// The latest render's fetch, without this flight's modal override: the client may have changed.
|
||||
void fetchWorktreesRef.current({ queueIfInFlight: true })
|
||||
}
|
||||
}
|
||||
},
|
||||
[client, connState, hostId, fetchServerRows]
|
||||
)
|
||||
const fetchWorktreesRef = useRef(fetchWorktrees)
|
||||
useEffect(() => {
|
||||
fetchWorktreesRef.current = fetchWorktrees
|
||||
}, [fetchWorktrees])
|
||||
|
||||
const queryClient = useQueryClient()
|
||||
// Embedded sidebars never receive route focus; a routed list reads only while it is on top.
|
||||
const [focused, setFocused] = useState(true)
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
setFocused(true)
|
||||
// Focus must probe half-open sockets without suspending healthy relay sessions; state-dependent focus callbacks would defeat reconnect backoff.
|
||||
clientRef.current?.notifyForeground('focus')
|
||||
return () => setFocused(false)
|
||||
}, [])
|
||||
)
|
||||
const subscribed = embedded || focused
|
||||
useHostEventRouter(hostId, client)
|
||||
|
||||
const host = { hostId: hostId ?? '', client }
|
||||
// The host's sidebar feed replaces the poll: its notices say when a row changed.
|
||||
const refetchInterval = sidebarFeed ? false : HOST_LIST_POLL_MS
|
||||
const catalog = useHostQuery(
|
||||
hostWorktreeCatalogQuery,
|
||||
host,
|
||||
HOST_LIST_TARGET,
|
||||
NO_HOST_LIST_INPUT,
|
||||
{
|
||||
enabled: !!hostId,
|
||||
subscribed,
|
||||
refetchInterval
|
||||
}
|
||||
)
|
||||
// An older desktop or shell lists no server rows, rather than rows that could not open.
|
||||
const relays = client !== null && relaysToServers(client, hostCapabilities)
|
||||
const servers = useHostQuery(
|
||||
hostServerWorkspacesQuery,
|
||||
host,
|
||||
HOST_LIST_TARGET,
|
||||
{ deltas: hostCapabilities.includes(MOBILE_RELAY_HOST_WORKTREE_DELTAS_RUNTIME_CAPABILITY) },
|
||||
{ enabled: !!hostId && relays, subscribed, refetchInterval }
|
||||
)
|
||||
|
||||
// Set by a read the new-workspace sheet asked for; other reads wait for the sheet to close.
|
||||
const applyDuringModalRef = useRef(false)
|
||||
// The first page drawn while the full catalog was out, until a confirmed catalog replaces it.
|
||||
const firstPageRef = useRef<Worktree[] | null>(null)
|
||||
// Even unchanged replies reassert host truth over optimistic local edits.
|
||||
useEffect(() => {
|
||||
const confirmedSnapshot = catalog.data
|
||||
if (!confirmedSnapshot || !hostId) {
|
||||
return
|
||||
}
|
||||
if (showNewWorktree && !applyDuringModalRef.current) {
|
||||
return
|
||||
}
|
||||
applyDuringModalRef.current = false
|
||||
// A copy: screens may sort the list in place, and the cache's rows are the next delta's base.
|
||||
const confirmed = [...confirmedSnapshot.worktrees]
|
||||
// Confirmed rows may keep the first page's array; a later failed read must not clear them.
|
||||
firstPageRef.current = null
|
||||
setCatalogError(null)
|
||||
// A confirmed catalog proves a transient action failure is no longer current.
|
||||
setActionError('')
|
||||
// Reuse identical snapshot arrays to avoid rebuilding SectionList/sorts on the tap path.
|
||||
setWorktrees((current) => (areWorktreeListsEqual(current, confirmed) ? current : confirmed))
|
||||
setLastKnownWorktrees((current) =>
|
||||
areWorktreeListsEqual(current, confirmed) ? current : confirmed
|
||||
)
|
||||
setWorktreesLoaded(true)
|
||||
// Drop the optimistic active override once the host reports it active, so later desktop changes win.
|
||||
setOptimisticActiveWorktreeIdentity((pending) =>
|
||||
clearConfirmedActiveWorktreeIdentity(pending, confirmed)
|
||||
)
|
||||
// Clear optimistic sleep overrides once the server confirms inactive (liveTerminalCount === 0).
|
||||
setSleptIds((prev) => retainLiveSleptWorktreeIdentities(prev, confirmed))
|
||||
// Sync server pins only without the feed; feed-built lists keep no local pin cache.
|
||||
if (!ORCA_SIDEBAR_INPUTS_IS_MAIN) {
|
||||
const serverPinned = new Set(confirmed.filter((w) => w.isPinned).map((w) => w.worktreeId))
|
||||
setPinnedIds((prev) => {
|
||||
if (serverPinned.size === prev.size && [...serverPinned].every((id) => prev.has(id))) {
|
||||
return prev
|
||||
}
|
||||
void savePinnedIds(hostId, serverPinned)
|
||||
return serverPinned
|
||||
})
|
||||
}
|
||||
}, [catalog.data, catalog.dataUpdatedAt, hostId, showNewWorktree])
|
||||
|
||||
// A first page only helps a never-loaded empty screen whose full read is not already failing.
|
||||
const firstPageUseful = !worktreesLoaded && worktrees.length === 0 && catalogError === null
|
||||
const readingFirstPage = catalog.data === undefined && catalog.isFetching && firstPageUseful
|
||||
// With nothing to show yet, draw the host's first rows while the full catalog arrives.
|
||||
useEffect(() => {
|
||||
if (!readingFirstPage || !client) {
|
||||
return
|
||||
}
|
||||
let current = true
|
||||
// Fails fast while connecting; the connected edge's catalog read asks again.
|
||||
void readWorktreeFirstPage(hostReadClient(client))
|
||||
.then((rows) => {
|
||||
if (rows && current) {
|
||||
firstPageRef.current = rows
|
||||
setWorktrees((shown) => (shown.length === 0 ? rows : shown))
|
||||
}
|
||||
})
|
||||
.catch(() => {})
|
||||
return () => {
|
||||
current = false
|
||||
}
|
||||
}, [readingFirstPage, client])
|
||||
|
||||
useEffect(() => {
|
||||
// errorUpdatedAt outlives the error: a later success clears only `error`.
|
||||
if (catalog.status !== 'error') {
|
||||
return
|
||||
}
|
||||
const code = catalogErrorCode(catalog.error)
|
||||
if (code === null) {
|
||||
return
|
||||
}
|
||||
setCatalogError(code)
|
||||
// A failed full read must not leave the first page looking like the whole host.
|
||||
setWorktrees((shown) => (shown === firstPageRef.current ? [] : shown))
|
||||
}, [catalog.status, catalog.errorUpdatedAt])
|
||||
|
||||
useEffect(() => {
|
||||
if (!relays) {
|
||||
setServerWorkspaces(NO_SERVER_WORKSPACES)
|
||||
} else if (servers.data) {
|
||||
setServerWorkspaces(servers.data)
|
||||
}
|
||||
}, [relays, servers.data])
|
||||
|
||||
const fetchWorktrees = useCallback(
|
||||
async (options: { allowDuringModal?: boolean } = {}) => {
|
||||
if (!hostId) {
|
||||
return
|
||||
}
|
||||
const query = queryClient
|
||||
.getQueryCache()
|
||||
.find({ queryKey: hostWorktreeCatalogKey(hostId), exact: true })
|
||||
if (!query) {
|
||||
return
|
||||
}
|
||||
applyDuringModalRef.current ||= options.allowDuringModal === true
|
||||
// After any read in flight: it may predate the change this read is for.
|
||||
refetchHostQueryAfterInFlight(queryClient, query)
|
||||
},
|
||||
[hostId, queryClient]
|
||||
)
|
||||
|
||||
const startWorktreeRefresh = useCallback(() => {
|
||||
if (!client || connState !== 'connected') {
|
||||
@@ -253,20 +236,12 @@ export function useHostWorktreeCatalog(args: {
|
||||
}
|
||||
return startHostWorktreeRefresh({
|
||||
client,
|
||||
fetchWorktrees,
|
||||
fetchRepoMetadata,
|
||||
refreshWorktrees: () => void fetchWorktrees(),
|
||||
syncViewSettings: syncViewSettingsFromDesktop,
|
||||
sidebarFeed,
|
||||
appState: AppState
|
||||
})
|
||||
}, [
|
||||
client,
|
||||
connState,
|
||||
fetchWorktrees,
|
||||
fetchRepoMetadata,
|
||||
syncViewSettingsFromDesktop,
|
||||
sidebarFeed
|
||||
])
|
||||
}, [client, connState, fetchWorktrees, syncViewSettingsFromDesktop, sidebarFeed])
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
@@ -283,15 +258,46 @@ export function useHostWorktreeCatalog(args: {
|
||||
}
|
||||
}, [embedded, startWorktreeRefresh])
|
||||
|
||||
// Refresh on transition to connected; steady polls can miss the stalest post-sleep cache (#8498).
|
||||
const { refreshing, onRefresh } = useWorktreeResync({
|
||||
client,
|
||||
connState,
|
||||
fetchWorktrees,
|
||||
fetchRepoMetadata
|
||||
})
|
||||
// A read stuck on a replaced client must not hold the list back: TanStack joins a read in flight.
|
||||
const readClientRef = useRef(client)
|
||||
useEffect(() => {
|
||||
const replaced = readClientRef.current !== null && readClientRef.current !== client
|
||||
readClientRef.current = client
|
||||
if (!replaced || !hostId) {
|
||||
return
|
||||
}
|
||||
const hostList = { queryKey: hostScopeKey(hostId), predicate: isHostListQuery }
|
||||
void queryClient
|
||||
.cancelQueries(hostList)
|
||||
.then(() => queryClient.refetchQueries({ ...hostList, type: 'active' }))
|
||||
}, [client, hostId, queryClient])
|
||||
|
||||
return { fetchWorktrees, onRefresh, refreshing }
|
||||
const [refreshing, setRefreshing] = useState(false)
|
||||
const onRefresh = useCallback(async () => {
|
||||
if (!hostId || connState !== 'connected') {
|
||||
return
|
||||
}
|
||||
setRefreshing(true)
|
||||
try {
|
||||
await queryClient.refetchQueries({
|
||||
queryKey: hostScopeKey(hostId),
|
||||
type: 'active',
|
||||
predicate: isHostListQuery
|
||||
})
|
||||
} finally {
|
||||
setRefreshing(false)
|
||||
}
|
||||
}, [connState, hostId, queryClient])
|
||||
|
||||
return {
|
||||
fetchWorktrees,
|
||||
onRefresh,
|
||||
refreshing,
|
||||
// The other host-list reads follow the catalog's visibility.
|
||||
subscribed,
|
||||
// The first read after the list painted from memory; polls and pull-to-refresh show nothing here.
|
||||
revalidating: catalog.isRefetching && !catalog.isFetchedAfterMount
|
||||
}
|
||||
}
|
||||
|
||||
export type HostWorktreeCatalog = ReturnType<typeof useHostWorktreeCatalog>
|
||||
|
||||
@@ -1,39 +1,27 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useHostQuery } from '../query/host-query'
|
||||
import type { RpcClient } from '../transport/rpc-client'
|
||||
import type { Worktree } from '../worktree/workspace-list-types'
|
||||
import { worktreeCatalogRead } from '../worktree/worktree-catalog-operations'
|
||||
import { WORKTREE_PS_FULL_LIMIT } from '../worktree/worktree-catalog-snapshot-client'
|
||||
import {
|
||||
HOST_LIST_TARGET,
|
||||
hostWorktreeCatalogQuery,
|
||||
NO_HOST_LIST_INPUT
|
||||
} from '../host-screen/host-list-queries'
|
||||
|
||||
type WorktreeRow = Pick<Worktree, 'path' | 'repoId' | 'branch'>
|
||||
|
||||
const NO_ROWS: readonly WorktreeRow[] = []
|
||||
|
||||
// The host list's catalog: the drawer shows it at once when this document already read it.
|
||||
export function useHostWorktreeRows(
|
||||
client: RpcClient | null,
|
||||
connected: boolean,
|
||||
hostId: string
|
||||
): readonly WorktreeRow[] {
|
||||
const [rows, setRows] = useState<readonly WorktreeRow[]>(NO_ROWS)
|
||||
|
||||
useEffect(() => {
|
||||
if (!client || !connected) {
|
||||
return
|
||||
}
|
||||
let live = true
|
||||
worktreeCatalogRead
|
||||
.requestSingleFlight(client, hostId, { limit: WORKTREE_PS_FULL_LIMIT })
|
||||
.then((reply) => {
|
||||
const catalog = worktreeCatalogRead.interpret(reply)
|
||||
if (live && catalog.accepted && catalog.value.worktrees) {
|
||||
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: proven worktree.ps rows, which the host screen's snapshot client admits as `Worktree`; the drawer reads only these three.
|
||||
setRows(catalog.value.worktrees as WorktreeRow[])
|
||||
}
|
||||
})
|
||||
.catch(() => {})
|
||||
return () => {
|
||||
live = false
|
||||
}
|
||||
}, [client, connected, hostId])
|
||||
|
||||
return rows
|
||||
const { data } = useHostQuery(
|
||||
hostWorktreeCatalogQuery,
|
||||
{ hostId, client: connected ? client : null },
|
||||
HOST_LIST_TARGET,
|
||||
NO_HOST_LIST_INPUT
|
||||
)
|
||||
return data?.worktrees ?? NO_ROWS
|
||||
}
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
import { createElement } from 'react'
|
||||
import { QueryClientProvider } from '@tanstack/react-query'
|
||||
import { act, create, type ReactTestRenderer } from 'react-test-renderer'
|
||||
import { afterEach, expect, it } from 'vitest'
|
||||
import { settleQueryNotifications } from '../test-support/rpc-recording/hook-mount'
|
||||
import { stubRpcClient } from '../transport/rpc-client-test-stub'
|
||||
import type { RpcResponse } from '../transport/types'
|
||||
import { useHostEventRouter } from './host-event-router'
|
||||
import { defineHostQuery, useHostQuery } from './host-query'
|
||||
import { createOrcaQueryClient } from './orca-query-client'
|
||||
|
||||
const NO_INPUT = {}
|
||||
|
||||
const rowsQuery = defineHostQuery({
|
||||
family: 'test.rows',
|
||||
fetch: async (client, _target, _input: typeof NO_INPUT) => {
|
||||
const reply = await client.sendRequest('test.rows')
|
||||
return reply.ok ? String(reply.result) : 'refused'
|
||||
},
|
||||
signal: { events: ['worktreesChanged'] }
|
||||
})
|
||||
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
|
||||
afterEach(() => {
|
||||
act(() => renderer?.unmount())
|
||||
renderer = null
|
||||
})
|
||||
|
||||
function desktop() {
|
||||
let emit: (event: unknown) => void = () => {}
|
||||
const replies: ((rows: string) => void)[] = []
|
||||
const stateListeners = new Set<(state: 'connected' | 'disconnected') => void>()
|
||||
const client = stubRpcClient({
|
||||
getLastConnectedAt: () => 1,
|
||||
onStateChange: (listener) => {
|
||||
stateListeners.add(listener)
|
||||
return () => stateListeners.delete(listener)
|
||||
},
|
||||
subscribe: (_method, _params, onData) => {
|
||||
emit = onData
|
||||
return () => {}
|
||||
},
|
||||
sendRequest: () =>
|
||||
new Promise<RpcResponse>((resolve) => {
|
||||
replies.push((rows) => resolve({ id: 'r', ok: true, result: rows }))
|
||||
})
|
||||
})
|
||||
return {
|
||||
client,
|
||||
emit: (event: unknown) => emit(event),
|
||||
replies,
|
||||
setState: (state: 'connected' | 'disconnected') => stateListeners.forEach((l) => l(state))
|
||||
}
|
||||
}
|
||||
|
||||
async function mountList(host: ReturnType<typeof desktop>): Promise<() => string | undefined> {
|
||||
let rows: string | undefined
|
||||
function List(): null {
|
||||
useHostEventRouter('host-1', host.client)
|
||||
rows = useHostQuery(
|
||||
rowsQuery,
|
||||
{ hostId: 'host-1', client: host.client },
|
||||
{ exec: 'local' },
|
||||
NO_INPUT
|
||||
).data
|
||||
return null
|
||||
}
|
||||
await act(async () => {
|
||||
renderer = create(
|
||||
createElement(QueryClientProvider, { client: createOrcaQueryClient() }, createElement(List))
|
||||
)
|
||||
})
|
||||
await settleQueryNotifications()
|
||||
return () => rows
|
||||
}
|
||||
|
||||
it('reads again when the desktop reports a change during a read, ending on the rows after it', async () => {
|
||||
const host = desktop()
|
||||
const rows = await mountList(host)
|
||||
host.emit({ type: 'ready', subscriptionId: 'events-1' })
|
||||
expect(host.replies).toHaveLength(1)
|
||||
|
||||
// The read already out was sent before this change, so its rows may not include it.
|
||||
host.emit({ type: 'worktreesChanged', repoId: 'repo-1', sortOrderOnly: false })
|
||||
await act(async () => host.replies[0]('before'))
|
||||
await settleQueryNotifications()
|
||||
expect(host.replies).toHaveLength(2)
|
||||
|
||||
await act(async () => host.replies[1]('after'))
|
||||
await settleQueryNotifications()
|
||||
expect(rows()).toBe('after')
|
||||
})
|
||||
|
||||
it('listens again on the next connection after the desktop ends the event stream', async () => {
|
||||
const host = desktop()
|
||||
await mountList(host)
|
||||
await act(async () => host.replies[0]('first'))
|
||||
host.emit({ type: 'ready', subscriptionId: 'events-1' })
|
||||
host.emit({ type: 'end' })
|
||||
await act(async () => {
|
||||
host.setState('disconnected')
|
||||
host.setState('connected')
|
||||
})
|
||||
// The connected edge's own re-read.
|
||||
await act(async () => host.replies.at(-1)!('second'))
|
||||
await settleQueryNotifications()
|
||||
host.emit({ type: 'ready', subscriptionId: 'events-2' })
|
||||
const before = host.replies.length
|
||||
|
||||
host.emit({ type: 'worktreesChanged', repoId: 'repo-1', sortOrderOnly: false })
|
||||
await settleQueryNotifications()
|
||||
expect(host.replies).toHaveLength(before + 1)
|
||||
})
|
||||
|
||||
it('survives a stream that ends while opening and opens it on the next connection', async () => {
|
||||
const host = desktop()
|
||||
let refuse = true
|
||||
const subscribe = host.client.subscribe
|
||||
host.client.subscribe = (method, params, onData, options) => {
|
||||
if (refuse) {
|
||||
refuse = false
|
||||
onData({ type: 'error', message: 'refused' })
|
||||
return () => {}
|
||||
}
|
||||
return subscribe(method, params, onData, options)
|
||||
}
|
||||
await mountList(host)
|
||||
await act(async () => host.replies[0]('first'))
|
||||
await act(async () => {
|
||||
host.setState('disconnected')
|
||||
host.setState('connected')
|
||||
})
|
||||
await act(async () => host.replies.at(-1)!('second'))
|
||||
await settleQueryNotifications()
|
||||
host.emit({ type: 'ready', subscriptionId: 'events-2' })
|
||||
const before = host.replies.length
|
||||
|
||||
host.emit({ type: 'worktreesChanged', repoId: 'repo-1', sortOrderOnly: false })
|
||||
await settleQueryNotifications()
|
||||
expect(host.replies).toHaveLength(before + 1)
|
||||
})
|
||||
@@ -0,0 +1,207 @@
|
||||
import { useEffect } from 'react'
|
||||
import { focusManager, useQueryClient, type Query, type QueryClient } from '@tanstack/react-query'
|
||||
import type { RpcClient } from '../transport/rpc-client'
|
||||
import {
|
||||
subscribeSharedClientEvents,
|
||||
type SharedClientEvent
|
||||
} from '../transport/shared-client-events'
|
||||
import type { HostQuerySignal } from './host-query'
|
||||
import { hostScopeKey } from './host-query-key'
|
||||
|
||||
function signalOf(query: Query): HostQuerySignal | undefined {
|
||||
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: hostQueryOptions writes `signal` from the family's HostQuerySignal.
|
||||
return query.meta?.signal as HostQuerySignal | undefined
|
||||
}
|
||||
|
||||
const trailingReads = new WeakMap<QueryClient, Set<string>>()
|
||||
|
||||
function trailingReadsOf(queryClient: QueryClient): Set<string> {
|
||||
const existing = trailingReads.get(queryClient)
|
||||
if (existing) {
|
||||
return existing
|
||||
}
|
||||
const hashes = new Set<string>()
|
||||
trailingReads.set(queryClient, hashes)
|
||||
// One cache listener per world, for as long as the world's client lives.
|
||||
queryClient.getQueryCache().subscribe(({ type, query }) => {
|
||||
if (type === 'updated' && hashes.has(query.queryHash) && query.state.fetchStatus === 'idle') {
|
||||
hashes.delete(query.queryHash)
|
||||
refetchHostQueryAfterInFlight(queryClient, query)
|
||||
}
|
||||
})
|
||||
return hashes
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an observed query again, after the read already out settles if there is one: that read may
|
||||
* have been sent before the change that asked. TanStack's own refetch would join it or cancel it.
|
||||
*/
|
||||
export function refetchHostQueryAfterInFlight(queryClient: QueryClient, query: Query): void {
|
||||
if (query.state.fetchStatus === 'fetching') {
|
||||
trailingReadsOf(queryClient).add(query.queryHash)
|
||||
return
|
||||
}
|
||||
void queryClient.refetchQueries(
|
||||
{ queryKey: query.queryKey, exact: true, type: 'active' },
|
||||
{ cancelRefetch: false }
|
||||
)
|
||||
}
|
||||
|
||||
function startHostEventRouter(
|
||||
queryClient: QueryClient,
|
||||
hostId: string,
|
||||
client: RpcClient
|
||||
): () => void {
|
||||
let ready = false
|
||||
let stopStream: (() => void) | null = null
|
||||
// Bumped when the stream ends, so an opener whose wire ended synchronously keeps no stale stop.
|
||||
let streamEpoch = 0
|
||||
// When the stream last said it was live; a replayed ready may have missed events since.
|
||||
let readyAt = 0
|
||||
const floorTimers = new Map<string, ReturnType<typeof setTimeout>>()
|
||||
const signalledQueries = (matches: (signal: HostQuerySignal) => boolean): Query[] =>
|
||||
queryClient.getQueryCache().findAll({
|
||||
queryKey: hostScopeKey(hostId),
|
||||
predicate: (query) => {
|
||||
const signal = signalOf(query)
|
||||
return signal !== undefined && matches(signal)
|
||||
}
|
||||
})
|
||||
const markStale = (query: Query): Promise<void> =>
|
||||
queryClient.invalidateQueries({ queryKey: query.queryKey, exact: true, refetchType: 'none' })
|
||||
// Unobserved or hidden: stale only, so the next mount or foreground reads it.
|
||||
const readsNow = (query: Query): boolean => query.isActive() && focusManager.isFocused()
|
||||
|
||||
const onChange = (query: Query): void => {
|
||||
void markStale(query)
|
||||
if (!readsNow(query) || floorTimers.has(query.queryHash)) {
|
||||
return
|
||||
}
|
||||
// Change events can come per agent step; keep reads `floorMs` apart, polls included.
|
||||
const wait = query.state.dataUpdatedAt + (signalOf(query)?.floorMs ?? 0) - Date.now()
|
||||
if (wait <= 0) {
|
||||
refetchHostQueryAfterInFlight(queryClient, query)
|
||||
return
|
||||
}
|
||||
floorTimers.set(
|
||||
query.queryHash,
|
||||
setTimeout(() => {
|
||||
floorTimers.delete(query.queryHash)
|
||||
if (readsNow(query)) {
|
||||
refetchHostQueryAfterInFlight(queryClient, query)
|
||||
}
|
||||
}, wait)
|
||||
)
|
||||
}
|
||||
|
||||
const onReplayedReady = (missedSince: number): void => {
|
||||
// After a reconnect, the connected edge already re-read every observed query; a replay with no reconnect (a path switch) did not.
|
||||
const connectedAt = client.getLastConnectedAt() ?? 0
|
||||
const readSince = connectedAt > missedSince ? connectedAt : Infinity
|
||||
for (const query of signalledQueries(() => true)) {
|
||||
if (query.state.fetchStatus === 'fetching' || query.state.dataUpdatedAt >= readSince) {
|
||||
continue
|
||||
}
|
||||
if (readsNow(query)) {
|
||||
void queryClient.invalidateQueries({ queryKey: query.queryKey, exact: true })
|
||||
} else {
|
||||
void markStale(query)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const onEvent = (event: SharedClientEvent): void => {
|
||||
if (event.type === 'ready') {
|
||||
// Events are not queued while disconnected, so a replayed ready may follow missed changes.
|
||||
if (ready && !('joined' in event)) {
|
||||
onReplayedReady(readyAt)
|
||||
}
|
||||
ready = true
|
||||
readyAt = Date.now()
|
||||
return
|
||||
}
|
||||
if (event.type === 'end' || event.type === 'error') {
|
||||
// The shared stream is gone; the next connected edge opens a fresh one (the edge itself re-reads what was missed).
|
||||
ready = false
|
||||
streamEpoch += 1
|
||||
stopStream?.()
|
||||
stopStream = null
|
||||
return
|
||||
}
|
||||
const eventType = event.type
|
||||
for (const query of signalledQueries((signal) => signal.events.includes(eventType))) {
|
||||
onChange(query)
|
||||
}
|
||||
}
|
||||
|
||||
const openStream = (): void => {
|
||||
const epoch = streamEpoch
|
||||
try {
|
||||
const stop = subscribeSharedClientEvents(client, onEvent)
|
||||
if (streamEpoch === epoch) {
|
||||
stopStream = stop
|
||||
} else {
|
||||
// The wire ended while opening; the next connected edge tries again.
|
||||
stop()
|
||||
}
|
||||
} catch {
|
||||
// A page that can't open a stream now gets another chance on the next connected edge.
|
||||
}
|
||||
}
|
||||
openStream()
|
||||
const stopWatchingConnection = client.onStateChange((state) => {
|
||||
if (state === 'connected' && !stopStream) {
|
||||
openStream()
|
||||
}
|
||||
})
|
||||
return () => {
|
||||
stopWatchingConnection()
|
||||
stopStream?.()
|
||||
stopStream = null
|
||||
for (const timer of floorTimers.values()) {
|
||||
clearTimeout(timer)
|
||||
}
|
||||
floorTimers.clear()
|
||||
}
|
||||
}
|
||||
|
||||
type Router = { holders: number; stop: () => void }
|
||||
|
||||
const routers = new WeakMap<QueryClient, Map<RpcClient, Router>>()
|
||||
|
||||
// One router per world per host client, however many screens read that host.
|
||||
function retainHostEventRouter(
|
||||
queryClient: QueryClient,
|
||||
hostId: string,
|
||||
client: RpcClient
|
||||
): () => void {
|
||||
let byClient = routers.get(queryClient)
|
||||
if (!byClient) {
|
||||
byClient = new Map()
|
||||
routers.set(queryClient, byClient)
|
||||
}
|
||||
const clients = byClient
|
||||
let router = clients.get(client)
|
||||
if (!router) {
|
||||
router = { holders: 0, stop: startHostEventRouter(queryClient, hostId, client) }
|
||||
clients.set(client, router)
|
||||
}
|
||||
const held = router
|
||||
held.holders += 1
|
||||
return () => {
|
||||
held.holders -= 1
|
||||
if (held.holders === 0) {
|
||||
held.stop()
|
||||
clients.delete(client)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Keeps the host's event-signalled families (see HostQuery `signal`) fresh while a screen reads them. */
|
||||
export function useHostEventRouter(hostId: string | undefined, client: RpcClient | null): void {
|
||||
const queryClient = useQueryClient()
|
||||
useEffect(
|
||||
() => (client && hostId ? retainHostEventRouter(queryClient, hostId, client) : undefined),
|
||||
[queryClient, hostId, client]
|
||||
)
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
type UseQueryOptions,
|
||||
type UseQueryResult
|
||||
} from '@tanstack/react-query'
|
||||
import type { RuntimeClientEventStreamMessage } from '../../../src/shared/runtime-client-events'
|
||||
import type { RpcClient } from '../transport/rpc-client'
|
||||
import { useHostConnectionInvalidation } from './host-connection-invalidation'
|
||||
import {
|
||||
@@ -36,6 +37,13 @@ export type HostQuery<TInput extends HostQueryParams, TData> = {
|
||||
refetchOnWindowFocus?: boolean
|
||||
// Kept fresh by a subscription, so the connected edge leaves it alone.
|
||||
streamBacked?: boolean
|
||||
// Desktop change events that make this family stale (host-event-router), and the least time between reads they cause.
|
||||
signal?: HostQuerySignal
|
||||
}
|
||||
|
||||
export type HostQuerySignal = {
|
||||
events: readonly Exclude<RuntimeClientEventStreamMessage['type'], 'ready' | 'end'>[]
|
||||
floorMs?: number
|
||||
}
|
||||
|
||||
export function defineHostQuery<TInput extends HostQueryParams, TData>(
|
||||
@@ -51,7 +59,7 @@ function hostQueryOptions<TInput extends HostQueryParams, TData>(
|
||||
target: HostQueryTarget,
|
||||
input: TInput
|
||||
) {
|
||||
const { family, fetch, streamBacked, ...freshness } = query
|
||||
const { family, fetch, streamBacked, signal, ...freshness } = query
|
||||
const queryKey = hostQueryKey(hostId, family, target, input)
|
||||
return queryOptions({
|
||||
...freshness,
|
||||
@@ -59,7 +67,7 @@ function hostQueryOptions<TInput extends HostQueryParams, TData>(
|
||||
// No abort signal: the host has no cancel, and a screen that unmounts still wants the answer cached.
|
||||
queryFn: ({ client: queryClient }) =>
|
||||
fetch(hostReadClient(client), target, input, queryClient.getQueryData<TData>(queryKey)),
|
||||
meta: { streamBacked }
|
||||
meta: { streamBacked, signal }
|
||||
})
|
||||
}
|
||||
|
||||
@@ -119,7 +127,7 @@ export function useHostQuery<TInput extends HostQueryParams, TData>(
|
||||
host: { hostId: string; client: RpcClient | null },
|
||||
target: HostQueryTarget,
|
||||
input: TInput,
|
||||
options?: { enabled?: boolean; subscribed?: boolean }
|
||||
options?: { enabled?: boolean; subscribed?: boolean; refetchInterval?: number | false }
|
||||
): UseQueryResult<TData> {
|
||||
const { hostId, client } = host
|
||||
const enabled = client !== null && options?.enabled !== false
|
||||
@@ -130,7 +138,11 @@ export function useHostQuery<TInput extends HostQueryParams, TData>(
|
||||
const observer = observerOptions(query, client, hostId, target, input)
|
||||
return enabled ? observer : { ...observer, queryFn: skipToken }
|
||||
}, [query, client, hostId, keyHash, enabled])
|
||||
return useQuery({ ...queryOptions, subscribed: options?.subscribed })
|
||||
return useQuery({
|
||||
...queryOptions,
|
||||
subscribed: options?.subscribed,
|
||||
refetchInterval: options?.refetchInterval
|
||||
})
|
||||
}
|
||||
|
||||
// One query per input (e.g. one per open folder), combined into one value; keep `combine` stable so the value is memoized.
|
||||
|
||||
+10
-7
@@ -46,12 +46,16 @@ export function clientEventStreamMountAdapters(
|
||||
const startHostWorktreeRefresh = modules.load<
|
||||
typeof import('../../../worktree/host-worktree-refresh')
|
||||
>('mobile/src/worktree/host-worktree-refresh.ts').startHostWorktreeRefresh
|
||||
const counts = { fetchWorktrees: 0, fetchRepoMetadata: 0 }
|
||||
const counts = { refreshWorktrees: 0, syncViewSettings: 0 }
|
||||
let stop: (() => void) | null = null
|
||||
// Keep absent options distinct from empty objects; record counts because ordering/options are already captured on the wire.
|
||||
const fetching = (fetch: 'fetchWorktrees' | 'fetchRepoMetadata') => (options?: unknown) => {
|
||||
counts[fetch]++
|
||||
effect(fetch, { options })
|
||||
const refreshWorktrees = () => {
|
||||
counts.refreshWorktrees++
|
||||
effect('refreshWorktrees', {})
|
||||
}
|
||||
const syncViewSettings = (options?: unknown) => {
|
||||
counts.syncViewSettings++
|
||||
effect('syncViewSettings', { options })
|
||||
return Promise.resolve()
|
||||
}
|
||||
return {
|
||||
@@ -59,9 +63,8 @@ export function clientEventStreamMountAdapters(
|
||||
if (name === 'start') {
|
||||
stop = startHostWorktreeRefresh({
|
||||
client,
|
||||
fetchWorktrees: fetching('fetchWorktrees'),
|
||||
fetchRepoMetadata: fetching('fetchRepoMetadata'),
|
||||
syncViewSettings: () => Promise.resolve(),
|
||||
refreshWorktrees,
|
||||
syncViewSettings,
|
||||
appState: {
|
||||
currentState: 'active',
|
||||
addEventListener: () => ({ remove: () => {} })
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
import { notifyManager, useQueryClient, type QueryClient } from '@tanstack/react-query'
|
||||
import type * as HostListQueries from '../../../host-screen/host-list-queries'
|
||||
import type * as ResumeMetadata from '../../../agent-history/mobile-agent-history-resume-metadata'
|
||||
import { hostQueryKey, hostScopeKey } from '../../../query/host-query-key'
|
||||
import type { OperationExposure } from '../operation-module-loader'
|
||||
import type { MountAdapter } from '../recording-scenario'
|
||||
import { hookMount } from '../hook-mount'
|
||||
import { observableModel, projectObservable } from '../observable-model'
|
||||
import { operationModuleLoader } from '../operation-module-loader'
|
||||
|
||||
// TanStack's own default: notify on a zero-delay timer.
|
||||
const scheduleNotify = (callback: () => void) => setTimeout(callback, 0)
|
||||
|
||||
export const settingsMountExposures: readonly OperationExposure[] = []
|
||||
|
||||
export function settingsMountAdapters(
|
||||
@@ -86,34 +92,75 @@ export function settingsMountAdapters(
|
||||
const useMetadata = modules.load<
|
||||
typeof import('../../../host-screen/use-host-repo-metadata')
|
||||
>('mobile/src/host-screen/use-host-repo-metadata.ts').useHostRepoMetadata
|
||||
const state = observableModel(context, {
|
||||
clientRef: { current: context.client },
|
||||
fetchRepoMetadataInFlightRef: { current: new Set() },
|
||||
fetchRepoMetadataPendingRef: { current: new Set() },
|
||||
repoMetadataFetchedAtRef: { current: 0 }
|
||||
})
|
||||
let load: ReturnType<typeof useMetadata>
|
||||
const queries = modules.load<typeof HostListQueries>(
|
||||
'mobile/src/host-screen/host-list-queries.ts'
|
||||
)
|
||||
const state = observableModel(context, { clientRef: { current: context.client } })
|
||||
// A fault schedule ends with the read settling on a timeout, after the recorder's last zero-time drain; deliver
|
||||
// TanStack's notifications as microtasks so that read's notify is not left as a timer.
|
||||
notifyManager.setScheduler(queueMicrotask)
|
||||
// Mounted unread, like a list under another screen; `load` brings it on top.
|
||||
let subscribed = false
|
||||
let queryClient: QueryClient | undefined
|
||||
const hook = hookMount(() => {
|
||||
load = useMetadata({
|
||||
queryClient = useQueryClient()
|
||||
useMetadata({
|
||||
client: context.client,
|
||||
connState: 'connected',
|
||||
hostId: 'host-1',
|
||||
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the recorder supplies only the members the hook reads.
|
||||
state: state as unknown as Parameters<typeof useMetadata>[0]['state']
|
||||
state: state as unknown as Parameters<typeof useMetadata>[0]['state'],
|
||||
subscribed
|
||||
})
|
||||
})
|
||||
const query = () =>
|
||||
queryClient?.getQueryCache().find({
|
||||
queryKey: hostQueryKey(
|
||||
'host-1',
|
||||
queries.hostRepoCatalogQuery.family,
|
||||
queries.HOST_LIST_TARGET,
|
||||
queries.NO_HOST_LIST_INPUT
|
||||
),
|
||||
exact: true
|
||||
})
|
||||
return {
|
||||
action(name) {
|
||||
if (name === 'mount') {
|
||||
return hook.mount()
|
||||
}
|
||||
return load({ force: name !== 'load-cached' })
|
||||
if (!subscribed) {
|
||||
subscribed = true
|
||||
hook.update()
|
||||
// Settles with the read the list started on coming on top; failures show in the state.
|
||||
return query()
|
||||
?.fetch()
|
||||
.then(
|
||||
() => undefined,
|
||||
() => undefined
|
||||
)
|
||||
}
|
||||
if (name === 'load-cached') {
|
||||
// A return to the list: the remount reads again only what the family's staleTime calls stale.
|
||||
hook.leave()
|
||||
hook.mount()
|
||||
const current = query()
|
||||
return current?.state.fetchStatus === 'fetching'
|
||||
? current.fetch().then(
|
||||
() => undefined,
|
||||
() => undefined
|
||||
)
|
||||
: undefined
|
||||
}
|
||||
// A change event.
|
||||
return queryClient?.invalidateQueries({ queryKey: hostScopeKey('host-1') })
|
||||
},
|
||||
state: () =>
|
||||
projectObservable(
|
||||
Object.fromEntries(Object.entries(state).filter(([key]) => !key.endsWith('Ref')))
|
||||
),
|
||||
dispose: hook.unmount
|
||||
dispose: async () => {
|
||||
await hook.unmount()
|
||||
notifyManager.setScheduler(scheduleNotify)
|
||||
}
|
||||
}
|
||||
},
|
||||
'settings.task-hydration': (context) => {
|
||||
|
||||
@@ -429,16 +429,9 @@ describe('actual stream result partitions', () => {
|
||||
const outer = ['outer-refused', 'outer-refused-no-message', 'method-not-found'].includes(
|
||||
partition
|
||||
)
|
||||
// Workspace options reads: one at start, one more when the replayed ready after the cutover arrives intact.
|
||||
const expected =
|
||||
partition === 'normal'
|
||||
? [5, 4]
|
||||
: outer && position !== 'last'
|
||||
? [2, 2]
|
||||
: failed
|
||||
? [2, 2]
|
||||
: position === 'middle'
|
||||
? [4, 4]
|
||||
: [4, 3]
|
||||
partition === 'normal' || (position === 'middle' && !outer && !failed) ? 2 : 1
|
||||
if (failed) {
|
||||
expect('error' in run.outcome).toBe(true)
|
||||
if (!('error' in run.outcome) || !(run.outcome.error instanceof RecordingStreamGuard)) {
|
||||
@@ -456,8 +449,8 @@ describe('actual stream result partitions', () => {
|
||||
{ cutover: { status: 'fulfilled' } }
|
||||
)
|
||||
expect(run.outcome.recording.checkpoints.at(-1)!.observation.state).toEqual({
|
||||
fetchWorktrees: expected[0],
|
||||
fetchRepoMetadata: expected[1],
|
||||
refreshWorktrees: 0,
|
||||
syncViewSettings: expected,
|
||||
running: false
|
||||
})
|
||||
}
|
||||
@@ -508,12 +501,10 @@ describe('actual stream result partitions', () => {
|
||||
expect(frames).toHaveLength(2 + unsubscribeIds.length)
|
||||
}
|
||||
expect(run.requests).toEqual([])
|
||||
expect(run.effects.filter((effect) => effect.name === 'fetchWorktrees')).toHaveLength(
|
||||
expected[0]!
|
||||
)
|
||||
expect(run.effects.filter((effect) => effect.name === 'fetchRepoMetadata')).toHaveLength(
|
||||
expected[1]!
|
||||
expect(run.effects.filter((effect) => effect.name === 'syncViewSettings')).toHaveLength(
|
||||
expected
|
||||
)
|
||||
expect(run.effects.filter((effect) => effect.name === 'refreshWorktrees')).toHaveLength(0)
|
||||
if (mode !== 'native') {
|
||||
const posted = run.pairs[0]!.toShell.map((json) => JSON.parse(json))
|
||||
expect(posted.filter((frame) => frame.type === 'subscribe')).toHaveLength(1)
|
||||
@@ -533,8 +524,8 @@ describe('actual stream result partitions', () => {
|
||||
expect(run.outcome.error).toBeInstanceOf(RecordingStreamGuard)
|
||||
expect(run.actions).toEqual(['start'])
|
||||
expect(run.reads.at(-1)).toEqual({
|
||||
fetchWorktrees: 2,
|
||||
fetchRepoMetadata: 2,
|
||||
refreshWorktrees: 0,
|
||||
syncViewSettings: 1,
|
||||
running: true
|
||||
})
|
||||
break
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import type { RpcClient } from './rpc-client'
|
||||
import type { ConnectionState } from './types'
|
||||
|
||||
export function useWorktreeResync(args: {
|
||||
client: RpcClient | null
|
||||
connState: ConnectionState
|
||||
fetchWorktrees: (opts?: { allowDuringModal?: boolean }) => Promise<void>
|
||||
fetchRepoMetadata: (options?: { force?: boolean; queueIfInFlight?: boolean }) => Promise<void>
|
||||
}): { refreshing: boolean; onRefresh: () => Promise<void> } {
|
||||
const { client, connState, fetchWorktrees, fetchRepoMetadata } = args
|
||||
|
||||
// Reconnect must refetch cached worktrees after background/sleep (#8498).
|
||||
const prevConnStateRef = useRef(connState)
|
||||
useEffect(() => {
|
||||
const prev = prevConnStateRef.current
|
||||
prevConnStateRef.current = connState
|
||||
if (prev !== 'connected' && connState === 'connected' && client) {
|
||||
void fetchWorktrees({ allowDuringModal: true })
|
||||
}
|
||||
// startHostWorktreeRefresh owns repo metadata refresh; reconnect here refetches worktrees only.
|
||||
}, [connState, client, fetchWorktrees])
|
||||
|
||||
const [refreshing, setRefreshing] = useState(false)
|
||||
const onRefresh = useCallback(async () => {
|
||||
if (!client || connState !== 'connected') {
|
||||
return
|
||||
}
|
||||
setRefreshing(true)
|
||||
try {
|
||||
await fetchWorktrees({ allowDuringModal: true })
|
||||
await fetchRepoMetadata({ force: true })
|
||||
} finally {
|
||||
setRefreshing(false)
|
||||
}
|
||||
}, [client, connState, fetchWorktrees, fetchRepoMetadata])
|
||||
|
||||
return { refreshing, onRefresh }
|
||||
}
|
||||
@@ -22,8 +22,7 @@ type RefreshArgs = Parameters<typeof startHostWorktreeRefresh>[0]
|
||||
|
||||
describe('startHostWorktreeRefresh', () => {
|
||||
let eventListener: ((payload: unknown) => void) | null
|
||||
let fetchWorktrees: ReturnType<typeof vi.fn<RefreshArgs['fetchWorktrees']>>
|
||||
let fetchRepoMetadata: ReturnType<typeof vi.fn<RefreshArgs['fetchRepoMetadata']>>
|
||||
let refreshWorktrees: ReturnType<typeof vi.fn<RefreshArgs['refreshWorktrees']>>
|
||||
let syncViewSettings: ReturnType<typeof vi.fn<RefreshArgs['syncViewSettings']>>
|
||||
let unsubscribe: ReturnType<typeof vi.fn<() => void>>
|
||||
let stop: (() => void) | null
|
||||
@@ -34,8 +33,7 @@ describe('startHostWorktreeRefresh', () => {
|
||||
appState.listener = null
|
||||
appState.remove.mockClear()
|
||||
eventListener = null
|
||||
fetchWorktrees = vi.fn<RefreshArgs['fetchWorktrees']>().mockResolvedValue(undefined)
|
||||
fetchRepoMetadata = vi.fn<RefreshArgs['fetchRepoMetadata']>().mockResolvedValue(undefined)
|
||||
refreshWorktrees = vi.fn<RefreshArgs['refreshWorktrees']>()
|
||||
syncViewSettings = vi.fn<RefreshArgs['syncViewSettings']>().mockResolvedValue(undefined)
|
||||
unsubscribe = vi.fn<() => void>()
|
||||
stop = null
|
||||
@@ -52,8 +50,7 @@ describe('startHostWorktreeRefresh', () => {
|
||||
})
|
||||
stop = startHostWorktreeRefresh({
|
||||
client,
|
||||
fetchWorktrees,
|
||||
fetchRepoMetadata,
|
||||
refreshWorktrees,
|
||||
syncViewSettings,
|
||||
sidebarFeed,
|
||||
appState: AppState
|
||||
@@ -65,77 +62,22 @@ describe('startHostWorktreeRefresh', () => {
|
||||
vi.useRealTimers()
|
||||
})
|
||||
|
||||
it('keeps the worktree poll active but skips ticks while backgrounded', async () => {
|
||||
it('reads nothing on change notices while hidden and catches up once on return', () => {
|
||||
start()
|
||||
expect(fetchWorktrees).toHaveBeenCalledTimes(1)
|
||||
|
||||
appState.currentState = 'background'
|
||||
await vi.advanceTimersByTimeAsync(6_000)
|
||||
expect(fetchWorktrees).toHaveBeenCalledTimes(1)
|
||||
|
||||
appState.currentState = 'active'
|
||||
await vi.advanceTimersByTimeAsync(3_000)
|
||||
expect(fetchWorktrees).toHaveBeenCalledTimes(2)
|
||||
|
||||
stop?.()
|
||||
stop = null
|
||||
fetchWorktrees.mockClear()
|
||||
eventListener?.({ type: 'worktreesChanged', repoId: 'repo-1' })
|
||||
await vi.advanceTimersByTimeAsync(3_000)
|
||||
expect(fetchWorktrees).not.toHaveBeenCalled()
|
||||
expect(unsubscribe).toHaveBeenCalledOnce()
|
||||
expect(appState.remove).toHaveBeenCalledOnce()
|
||||
})
|
||||
|
||||
it('refreshes worktrees on worktreesChanged and both snapshots after stream replay', () => {
|
||||
start()
|
||||
fetchWorktrees.mockClear()
|
||||
fetchRepoMetadata.mockClear()
|
||||
|
||||
eventListener?.({ type: 'worktreesChanged', repoId: 'repo-1' })
|
||||
expect(fetchWorktrees).toHaveBeenCalledTimes(1)
|
||||
|
||||
eventListener?.({ type: 'ready', subscriptionId: 'events-1' })
|
||||
eventListener?.({ type: 'ready', subscriptionId: 'events-2' })
|
||||
expect(fetchWorktrees).toHaveBeenCalledTimes(2)
|
||||
expect(fetchRepoMetadata).toHaveBeenCalledWith({ force: true, queueIfInFlight: true })
|
||||
})
|
||||
|
||||
it('reads nothing on change notices while hidden and catches up once on return', async () => {
|
||||
start()
|
||||
fetchWorktrees.mockClear()
|
||||
fetchRepoMetadata.mockClear()
|
||||
syncViewSettings.mockClear()
|
||||
|
||||
appState.currentState = 'background'
|
||||
eventListener?.({ type: 'worktreesChanged', repoId: 'repo-1' })
|
||||
eventListener?.({ type: 'reposChanged' })
|
||||
eventListener?.({ type: 'workspaceViewChanged' })
|
||||
eventListener?.({
|
||||
type: 'sshStateChanged',
|
||||
targetId: 'ssh-1',
|
||||
state: { targetId: 'ssh-1', status: 'connected' }
|
||||
})
|
||||
await vi.advanceTimersByTimeAsync(10_000)
|
||||
expect(fetchWorktrees).not.toHaveBeenCalled()
|
||||
expect(fetchRepoMetadata).not.toHaveBeenCalled()
|
||||
eventListener?.({ type: 'workspaceViewChanged' })
|
||||
expect(syncViewSettings).not.toHaveBeenCalled()
|
||||
|
||||
appState.currentState = 'active'
|
||||
appState.listener?.('active')
|
||||
expect(fetchWorktrees).toHaveBeenCalledExactlyOnceWith({
|
||||
allowDuringModal: true,
|
||||
queueIfInFlight: true
|
||||
})
|
||||
expect(fetchRepoMetadata).toHaveBeenCalledExactlyOnceWith({
|
||||
force: true,
|
||||
queueIfInFlight: true
|
||||
})
|
||||
expect(syncViewSettings).toHaveBeenCalledExactlyOnceWith({ resend: true })
|
||||
})
|
||||
|
||||
describe('with the host’s sidebar feed', () => {
|
||||
it('reads it at start, on every ready and on foreground, and never polls', async () => {
|
||||
it('reads it at start, on every ready and on foreground', () => {
|
||||
const sidebarFeed = { refresh: vi.fn(), changed: vi.fn(), onInstalled: vi.fn(() => () => {}) }
|
||||
start(sidebarFeed)
|
||||
expect(sidebarFeed.refresh).toHaveBeenCalledTimes(1)
|
||||
@@ -146,10 +88,6 @@ describe('startHostWorktreeRefresh', () => {
|
||||
expect(syncViewSettings).toHaveBeenCalledWith({ resend: true })
|
||||
appState.listener?.('active')
|
||||
expect(sidebarFeed.refresh).toHaveBeenCalledTimes(4)
|
||||
|
||||
fetchWorktrees.mockClear()
|
||||
await vi.advanceTimersByTimeAsync(30_000)
|
||||
expect(fetchWorktrees).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -4,12 +4,6 @@ import {
|
||||
} from '../transport/shared-client-events'
|
||||
import type { HostSidebarFeedTriggers } from '../host-screen/host-sidebar-feed-connection'
|
||||
|
||||
// Also the floor between change-driven reads, which arrive per agent step (Home throttles the same).
|
||||
const WORKTREE_REFRESH_MS = 3000
|
||||
|
||||
type WorktreeRefreshOptions = { allowDuringModal?: boolean; queueIfInFlight?: boolean }
|
||||
type RepoRefreshOptions = { force?: boolean; queueIfInFlight?: boolean }
|
||||
|
||||
// Keep lifecycle callers independent of RN types.
|
||||
type HostAppState = {
|
||||
currentState: string
|
||||
@@ -18,8 +12,8 @@ type HostAppState = {
|
||||
|
||||
type HostWorktreeRefreshArgs = {
|
||||
client: SharedClientEventsClient
|
||||
fetchWorktrees: (options?: WorktreeRefreshOptions) => Promise<void>
|
||||
fetchRepoMetadata: (options?: RepoRefreshOptions) => Promise<void>
|
||||
/** Reads the worktree catalog again, after any read already out. */
|
||||
refreshWorktrees: () => void
|
||||
/** Re-reads the Workspace options the phone shares with the desktop; `resend` retries unanswered changes. */
|
||||
syncViewSettings: (options?: { resend?: boolean }) => Promise<void>
|
||||
/** The host's sidebar feed, which replaces the poll: its notices say when anything changed. */
|
||||
@@ -28,80 +22,33 @@ type HostWorktreeRefreshArgs = {
|
||||
appState: HostAppState
|
||||
}
|
||||
|
||||
/**
|
||||
* The host list's reads that are not query families: the shared Workspace options and the sidebar
|
||||
* feed. The catalog, repos and server rows are queries the event router keeps fresh.
|
||||
*/
|
||||
export function startHostWorktreeRefresh({
|
||||
client,
|
||||
fetchWorktrees,
|
||||
fetchRepoMetadata,
|
||||
refreshWorktrees,
|
||||
syncViewSettings,
|
||||
sidebarFeed,
|
||||
appState
|
||||
}: HostWorktreeRefreshArgs): () => void {
|
||||
let stale = false
|
||||
let eventStreamReady = false
|
||||
// Set when a buried or backgrounded page skips notices; coming back then re-reads everything once.
|
||||
// Set when a buried or backgrounded page skips a notice; coming back then re-reads the options once.
|
||||
let owed = false
|
||||
let lastWorktreeReadAt = 0
|
||||
let trailingRead: ReturnType<typeof setTimeout> | undefined
|
||||
const hidden = (): boolean => appState.currentState !== 'active'
|
||||
|
||||
const readWorktrees = (options?: WorktreeRefreshOptions): void => {
|
||||
lastWorktreeReadAt = Date.now()
|
||||
void fetchWorktrees(options)
|
||||
}
|
||||
const readEverything = (worktreeOptions?: WorktreeRefreshOptions): void => {
|
||||
readWorktrees(worktreeOptions)
|
||||
void fetchRepoMetadata({ force: true, queueIfInFlight: true })
|
||||
void syncViewSettings({ resend: true })
|
||||
}
|
||||
const refreshWorktreesOnChange = (): void => {
|
||||
if (trailingRead) {
|
||||
return
|
||||
}
|
||||
const wait = lastWorktreeReadAt + WORKTREE_REFRESH_MS - Date.now()
|
||||
if (wait <= 0) {
|
||||
readWorktrees()
|
||||
return
|
||||
}
|
||||
trailingRead = setTimeout(() => {
|
||||
trailingRead = undefined
|
||||
if (!hidden()) {
|
||||
readWorktrees()
|
||||
}
|
||||
}, wait)
|
||||
}
|
||||
|
||||
const refreshOnForeground = (): void => {
|
||||
if (hidden()) {
|
||||
const appStateSubscription = appState.addEventListener('change', (state) => {
|
||||
if (state !== 'active' || hidden()) {
|
||||
return
|
||||
}
|
||||
sidebarFeed?.refresh()
|
||||
clearTimeout(trailingRead)
|
||||
trailingRead = undefined
|
||||
if (owed) {
|
||||
owed = false
|
||||
// Queued: a read still in flight was sent before the notices this page skipped.
|
||||
readEverything({ allowDuringModal: true, queueIfInFlight: true })
|
||||
return
|
||||
}
|
||||
readWorktrees({ allowDuringModal: true })
|
||||
void fetchRepoMetadata({ queueIfInFlight: true })
|
||||
}
|
||||
|
||||
const appStateSubscription = appState.addEventListener('change', (state) => {
|
||||
if (state === 'active') {
|
||||
refreshOnForeground()
|
||||
void syncViewSettings({ resend: true })
|
||||
}
|
||||
})
|
||||
const interval = sidebarFeed
|
||||
? null
|
||||
: setInterval(() => {
|
||||
if (hidden()) {
|
||||
return
|
||||
}
|
||||
void fetchWorktrees()
|
||||
// Older hosts emit no reposChanged; retain foreground repo polling, self-throttled to once a minute.
|
||||
void fetchRepoMetadata()
|
||||
}, WORKTREE_REFRESH_MS)
|
||||
const unsubscribe = subscribeSharedClientEvents(client, (event) => {
|
||||
if (stale) {
|
||||
return
|
||||
@@ -112,11 +59,11 @@ export function startHostWorktreeRefresh({
|
||||
const replayedAfterReconnect = eventStreamReady
|
||||
eventStreamReady = true
|
||||
if (replayedAfterReconnect) {
|
||||
// Why: client events are not queued while disconnected, so re-read both snapshots after replay.
|
||||
// Why: client events are not queued while disconnected, so re-read after replay.
|
||||
if (hidden()) {
|
||||
owed = true
|
||||
} else {
|
||||
readEverything()
|
||||
void syncViewSettings({ resend: true })
|
||||
}
|
||||
}
|
||||
return
|
||||
@@ -131,36 +78,19 @@ export function startHostWorktreeRefresh({
|
||||
}
|
||||
if (event.type === 'sidebarInputsChanged') {
|
||||
sidebarFeed?.changed(event)
|
||||
} else if (event.type === 'reposChanged') {
|
||||
// Why: folder workspace mutations publish reposChanged, and worktree.ps owns this catalog.
|
||||
refreshWorktreesOnChange()
|
||||
void fetchRepoMetadata({ force: true, queueIfInFlight: true })
|
||||
} else if (event.type === 'workspaceViewChanged') {
|
||||
void syncViewSettings()
|
||||
} else if (event.type === 'worktreesChanged') {
|
||||
refreshWorktreesOnChange()
|
||||
} else if (event.type === 'sshStateChanged') {
|
||||
// Why: host badges carry SSH health, read with the labels from ssh.listTargetSummaries.
|
||||
void fetchRepoMetadata({ force: true, queueIfInFlight: true })
|
||||
}
|
||||
})
|
||||
|
||||
// A row's live content (agents, terminals) may follow any install; worktree.ps carries it.
|
||||
const stopFollowingFeed = sidebarFeed?.onInstalled(
|
||||
() => void fetchWorktrees({ queueIfInFlight: true })
|
||||
)
|
||||
const stopFollowingFeed = sidebarFeed?.onInstalled(refreshWorktrees)
|
||||
sidebarFeed?.refresh()
|
||||
void fetchWorktrees()
|
||||
void fetchRepoMetadata({ force: true, queueIfInFlight: true })
|
||||
// A lost write predates the new lifecycle’s replayed ready; explicitly resend it on reconnect.
|
||||
void syncViewSettings({ resend: true })
|
||||
|
||||
return () => {
|
||||
stale = true
|
||||
clearTimeout(trailingRead)
|
||||
if (interval) {
|
||||
clearInterval(interval)
|
||||
}
|
||||
stopFollowingFeed?.()
|
||||
appStateSubscription.remove()
|
||||
unsubscribe()
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import { useCallback, useSyncExternalStore } from 'react'
|
||||
import { AppState } from 'react-native'
|
||||
import { readViewBase } from '../query/host-read-client'
|
||||
import type { MobileRelayHost } from '../../../src/shared/remote-runtime/relay/mobile-relay-hosts-contract'
|
||||
import type { RpcClient } from '../transport/rpc-client'
|
||||
import { relayHostsListRead } from './server-workspace-operations'
|
||||
@@ -116,10 +117,12 @@ function createFeed(client: RpcClient): RelayHostsFeed {
|
||||
}
|
||||
|
||||
export function relayHostsFeed(client: RpcClient): RelayHostsFeed {
|
||||
let feed = feeds.get(client)
|
||||
// A query's fail-fast view shares the feed its connection's screens already hold.
|
||||
const owner = readViewBase(client)
|
||||
let feed = feeds.get(owner)
|
||||
if (!feed) {
|
||||
feed = createFeed(client)
|
||||
feeds.set(client, feed)
|
||||
feed = createFeed(owner)
|
||||
feeds.set(owner, feed)
|
||||
}
|
||||
return feed
|
||||
}
|
||||
|
||||
@@ -207,31 +207,84 @@ export class WorktreeCatalogSnapshotClient {
|
||||
if (pending.admission.kind !== 'full' && pending.requestedSnapshotId !== this.snapshotId) {
|
||||
return null
|
||||
}
|
||||
if (pending.admission.kind === 'invalid') {
|
||||
const held = this.confirmedWorktrees && {
|
||||
snapshotId: this.snapshotId,
|
||||
worktrees: this.confirmedWorktrees
|
||||
}
|
||||
const next = applyWorktreeCatalogAdmission(held, pending.admission)
|
||||
if (!next) {
|
||||
this.snapshotId = null
|
||||
return null
|
||||
}
|
||||
|
||||
const { admission } = pending
|
||||
if (admission.kind === 'delta') {
|
||||
const rebuilt = this.confirmedWorktrees
|
||||
? rebuildFromBase(admission.rowsFromBase, this.confirmedWorktrees)
|
||||
: null
|
||||
if (!rebuilt) {
|
||||
this.snapshotId = null
|
||||
return null
|
||||
}
|
||||
this.confirmedWorktrees = rebuilt
|
||||
} else if (admission.kind === 'full') {
|
||||
this.confirmedWorktrees = admission.worktrees
|
||||
}
|
||||
this.snapshotId = admission.snapshotId
|
||||
this.snapshotId = next.snapshotId
|
||||
this.confirmedWorktrees = next.worktrees
|
||||
// Reassert confirmed host rows on unchanged polls too, overriding optimistic local edits.
|
||||
// A copy: the next delta indexes into these rows, so a caller sorting in place must not reorder them.
|
||||
return this.confirmedWorktrees ? [...this.confirmedWorktrees] : null
|
||||
return [...next.worktrees]
|
||||
}
|
||||
}
|
||||
|
||||
export type WorktreeCatalogSnapshot = { snapshotId: string | null; worktrees: Worktree[] }
|
||||
|
||||
/** The catalog an admitted reply describes on top of `held`; null when it can't be rebuilt from it. */
|
||||
function applyWorktreeCatalogAdmission(
|
||||
held: WorktreeCatalogSnapshot | null,
|
||||
admission: WorktreeCatalogAdmission<Worktree>
|
||||
): WorktreeCatalogSnapshot | null {
|
||||
if (admission.kind === 'full') {
|
||||
return { snapshotId: admission.snapshotId, worktrees: admission.worktrees }
|
||||
}
|
||||
if (admission.kind === 'invalid' || !held || held.snapshotId === null) {
|
||||
return null
|
||||
}
|
||||
if (admission.kind === 'unchanged') {
|
||||
return admission.snapshotId === held.snapshotId ? held : null
|
||||
}
|
||||
if (admission.baseSnapshotId !== held.snapshotId) {
|
||||
return null
|
||||
}
|
||||
const worktrees = rebuildFromBase(admission.rowsFromBase, held.worktrees)
|
||||
return worktrees && { snapshotId: admission.snapshotId, worktrees }
|
||||
}
|
||||
|
||||
/** A catalog read refused by the host, or a reply no catalog can be built from; `code` is shown verbatim (STA-3123). */
|
||||
export class WorktreeCatalogReadError extends Error {
|
||||
constructor(readonly code: string) {
|
||||
super(`worktree catalog read failed: ${code}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the desktop catalog on top of the one the caller holds: an unchanged reply returns `held`
|
||||
* itself, a delta rebuilds from its rows, and a delta that can't be rebuilt asks once in full.
|
||||
*/
|
||||
export async function readWorktreeCatalogSnapshot(
|
||||
client: RpcClient,
|
||||
held: WorktreeCatalogSnapshot | undefined
|
||||
): Promise<WorktreeCatalogSnapshot> {
|
||||
let base = held?.snapshotId ? held : null
|
||||
for (let attempt = 0; attempt < 2; attempt += 1) {
|
||||
const requestedSnapshotId = base?.snapshotId ?? null
|
||||
const catalog = await desktopCatalogRequest(client, {
|
||||
afterSnapshotId: requestedSnapshotId,
|
||||
acceptRowDelta: true
|
||||
})
|
||||
if (!catalog.accepted) {
|
||||
throw new WorktreeCatalogReadError(catalog.code)
|
||||
}
|
||||
const admission = admitWorktreeCatalogResponse(catalog.value, requestedSnapshotId)
|
||||
const next = applyWorktreeCatalogAdmission(base, admission)
|
||||
if (next) {
|
||||
return next
|
||||
}
|
||||
if (admission.kind !== 'delta') {
|
||||
break
|
||||
}
|
||||
base = null
|
||||
}
|
||||
throw new WorktreeCatalogReadError('invalid_response')
|
||||
}
|
||||
|
||||
// Null when an index falls outside the base; the caller then asks for a full catalog.
|
||||
function rebuildFromBase(rowsFromBase: (number | Worktree)[], base: Worktree[]): Worktree[] | null {
|
||||
const rows: Worktree[] = []
|
||||
|
||||
@@ -132,8 +132,7 @@ export async function openPhoneFeedList(
|
||||
const stop = startHostWorktreeRefresh({
|
||||
client: socketEventClient(socket),
|
||||
// The list reads rows and catalog from the feed alone; worktree.ps adds only card content.
|
||||
fetchWorktrees: async () => {},
|
||||
fetchRepoMetadata: async () => {},
|
||||
refreshWorktrees: () => {},
|
||||
syncViewSettings,
|
||||
sidebarFeed: {
|
||||
refresh: () => reader.refresh(),
|
||||
|
||||
Reference in New Issue
Block a user