mirror of
https://github.com/stablyai/orca.git
synced 2026-11-13 17:37:40 +00:00
feat(mobile): rebuild Home around what needs you, with actions pinned at the bottom (#36520)
This commit is contained in:
@@ -751,8 +751,6 @@
|
||||
3 test-no-wall-clock:config/scripts/reclaim-dev-electron-bundles.test.ts
|
||||
1 test-no-wall-clock:config/scripts/reclaim-release-test-caches.test.ts
|
||||
1 test-no-wall-clock:mobile/scripts/native-build-cache.test.ts
|
||||
1 test-no-wall-clock:mobile/src/components/MobileHostCard-truthful-status.test.tsx
|
||||
1 test-no-wall-clock:mobile/src/components/MobileHostCard.test.ts
|
||||
1 test-no-wall-clock:mobile/src/components/pr-sidebar/pr-comment-card-web-avatar.test.tsx
|
||||
1 test-no-wall-clock:mobile/src/session/mobile-structured-send-new-action.test.ts
|
||||
2 test-no-wall-clock:mobile/src/session/native-chat/composer/use-mobile-native-chat-message-send-draft-lifetime.test.ts
|
||||
|
||||
@@ -1208,15 +1208,6 @@
|
||||
"auto.components.mobile.PhoneCarousel.93217b41c1": {
|
||||
"ko": "워크트리 목록"
|
||||
},
|
||||
"auto.components.mobile.slides.HomeSlide.0bad5b07c8": {
|
||||
"ko": "GitHub 및 Linear"
|
||||
},
|
||||
"auto.components.mobile.slides.HomeSlide.0bc1881bc4": {
|
||||
"ko": "연결됨 · 워크트리 40개 · 활성 5개"
|
||||
},
|
||||
"auto.components.mobile.slides.HomeSlide.c0e2e9dcd9": {
|
||||
"ko": "다시 오신 것을 환영합니다"
|
||||
},
|
||||
"auto.components.mobile.slides.TerminalSlide.2defc05141": {
|
||||
"ko": "dev@mac"
|
||||
},
|
||||
|
||||
@@ -1,224 +0,0 @@
|
||||
{
|
||||
"goldenFormatVersion": 6,
|
||||
"operation": "home.host-accounts",
|
||||
"family": "home.host-accounts",
|
||||
"namedDeltas": [],
|
||||
"values": {
|
||||
"16b4c187b90c": {
|
||||
"name": "accounts.list#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "accounts.list"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-1",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"claude": {
|
||||
"accounts": [
|
||||
{
|
||||
"email": "claude@example.test",
|
||||
"id": "claude-1",
|
||||
"updatedAt": 1700000000000
|
||||
}
|
||||
],
|
||||
"activeAccountId": "claude-1"
|
||||
},
|
||||
"codex": {
|
||||
"accounts": [],
|
||||
"activeAccountId": {
|
||||
"$rpc": "null"
|
||||
}
|
||||
},
|
||||
"rateLimits": {
|
||||
"claude": {
|
||||
"$rpc": "null"
|
||||
},
|
||||
"codex": {
|
||||
"$rpc": "null"
|
||||
},
|
||||
"inactiveClaudeAccounts": [],
|
||||
"inactiveCodexAccounts": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"2525c654127c": {
|
||||
"host-1": {
|
||||
"claude": {
|
||||
"accounts": [
|
||||
{
|
||||
"email": "claude@example.test",
|
||||
"id": "claude-1",
|
||||
"updatedAt": 1700000000000
|
||||
}
|
||||
],
|
||||
"activeAccountId": "claude-1"
|
||||
},
|
||||
"codex": {
|
||||
"accounts": [],
|
||||
"activeAccountId": {
|
||||
"$rpc": "null"
|
||||
}
|
||||
},
|
||||
"rateLimits": {
|
||||
"claude": {
|
||||
"$rpc": "null"
|
||||
},
|
||||
"claudeTarget": {
|
||||
"runtime": "host",
|
||||
"wslDistro": {
|
||||
"$rpc": "null"
|
||||
}
|
||||
},
|
||||
"codex": {
|
||||
"$rpc": "null"
|
||||
},
|
||||
"codexTarget": {
|
||||
"runtime": "host",
|
||||
"wslDistro": {
|
||||
"$rpc": "null"
|
||||
}
|
||||
},
|
||||
"inactiveClaudeAccounts": [],
|
||||
"inactiveCodexAccounts": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"3bf8cb601ec3": {
|
||||
"name": "accounts.list#1",
|
||||
"ordinal": 2,
|
||||
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"accounts.list\"}"
|
||||
},
|
||||
"44136fa355b3": {},
|
||||
"78574a24b32d": {
|
||||
"name": "accounts",
|
||||
"ordinal": 3,
|
||||
"value": {
|
||||
"host-1": {
|
||||
"claude": {
|
||||
"accounts": [
|
||||
{
|
||||
"email": "claude@example.test",
|
||||
"id": "claude-1",
|
||||
"updatedAt": 1700000000000
|
||||
}
|
||||
],
|
||||
"activeAccountId": "claude-1"
|
||||
},
|
||||
"codex": {
|
||||
"accounts": [],
|
||||
"activeAccountId": {
|
||||
"$rpc": "null"
|
||||
}
|
||||
},
|
||||
"rateLimits": {
|
||||
"claude": {
|
||||
"$rpc": "null"
|
||||
},
|
||||
"claudeTarget": {
|
||||
"runtime": "host",
|
||||
"wslDistro": {
|
||||
"$rpc": "null"
|
||||
}
|
||||
},
|
||||
"codex": {
|
||||
"$rpc": "null"
|
||||
},
|
||||
"codexTarget": {
|
||||
"runtime": "host",
|
||||
"wslDistro": {
|
||||
"$rpc": "null"
|
||||
}
|
||||
},
|
||||
"inactiveClaudeAccounts": [],
|
||||
"inactiveCodexAccounts": []
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"884d6f264bfd": {
|
||||
"name": "accounts.list#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "accounts.list"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"eb79a9b3682a": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
}
|
||||
},
|
||||
"recording": {
|
||||
"scenario": "home-host-accounts",
|
||||
"checkpoints": [
|
||||
{
|
||||
"id": "accounts-pending",
|
||||
"observation": {
|
||||
"sender": ["884d6f264bfd"],
|
||||
"payloads": ["3bf8cb601ec3"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "44136fa355b3",
|
||||
"effects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "accounts-published",
|
||||
"observation": {
|
||||
"sender": ["16b4c187b90c"],
|
||||
"payloads": ["3bf8cb601ec3"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "2525c654127c",
|
||||
"effects": ["78574a24b32d"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
{
|
||||
"goldenFormatVersion": 6,
|
||||
"operation": "home.host-stats",
|
||||
"family": "home.host-stats",
|
||||
"namedDeltas": [],
|
||||
"values": {
|
||||
"44136fa355b3": {},
|
||||
"94616379becc": {
|
||||
"name": "stats",
|
||||
"ordinal": 3,
|
||||
"value": {
|
||||
"host-1": {
|
||||
"activeWorktrees": 1,
|
||||
"totalWorktrees": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
"9698bf4d8a23": {
|
||||
"name": "stats.summary#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "stats.summary"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-1",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"activeWorktrees": 1,
|
||||
"totalWorktrees": 3
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"9a84a7559023": {
|
||||
"host-1": {
|
||||
"activeWorktrees": 1,
|
||||
"totalWorktrees": 3
|
||||
}
|
||||
},
|
||||
"cf7f6575cf73": {
|
||||
"name": "stats.summary#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "stats.summary"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"d66672644411": {
|
||||
"name": "stats.summary#1",
|
||||
"ordinal": 2,
|
||||
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"stats.summary\"}"
|
||||
},
|
||||
"eb79a9b3682a": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
}
|
||||
},
|
||||
"recording": {
|
||||
"scenario": "home-host-stats",
|
||||
"checkpoints": [
|
||||
{
|
||||
"id": "stats-pending",
|
||||
"observation": {
|
||||
"sender": ["cf7f6575cf73"],
|
||||
"payloads": ["d66672644411"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "44136fa355b3",
|
||||
"effects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settled",
|
||||
"observation": {
|
||||
"sender": ["9698bf4d8a23"],
|
||||
"payloads": ["d66672644411"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "9a84a7559023",
|
||||
"effects": ["94616379becc"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,684 +0,0 @@
|
||||
{
|
||||
"goldenFormatVersion": 6,
|
||||
"operation": "home.host-accounts",
|
||||
"family": "home.host-accounts",
|
||||
"namedDeltas": [],
|
||||
"values": {
|
||||
"11f6bbd8152f": {
|
||||
"name": "accounts.list#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "accounts.list"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-1",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"error": {
|
||||
"message": "inner refused"
|
||||
},
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"16b4c187b90c": {
|
||||
"name": "accounts.list#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "accounts.list"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-1",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"claude": {
|
||||
"accounts": [
|
||||
{
|
||||
"email": "claude@example.test",
|
||||
"id": "claude-1",
|
||||
"updatedAt": 1700000000000
|
||||
}
|
||||
],
|
||||
"activeAccountId": "claude-1"
|
||||
},
|
||||
"codex": {
|
||||
"accounts": [],
|
||||
"activeAccountId": {
|
||||
"$rpc": "null"
|
||||
}
|
||||
},
|
||||
"rateLimits": {
|
||||
"claude": {
|
||||
"$rpc": "null"
|
||||
},
|
||||
"codex": {
|
||||
"$rpc": "null"
|
||||
},
|
||||
"inactiveClaudeAccounts": [],
|
||||
"inactiveCodexAccounts": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"2525c654127c": {
|
||||
"host-1": {
|
||||
"claude": {
|
||||
"accounts": [
|
||||
{
|
||||
"email": "claude@example.test",
|
||||
"id": "claude-1",
|
||||
"updatedAt": 1700000000000
|
||||
}
|
||||
],
|
||||
"activeAccountId": "claude-1"
|
||||
},
|
||||
"codex": {
|
||||
"accounts": [],
|
||||
"activeAccountId": {
|
||||
"$rpc": "null"
|
||||
}
|
||||
},
|
||||
"rateLimits": {
|
||||
"claude": {
|
||||
"$rpc": "null"
|
||||
},
|
||||
"claudeTarget": {
|
||||
"runtime": "host",
|
||||
"wslDistro": {
|
||||
"$rpc": "null"
|
||||
}
|
||||
},
|
||||
"codex": {
|
||||
"$rpc": "null"
|
||||
},
|
||||
"codexTarget": {
|
||||
"runtime": "host",
|
||||
"wslDistro": {
|
||||
"$rpc": "null"
|
||||
}
|
||||
},
|
||||
"inactiveClaudeAccounts": [],
|
||||
"inactiveCodexAccounts": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"2812f0b1ec77": {
|
||||
"name": "accounts.list#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "accounts.list"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"error": {
|
||||
"code": "method_not_found",
|
||||
"message": "Unknown method"
|
||||
},
|
||||
"id": "frame-1",
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"3439bc68151d": {
|
||||
"name": "accounts.list#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "accounts.list"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "rejected",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"error": {
|
||||
"category": "Error",
|
||||
"message": "transport failure",
|
||||
"isRpcDeliveryUnknown": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"3bf8cb601ec3": {
|
||||
"name": "accounts.list#1",
|
||||
"ordinal": 2,
|
||||
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"accounts.list\"}"
|
||||
},
|
||||
"44136fa355b3": {},
|
||||
"4a1c397d346b": {
|
||||
"name": "accounts.list#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "accounts.list"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-1",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"error": "inner refused",
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"564b115f0a17": {
|
||||
"name": "accounts.list#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "accounts.list"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "rejected",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"error": {
|
||||
"category": "Error",
|
||||
"message": "",
|
||||
"isRpcDeliveryUnknown": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"6d54d83570d0": {
|
||||
"name": "accounts.list#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "accounts.list"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-1",
|
||||
"ok": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"71d054262f17": {
|
||||
"name": "accounts.list#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "accounts.list"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"error": {
|
||||
"code": "refused",
|
||||
"message": "outer refused"
|
||||
},
|
||||
"id": "frame-1",
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"78574a24b32d": {
|
||||
"name": "accounts",
|
||||
"ordinal": 3,
|
||||
"value": {
|
||||
"host-1": {
|
||||
"claude": {
|
||||
"accounts": [
|
||||
{
|
||||
"email": "claude@example.test",
|
||||
"id": "claude-1",
|
||||
"updatedAt": 1700000000000
|
||||
}
|
||||
],
|
||||
"activeAccountId": "claude-1"
|
||||
},
|
||||
"codex": {
|
||||
"accounts": [],
|
||||
"activeAccountId": {
|
||||
"$rpc": "null"
|
||||
}
|
||||
},
|
||||
"rateLimits": {
|
||||
"claude": {
|
||||
"$rpc": "null"
|
||||
},
|
||||
"claudeTarget": {
|
||||
"runtime": "host",
|
||||
"wslDistro": {
|
||||
"$rpc": "null"
|
||||
}
|
||||
},
|
||||
"codex": {
|
||||
"$rpc": "null"
|
||||
},
|
||||
"codexTarget": {
|
||||
"runtime": "host",
|
||||
"wslDistro": {
|
||||
"$rpc": "null"
|
||||
}
|
||||
},
|
||||
"inactiveClaudeAccounts": [],
|
||||
"inactiveCodexAccounts": []
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"884d6f264bfd": {
|
||||
"name": "accounts.list#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "accounts.list"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"a2761753b29a": {
|
||||
"name": "accounts.list#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "accounts.list"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"error": {
|
||||
"code": "refused",
|
||||
"message": ""
|
||||
},
|
||||
"id": "frame-1",
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"d308707eb973": {
|
||||
"name": "accounts.list#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "accounts.list"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-1",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"error": "refused"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"d953c94f3173": {
|
||||
"name": "accounts.list#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "accounts.list"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-1",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"$rpc": "null"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"eb79a9b3682a": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
}
|
||||
},
|
||||
"recording": {
|
||||
"scenario": "matrix-home.host-accounts-accounts.list-1",
|
||||
"checkpoints": [
|
||||
{
|
||||
"id": "home-host-accounts.prelude:accounts-pending",
|
||||
"observation": {
|
||||
"sender": ["884d6f264bfd"],
|
||||
"payloads": ["3bf8cb601ec3"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "44136fa355b3",
|
||||
"effects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "home-host-accounts.normal:accounts-published",
|
||||
"observation": {
|
||||
"sender": ["16b4c187b90c"],
|
||||
"payloads": ["3bf8cb601ec3"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "2525c654127c",
|
||||
"effects": ["78574a24b32d"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "home-host-accounts.result-absent:accounts-published",
|
||||
"observation": {
|
||||
"sender": ["6d54d83570d0"],
|
||||
"payloads": ["3bf8cb601ec3"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "44136fa355b3",
|
||||
"effects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "home-host-accounts.result-null:accounts-published",
|
||||
"observation": {
|
||||
"sender": ["d953c94f3173"],
|
||||
"payloads": ["3bf8cb601ec3"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "44136fa355b3",
|
||||
"effects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "home-host-accounts.inner-ok-missing:accounts-published",
|
||||
"observation": {
|
||||
"sender": ["d308707eb973"],
|
||||
"payloads": ["3bf8cb601ec3"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "44136fa355b3",
|
||||
"effects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "home-host-accounts.inner-false-string-error:accounts-published",
|
||||
"observation": {
|
||||
"sender": ["4a1c397d346b"],
|
||||
"payloads": ["3bf8cb601ec3"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "44136fa355b3",
|
||||
"effects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "home-host-accounts.inner-false-object-error:accounts-published",
|
||||
"observation": {
|
||||
"sender": ["11f6bbd8152f"],
|
||||
"payloads": ["3bf8cb601ec3"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "44136fa355b3",
|
||||
"effects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "home-host-accounts.outer-refused:accounts-published",
|
||||
"observation": {
|
||||
"sender": ["71d054262f17"],
|
||||
"payloads": ["3bf8cb601ec3"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "44136fa355b3",
|
||||
"effects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "home-host-accounts.outer-refused-no-message:accounts-published",
|
||||
"observation": {
|
||||
"sender": ["a2761753b29a"],
|
||||
"payloads": ["3bf8cb601ec3"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "44136fa355b3",
|
||||
"effects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "home-host-accounts.method-not-found:accounts-published",
|
||||
"observation": {
|
||||
"sender": ["2812f0b1ec77"],
|
||||
"payloads": ["3bf8cb601ec3"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "44136fa355b3",
|
||||
"effects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "home-host-accounts.transport-rejection:accounts-published",
|
||||
"observation": {
|
||||
"sender": ["3439bc68151d"],
|
||||
"payloads": ["3bf8cb601ec3"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "44136fa355b3",
|
||||
"effects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "home-host-accounts.transport-rejection-no-message:accounts-published",
|
||||
"observation": {
|
||||
"sender": ["564b115f0a17"],
|
||||
"payloads": ["3bf8cb601ec3"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "44136fa355b3",
|
||||
"effects": []
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,666 +0,0 @@
|
||||
{
|
||||
"goldenFormatVersion": 6,
|
||||
"operation": "home.host-stats",
|
||||
"family": "home.host-stats",
|
||||
"namedDeltas": [],
|
||||
"values": {
|
||||
"1ac8bfa526f1": {
|
||||
"name": "stats.summary#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "stats.summary"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "rejected",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"error": {
|
||||
"category": "Error",
|
||||
"message": "",
|
||||
"isRpcDeliveryUnknown": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"4211139e96bd": {
|
||||
"name": "stats.summary#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "stats.summary"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-1",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"error": {
|
||||
"message": "inner refused"
|
||||
},
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"44136fa355b3": {},
|
||||
"556edbbc8712": {
|
||||
"host-1": {
|
||||
"$rpc": "null"
|
||||
}
|
||||
},
|
||||
"5691d0e49e5a": {
|
||||
"name": "stats.summary#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "stats.summary"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-1",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"error": "refused"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"570b0ab475a2": {
|
||||
"name": "stats",
|
||||
"ordinal": 3,
|
||||
"value": {
|
||||
"host-1": {
|
||||
"$rpc": "null"
|
||||
}
|
||||
}
|
||||
},
|
||||
"5b3b2e4ed756": {
|
||||
"name": "stats",
|
||||
"ordinal": 3,
|
||||
"value": {
|
||||
"host-1": {
|
||||
"error": "refused"
|
||||
}
|
||||
}
|
||||
},
|
||||
"5f68a32120c1": {
|
||||
"name": "stats.summary#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "stats.summary"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"error": {
|
||||
"code": "refused",
|
||||
"message": ""
|
||||
},
|
||||
"id": "frame-1",
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"6437faa290bd": {
|
||||
"name": "stats",
|
||||
"ordinal": 3,
|
||||
"value": {
|
||||
"host-1": {
|
||||
"error": {
|
||||
"message": "inner refused"
|
||||
},
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"8c4863e9051a": {
|
||||
"name": "stats.summary#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "stats.summary"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"error": {
|
||||
"code": "refused",
|
||||
"message": "outer refused"
|
||||
},
|
||||
"id": "frame-1",
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"94616379becc": {
|
||||
"name": "stats",
|
||||
"ordinal": 3,
|
||||
"value": {
|
||||
"host-1": {
|
||||
"activeWorktrees": 1,
|
||||
"totalWorktrees": 3
|
||||
}
|
||||
}
|
||||
},
|
||||
"9698bf4d8a23": {
|
||||
"name": "stats.summary#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "stats.summary"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-1",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"activeWorktrees": 1,
|
||||
"totalWorktrees": 3
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"9a84a7559023": {
|
||||
"host-1": {
|
||||
"activeWorktrees": 1,
|
||||
"totalWorktrees": 3
|
||||
}
|
||||
},
|
||||
"9c34abfa17e7": {
|
||||
"host-1": {
|
||||
"error": {
|
||||
"message": "inner refused"
|
||||
},
|
||||
"ok": false
|
||||
}
|
||||
},
|
||||
"af5ba5c22147": {
|
||||
"name": "stats.summary#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "stats.summary"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-1",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"$rpc": "null"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"b474d2a02a6a": {
|
||||
"host-1": {
|
||||
"error": "refused"
|
||||
}
|
||||
},
|
||||
"c5bfd5f18460": {
|
||||
"host-1": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
"c60ee6b032a7": {
|
||||
"name": "stats.summary#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "stats.summary"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"error": {
|
||||
"code": "method_not_found",
|
||||
"message": "Unknown method"
|
||||
},
|
||||
"id": "frame-1",
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"cf7f6575cf73": {
|
||||
"name": "stats.summary#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "stats.summary"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"d3ca24e5a81b": {
|
||||
"name": "stats.summary#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "stats.summary"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-1",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"error": "inner refused",
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"d548975ae3f5": {
|
||||
"name": "stats",
|
||||
"ordinal": 3,
|
||||
"value": {
|
||||
"host-1": {
|
||||
"error": "inner refused",
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"d66672644411": {
|
||||
"name": "stats.summary#1",
|
||||
"ordinal": 2,
|
||||
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"stats.summary\"}"
|
||||
},
|
||||
"db6e2ff86f92": {
|
||||
"name": "stats",
|
||||
"ordinal": 3,
|
||||
"value": {
|
||||
"host-1": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dccf8acd7784": {
|
||||
"name": "stats.summary#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "stats.summary"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-1",
|
||||
"ok": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"e05986a4b6e2": {
|
||||
"host-1": {
|
||||
"error": "inner refused",
|
||||
"ok": false
|
||||
}
|
||||
},
|
||||
"eb79a9b3682a": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
"fa85158f27a0": {
|
||||
"name": "stats.summary#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "stats.summary"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "rejected",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"error": {
|
||||
"category": "Error",
|
||||
"message": "transport failure",
|
||||
"isRpcDeliveryUnknown": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"recording": {
|
||||
"scenario": "matrix-home.host-stats-stats.summary-1",
|
||||
"checkpoints": [
|
||||
{
|
||||
"id": "home-host-stats.prelude:stats-pending",
|
||||
"observation": {
|
||||
"sender": ["cf7f6575cf73"],
|
||||
"payloads": ["d66672644411"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "44136fa355b3",
|
||||
"effects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "home-host-stats.normal:settled",
|
||||
"observation": {
|
||||
"sender": ["9698bf4d8a23"],
|
||||
"payloads": ["d66672644411"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "9a84a7559023",
|
||||
"effects": ["94616379becc"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "home-host-stats.result-absent:settled",
|
||||
"observation": {
|
||||
"sender": ["dccf8acd7784"],
|
||||
"payloads": ["d66672644411"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "c5bfd5f18460",
|
||||
"effects": ["db6e2ff86f92"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "home-host-stats.result-null:settled",
|
||||
"observation": {
|
||||
"sender": ["af5ba5c22147"],
|
||||
"payloads": ["d66672644411"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "556edbbc8712",
|
||||
"effects": ["570b0ab475a2"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "home-host-stats.inner-ok-missing:settled",
|
||||
"observation": {
|
||||
"sender": ["5691d0e49e5a"],
|
||||
"payloads": ["d66672644411"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "b474d2a02a6a",
|
||||
"effects": ["5b3b2e4ed756"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "home-host-stats.inner-false-string-error:settled",
|
||||
"observation": {
|
||||
"sender": ["d3ca24e5a81b"],
|
||||
"payloads": ["d66672644411"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "e05986a4b6e2",
|
||||
"effects": ["d548975ae3f5"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "home-host-stats.inner-false-object-error:settled",
|
||||
"observation": {
|
||||
"sender": ["4211139e96bd"],
|
||||
"payloads": ["d66672644411"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "9c34abfa17e7",
|
||||
"effects": ["6437faa290bd"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "home-host-stats.outer-refused:settled",
|
||||
"observation": {
|
||||
"sender": ["8c4863e9051a"],
|
||||
"payloads": ["d66672644411"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "44136fa355b3",
|
||||
"effects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "home-host-stats.outer-refused-no-message:settled",
|
||||
"observation": {
|
||||
"sender": ["5f68a32120c1"],
|
||||
"payloads": ["d66672644411"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "44136fa355b3",
|
||||
"effects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "home-host-stats.method-not-found:settled",
|
||||
"observation": {
|
||||
"sender": ["c60ee6b032a7"],
|
||||
"payloads": ["d66672644411"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "44136fa355b3",
|
||||
"effects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "home-host-stats.transport-rejection:settled",
|
||||
"observation": {
|
||||
"sender": ["fa85158f27a0"],
|
||||
"payloads": ["d66672644411"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "44136fa355b3",
|
||||
"effects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "home-host-stats.transport-rejection-no-message:settled",
|
||||
"observation": {
|
||||
"sender": ["1ac8bfa526f1"],
|
||||
"payloads": ["d66672644411"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "44136fa355b3",
|
||||
"effects": []
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,719 +0,0 @@
|
||||
{
|
||||
"goldenFormatVersion": 6,
|
||||
"operation": "settings.home-providers",
|
||||
"family": "settings.home-providers",
|
||||
"namedDeltas": [],
|
||||
"values": {
|
||||
"015b0f5c6e12": {
|
||||
"name": "linear.status#1",
|
||||
"ordinal": 3,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "linear.status"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"error": {
|
||||
"code": "method_not_found",
|
||||
"message": "Unknown method"
|
||||
},
|
||||
"id": "frame-3",
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"036d405abb7b": {
|
||||
"name": "linear.status#1",
|
||||
"ordinal": 3,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "linear.status"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"error": "refused"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"0a5b778e224a": {
|
||||
"name": "linear.status#1",
|
||||
"ordinal": 3,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "linear.status"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"connected": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"362f21a2ec36": {
|
||||
"name": "linear.status#1",
|
||||
"ordinal": 3,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "linear.status"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"403872f494c7": {
|
||||
"name": "linear.status#1",
|
||||
"ordinal": 3,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "linear.status"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"error": {
|
||||
"code": "refused",
|
||||
"message": ""
|
||||
},
|
||||
"id": "frame-3",
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"44136fa355b3": {},
|
||||
"4777b5dc4e66": {
|
||||
"name": "preflight.check#1",
|
||||
"ordinal": 2,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.check"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"58995bbfcb17": {
|
||||
"name": "linear.status#1",
|
||||
"ordinal": 3,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "linear.status"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"776ed8372613": {
|
||||
"name": "preflight.check#1",
|
||||
"ordinal": 2,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.check"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-2",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"glab": {
|
||||
"installed": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"79b8c1b0d1d1": {
|
||||
"host-1": ["github"]
|
||||
},
|
||||
"7b7b4c5e7804": {
|
||||
"name": "linear.status#1",
|
||||
"ordinal": 3,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "linear.status"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"error": {
|
||||
"message": "inner refused"
|
||||
},
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"805497bacbd3": {
|
||||
"name": "linear.status#1",
|
||||
"ordinal": 3,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "linear.status"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"error": "inner refused",
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"89e9993ccc21": {
|
||||
"name": "linear.status#1",
|
||||
"ordinal": 3,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "linear.status"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "rejected",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"error": {
|
||||
"category": "Error",
|
||||
"message": "transport failure",
|
||||
"isRpcDeliveryUnknown": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"8fce29bab175": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-1",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"settings": {
|
||||
"defaultTuiAgent": "codex",
|
||||
"disabledTuiAgents": ["claude"],
|
||||
"hostSettingOverrides": {},
|
||||
"prBotAuthorOverrides": ["bot-user"],
|
||||
"visibleTaskProviders": ["github", "linear"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"9037ac778ace": {
|
||||
"name": "providers",
|
||||
"ordinal": 7,
|
||||
"value": {
|
||||
"host-1": ["github"]
|
||||
}
|
||||
},
|
||||
"908e43fdefae": {
|
||||
"name": "preflight.check#1",
|
||||
"ordinal": 5,
|
||||
"json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.check\"}"
|
||||
},
|
||||
"975f2ff9a730": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 4,
|
||||
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
|
||||
},
|
||||
"a8d3d66241fe": {
|
||||
"name": "linear.status#1",
|
||||
"ordinal": 3,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "linear.status"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"$rpc": "null"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"aa3490f8f6f8": {
|
||||
"name": "linear.status#1",
|
||||
"ordinal": 6,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"linear.status\"}"
|
||||
},
|
||||
"ad3f1d9d51b1": {
|
||||
"name": "linear.status#1",
|
||||
"ordinal": 3,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "linear.status"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"error": {
|
||||
"code": "refused",
|
||||
"message": "outer refused"
|
||||
},
|
||||
"id": "frame-3",
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"adf2776f4eea": {
|
||||
"name": "linear.status#1",
|
||||
"ordinal": 3,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "linear.status"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "rejected",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"error": {
|
||||
"category": "Error",
|
||||
"message": "",
|
||||
"isRpcDeliveryUnknown": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"eb79a9b3682a": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
"ec8967c2d560": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"recording": {
|
||||
"scenario": "matrix-settings.home-providers-linear.status-1",
|
||||
"checkpoints": [
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.prelude:settings-pending",
|
||||
"observation": {
|
||||
"sender": ["ec8967c2d560", "4777b5dc4e66", "58995bbfcb17"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "44136fa355b3",
|
||||
"effects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.normal:settled",
|
||||
"observation": {
|
||||
"sender": ["8fce29bab175", "776ed8372613", "0a5b778e224a"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.result-absent:settled",
|
||||
"observation": {
|
||||
"sender": ["8fce29bab175", "776ed8372613", "362f21a2ec36"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.result-null:settled",
|
||||
"observation": {
|
||||
"sender": ["8fce29bab175", "776ed8372613", "a8d3d66241fe"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.inner-ok-missing:settled",
|
||||
"observation": {
|
||||
"sender": ["8fce29bab175", "776ed8372613", "036d405abb7b"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.inner-false-string-error:settled",
|
||||
"observation": {
|
||||
"sender": ["8fce29bab175", "776ed8372613", "805497bacbd3"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.inner-false-object-error:settled",
|
||||
"observation": {
|
||||
"sender": ["8fce29bab175", "776ed8372613", "7b7b4c5e7804"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.outer-refused:settled",
|
||||
"observation": {
|
||||
"sender": ["8fce29bab175", "776ed8372613", "ad3f1d9d51b1"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.outer-refused-no-message:settled",
|
||||
"observation": {
|
||||
"sender": ["8fce29bab175", "776ed8372613", "403872f494c7"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.method-not-found:settled",
|
||||
"observation": {
|
||||
"sender": ["8fce29bab175", "776ed8372613", "015b0f5c6e12"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.transport-rejection:settled",
|
||||
"observation": {
|
||||
"sender": ["8fce29bab175", "776ed8372613", "89e9993ccc21"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.transport-rejection-no-message:settled",
|
||||
"observation": {
|
||||
"sender": ["8fce29bab175", "776ed8372613", "adf2776f4eea"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,719 +0,0 @@
|
||||
{
|
||||
"goldenFormatVersion": 6,
|
||||
"operation": "settings.home-providers",
|
||||
"family": "settings.home-providers",
|
||||
"namedDeltas": [],
|
||||
"values": {
|
||||
"00a1ac4d876f": {
|
||||
"name": "preflight.check#1",
|
||||
"ordinal": 2,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.check"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-2",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"error": "inner refused",
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"04f6f989667f": {
|
||||
"name": "preflight.check#1",
|
||||
"ordinal": 2,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.check"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"error": {
|
||||
"code": "method_not_found",
|
||||
"message": "Unknown method"
|
||||
},
|
||||
"id": "frame-2",
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"0a5b778e224a": {
|
||||
"name": "linear.status#1",
|
||||
"ordinal": 3,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "linear.status"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"connected": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"32bc15340619": {
|
||||
"name": "preflight.check#1",
|
||||
"ordinal": 2,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.check"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"error": {
|
||||
"code": "refused",
|
||||
"message": "outer refused"
|
||||
},
|
||||
"id": "frame-2",
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"44136fa355b3": {},
|
||||
"4777b5dc4e66": {
|
||||
"name": "preflight.check#1",
|
||||
"ordinal": 2,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.check"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"58995bbfcb17": {
|
||||
"name": "linear.status#1",
|
||||
"ordinal": 3,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "linear.status"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"59eef396a6b8": {
|
||||
"name": "preflight.check#1",
|
||||
"ordinal": 2,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.check"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "rejected",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"error": {
|
||||
"category": "Error",
|
||||
"message": "",
|
||||
"isRpcDeliveryUnknown": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"73d2f90315b5": {
|
||||
"name": "preflight.check#1",
|
||||
"ordinal": 2,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.check"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-2",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"$rpc": "null"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"776ed8372613": {
|
||||
"name": "preflight.check#1",
|
||||
"ordinal": 2,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.check"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-2",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"glab": {
|
||||
"installed": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"79b8c1b0d1d1": {
|
||||
"host-1": ["github"]
|
||||
},
|
||||
"86350c837240": {
|
||||
"name": "preflight.check#1",
|
||||
"ordinal": 2,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.check"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-2",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"error": "refused"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"88df61a1af58": {
|
||||
"name": "preflight.check#1",
|
||||
"ordinal": 2,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.check"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "rejected",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"error": {
|
||||
"category": "Error",
|
||||
"message": "transport failure",
|
||||
"isRpcDeliveryUnknown": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"8fce29bab175": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-1",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"settings": {
|
||||
"defaultTuiAgent": "codex",
|
||||
"disabledTuiAgents": ["claude"],
|
||||
"hostSettingOverrides": {},
|
||||
"prBotAuthorOverrides": ["bot-user"],
|
||||
"visibleTaskProviders": ["github", "linear"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"9037ac778ace": {
|
||||
"name": "providers",
|
||||
"ordinal": 7,
|
||||
"value": {
|
||||
"host-1": ["github"]
|
||||
}
|
||||
},
|
||||
"908e43fdefae": {
|
||||
"name": "preflight.check#1",
|
||||
"ordinal": 5,
|
||||
"json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.check\"}"
|
||||
},
|
||||
"975f2ff9a730": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 4,
|
||||
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
|
||||
},
|
||||
"a7c1d986eef2": {
|
||||
"name": "preflight.check#1",
|
||||
"ordinal": 2,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.check"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-2",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"error": {
|
||||
"message": "inner refused"
|
||||
},
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"aa3490f8f6f8": {
|
||||
"name": "linear.status#1",
|
||||
"ordinal": 6,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"linear.status\"}"
|
||||
},
|
||||
"ad454c0116cc": {
|
||||
"name": "preflight.check#1",
|
||||
"ordinal": 2,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.check"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"error": {
|
||||
"code": "refused",
|
||||
"message": ""
|
||||
},
|
||||
"id": "frame-2",
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"cef9af4132f7": {
|
||||
"name": "preflight.check#1",
|
||||
"ordinal": 2,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.check"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-2",
|
||||
"ok": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"eb79a9b3682a": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
"ec8967c2d560": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"recording": {
|
||||
"scenario": "matrix-settings.home-providers-preflight.check-1",
|
||||
"checkpoints": [
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.prelude:settings-pending",
|
||||
"observation": {
|
||||
"sender": ["ec8967c2d560", "4777b5dc4e66", "58995bbfcb17"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "44136fa355b3",
|
||||
"effects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.normal:settled",
|
||||
"observation": {
|
||||
"sender": ["8fce29bab175", "776ed8372613", "0a5b778e224a"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.result-absent:settled",
|
||||
"observation": {
|
||||
"sender": ["8fce29bab175", "cef9af4132f7", "0a5b778e224a"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.result-null:settled",
|
||||
"observation": {
|
||||
"sender": ["8fce29bab175", "73d2f90315b5", "0a5b778e224a"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.inner-ok-missing:settled",
|
||||
"observation": {
|
||||
"sender": ["8fce29bab175", "86350c837240", "0a5b778e224a"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.inner-false-string-error:settled",
|
||||
"observation": {
|
||||
"sender": ["8fce29bab175", "00a1ac4d876f", "0a5b778e224a"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.inner-false-object-error:settled",
|
||||
"observation": {
|
||||
"sender": ["8fce29bab175", "a7c1d986eef2", "0a5b778e224a"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.outer-refused:settled",
|
||||
"observation": {
|
||||
"sender": ["8fce29bab175", "32bc15340619", "0a5b778e224a"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.outer-refused-no-message:settled",
|
||||
"observation": {
|
||||
"sender": ["8fce29bab175", "ad454c0116cc", "0a5b778e224a"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.method-not-found:settled",
|
||||
"observation": {
|
||||
"sender": ["8fce29bab175", "04f6f989667f", "0a5b778e224a"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.transport-rejection:settled",
|
||||
"observation": {
|
||||
"sender": ["8fce29bab175", "88df61a1af58", "0a5b778e224a"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.transport-rejection-no-message:settled",
|
||||
"observation": {
|
||||
"sender": ["8fce29bab175", "59eef396a6b8", "0a5b778e224a"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,729 +0,0 @@
|
||||
{
|
||||
"goldenFormatVersion": 6,
|
||||
"operation": "settings.home-providers",
|
||||
"family": "settings.home-providers",
|
||||
"namedDeltas": [],
|
||||
"values": {
|
||||
"08bc52c3b785": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-1",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"error": "inner refused",
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"0a5b778e224a": {
|
||||
"name": "linear.status#1",
|
||||
"ordinal": 3,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "linear.status"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"connected": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"1b8b10efd49c": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"error": {
|
||||
"code": "refused",
|
||||
"message": "outer refused"
|
||||
},
|
||||
"id": "frame-1",
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"44136fa355b3": {},
|
||||
"456bddaee00f": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-1",
|
||||
"ok": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"4777b5dc4e66": {
|
||||
"name": "preflight.check#1",
|
||||
"ordinal": 2,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.check"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"58995bbfcb17": {
|
||||
"name": "linear.status#1",
|
||||
"ordinal": 3,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "linear.status"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"776ed8372613": {
|
||||
"name": "preflight.check#1",
|
||||
"ordinal": 2,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.check"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-2",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"glab": {
|
||||
"installed": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"79b8c1b0d1d1": {
|
||||
"host-1": ["github"]
|
||||
},
|
||||
"83ed5cc34997": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-1",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"error": "refused"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"868d9c7eed63": {
|
||||
"host-1": ["github", "jira"]
|
||||
},
|
||||
"8fce29bab175": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-1",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"settings": {
|
||||
"defaultTuiAgent": "codex",
|
||||
"disabledTuiAgents": ["claude"],
|
||||
"hostSettingOverrides": {},
|
||||
"prBotAuthorOverrides": ["bot-user"],
|
||||
"visibleTaskProviders": ["github", "linear"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"9037ac778ace": {
|
||||
"name": "providers",
|
||||
"ordinal": 7,
|
||||
"value": {
|
||||
"host-1": ["github"]
|
||||
}
|
||||
},
|
||||
"908e43fdefae": {
|
||||
"name": "preflight.check#1",
|
||||
"ordinal": 5,
|
||||
"json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.check\"}"
|
||||
},
|
||||
"975f2ff9a730": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 4,
|
||||
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
|
||||
},
|
||||
"9ed7403a150d": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "rejected",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"error": {
|
||||
"category": "Error",
|
||||
"message": "",
|
||||
"isRpcDeliveryUnknown": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"a4cb9e2892b6": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-1",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"error": {
|
||||
"message": "inner refused"
|
||||
},
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"a940d6fc7d14": {
|
||||
"name": "providers",
|
||||
"ordinal": 7,
|
||||
"value": {
|
||||
"host-1": ["github", "jira"]
|
||||
}
|
||||
},
|
||||
"aa3490f8f6f8": {
|
||||
"name": "linear.status#1",
|
||||
"ordinal": 6,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"linear.status\"}"
|
||||
},
|
||||
"adf76218a426": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"error": {
|
||||
"code": "refused",
|
||||
"message": ""
|
||||
},
|
||||
"id": "frame-1",
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"eb79a9b3682a": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
"ec8967c2d560": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"eed02fc42efb": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-1",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"$rpc": "null"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"f93cbe622448": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "rejected",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"error": {
|
||||
"category": "Error",
|
||||
"message": "transport failure",
|
||||
"isRpcDeliveryUnknown": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"fa46c6c59726": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"error": {
|
||||
"code": "method_not_found",
|
||||
"message": "Unknown method"
|
||||
},
|
||||
"id": "frame-1",
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"recording": {
|
||||
"scenario": "matrix-settings.home-providers-settings.get-1",
|
||||
"checkpoints": [
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.prelude:settings-pending",
|
||||
"observation": {
|
||||
"sender": ["ec8967c2d560", "4777b5dc4e66", "58995bbfcb17"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "44136fa355b3",
|
||||
"effects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.normal:settled",
|
||||
"observation": {
|
||||
"sender": ["8fce29bab175", "776ed8372613", "0a5b778e224a"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.result-absent:settled",
|
||||
"observation": {
|
||||
"sender": ["456bddaee00f", "776ed8372613", "0a5b778e224a"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.result-null:settled",
|
||||
"observation": {
|
||||
"sender": ["eed02fc42efb", "776ed8372613", "0a5b778e224a"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.inner-ok-missing:settled",
|
||||
"observation": {
|
||||
"sender": ["83ed5cc34997", "776ed8372613", "0a5b778e224a"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "868d9c7eed63",
|
||||
"effects": ["a940d6fc7d14"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.inner-false-string-error:settled",
|
||||
"observation": {
|
||||
"sender": ["08bc52c3b785", "776ed8372613", "0a5b778e224a"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "868d9c7eed63",
|
||||
"effects": ["a940d6fc7d14"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.inner-false-object-error:settled",
|
||||
"observation": {
|
||||
"sender": ["a4cb9e2892b6", "776ed8372613", "0a5b778e224a"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "868d9c7eed63",
|
||||
"effects": ["a940d6fc7d14"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.outer-refused:settled",
|
||||
"observation": {
|
||||
"sender": ["1b8b10efd49c", "776ed8372613", "0a5b778e224a"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "868d9c7eed63",
|
||||
"effects": ["a940d6fc7d14"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.outer-refused-no-message:settled",
|
||||
"observation": {
|
||||
"sender": ["adf76218a426", "776ed8372613", "0a5b778e224a"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "868d9c7eed63",
|
||||
"effects": ["a940d6fc7d14"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.method-not-found:settled",
|
||||
"observation": {
|
||||
"sender": ["fa46c6c59726", "776ed8372613", "0a5b778e224a"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "868d9c7eed63",
|
||||
"effects": ["a940d6fc7d14"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.transport-rejection:settled",
|
||||
"observation": {
|
||||
"sender": ["f93cbe622448", "776ed8372613", "0a5b778e224a"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.transport-rejection-no-message:settled",
|
||||
"observation": {
|
||||
"sender": ["9ed7403a150d", "776ed8372613", "0a5b778e224a"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -4,24 +4,6 @@
|
||||
"family": "worktree.home-catalog",
|
||||
"namedDeltas": [],
|
||||
"values": {
|
||||
"07d517ec3da6": {
|
||||
"name": "info",
|
||||
"ordinal": 3,
|
||||
"value": {
|
||||
"host-1": {
|
||||
"activeCount": 2,
|
||||
"countsProvenAt": 1767225600000,
|
||||
"hostId": "host-1",
|
||||
"lastActiveWorktree": {
|
||||
"displayName": "One",
|
||||
"repo": "Repo",
|
||||
"status": "working",
|
||||
"worktreeId": "w-1"
|
||||
},
|
||||
"totalWorktrees": 2
|
||||
}
|
||||
}
|
||||
},
|
||||
"0c5e517bcdb4": {
|
||||
"name": "worktree.ps#1",
|
||||
"ordinal": 1,
|
||||
@@ -54,6 +36,41 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"0d02c4637af4": {
|
||||
"name": "info",
|
||||
"ordinal": 3,
|
||||
"value": {
|
||||
"host-1": {
|
||||
"activeCount": 2,
|
||||
"attention": [
|
||||
{
|
||||
"branch": {
|
||||
"$rpc": "undefined"
|
||||
},
|
||||
"displayName": "One",
|
||||
"dotStatus": "working",
|
||||
"lastOutputAt": {
|
||||
"$rpc": "undefined"
|
||||
},
|
||||
"repo": "Repo",
|
||||
"workingMode": {
|
||||
"$rpc": "undefined"
|
||||
},
|
||||
"worktreeId": "w-1"
|
||||
}
|
||||
],
|
||||
"countsProvenAt": 1767225600000,
|
||||
"hostId": "host-1",
|
||||
"lastActiveWorktree": {
|
||||
"displayName": "One",
|
||||
"repo": "Repo",
|
||||
"status": "working",
|
||||
"worktreeId": "w-1"
|
||||
},
|
||||
"totalWorktrees": 2
|
||||
}
|
||||
}
|
||||
},
|
||||
"14379cad1cb5": {
|
||||
"name": "worktree.ps#1",
|
||||
"ordinal": 1,
|
||||
@@ -89,6 +106,22 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"1d0a5fbc1e3b": {
|
||||
"name": "info",
|
||||
"ordinal": 3,
|
||||
"value": {
|
||||
"host-1": {
|
||||
"activeCount": 0,
|
||||
"attention": [],
|
||||
"countsProvenAt": 1767225600000,
|
||||
"hostId": "host-1",
|
||||
"lastActiveWorktree": {
|
||||
"$rpc": "null"
|
||||
},
|
||||
"totalWorktrees": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"2b2ff73e3290": {
|
||||
"name": "worktree.ps#1",
|
||||
"ordinal": 1,
|
||||
@@ -126,21 +159,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"2fb25a9212b1": {
|
||||
"name": "info",
|
||||
"ordinal": 3,
|
||||
"value": {
|
||||
"host-1": {
|
||||
"activeCount": 0,
|
||||
"countsProvenAt": 1767225600000,
|
||||
"hostId": "host-1",
|
||||
"lastActiveWorktree": {
|
||||
"$rpc": "null"
|
||||
},
|
||||
"totalWorktrees": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"3b32328e5154": {
|
||||
"name": "worktree.ps#1",
|
||||
"ordinal": 1,
|
||||
@@ -258,9 +276,26 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"6624fb97ea31": {
|
||||
"87aae336d391": {
|
||||
"host-1": {
|
||||
"activeCount": 2,
|
||||
"attention": [
|
||||
{
|
||||
"branch": {
|
||||
"$rpc": "undefined"
|
||||
},
|
||||
"displayName": "One",
|
||||
"dotStatus": "working",
|
||||
"lastOutputAt": {
|
||||
"$rpc": "undefined"
|
||||
},
|
||||
"repo": "Repo",
|
||||
"workingMode": {
|
||||
"$rpc": "undefined"
|
||||
},
|
||||
"worktreeId": "w-1"
|
||||
}
|
||||
],
|
||||
"countsProvenAt": 1767225600000,
|
||||
"hostId": "host-1",
|
||||
"lastActiveWorktree": {
|
||||
@@ -381,6 +416,18 @@
|
||||
"ordinal": 2,
|
||||
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"worktree.ps\",\"params\":{\"limit\":10000,\"supportsWorktreeVisibilitySourceDefaults\":true}}"
|
||||
},
|
||||
"abea2b7ff9d3": {
|
||||
"host-1": {
|
||||
"activeCount": 0,
|
||||
"attention": [],
|
||||
"countsProvenAt": 1767225600000,
|
||||
"hostId": "host-1",
|
||||
"lastActiveWorktree": {
|
||||
"$rpc": "null"
|
||||
},
|
||||
"totalWorktrees": 0
|
||||
}
|
||||
},
|
||||
"b7bbf0d88929": {
|
||||
"name": "worktree.ps#1",
|
||||
"ordinal": 1,
|
||||
@@ -431,17 +478,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"d0ec31ab66d5": {
|
||||
"host-1": {
|
||||
"activeCount": 0,
|
||||
"countsProvenAt": 1767225600000,
|
||||
"hostId": "host-1",
|
||||
"lastActiveWorktree": {
|
||||
"$rpc": "null"
|
||||
},
|
||||
"totalWorktrees": 0
|
||||
}
|
||||
},
|
||||
"d1800200eda0": {
|
||||
"name": "worktree.ps#1",
|
||||
"ordinal": 1,
|
||||
@@ -543,8 +579,8 @@
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "6624fb97ea31",
|
||||
"effects": ["07d517ec3da6"]
|
||||
"state": "87aae336d391",
|
||||
"effects": ["0d02c4637af4"]
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -579,8 +615,8 @@
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "d0ec31ab66d5",
|
||||
"effects": ["2fb25a9212b1"]
|
||||
"state": "abea2b7ff9d3",
|
||||
"effects": ["1d0a5fbc1e3b"]
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -591,8 +627,8 @@
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "d0ec31ab66d5",
|
||||
"effects": ["2fb25a9212b1"]
|
||||
"state": "abea2b7ff9d3",
|
||||
"effects": ["1d0a5fbc1e3b"]
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -603,8 +639,8 @@
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "d0ec31ab66d5",
|
||||
"effects": ["2fb25a9212b1"]
|
||||
"state": "abea2b7ff9d3",
|
||||
"effects": ["1d0a5fbc1e3b"]
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,760 +0,0 @@
|
||||
{
|
||||
"goldenFormatVersion": 6,
|
||||
"operation": "settings.home-providers",
|
||||
"family": "settings.home-providers",
|
||||
"namedDeltas": [],
|
||||
"values": {
|
||||
"014c74879acc": {
|
||||
"name": "preflight.check#1",
|
||||
"ordinal": 2,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.check"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "rejected",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"error": {
|
||||
"category": "LogicalClientCutoverError",
|
||||
"message": "RPC interrupted by connection migration",
|
||||
"isRpcDeliveryUnknown": true,
|
||||
"cause": {
|
||||
"category": "Error",
|
||||
"message": "Connection closed",
|
||||
"isRpcDeliveryUnknown": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"0a5b778e224a": {
|
||||
"name": "linear.status#1",
|
||||
"ordinal": 3,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "linear.status"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"connected": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"2470587460aa": {
|
||||
"name": "linear.status#1",
|
||||
"ordinal": 3,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "linear.status"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "rejected",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"error": {
|
||||
"category": "Error",
|
||||
"message": "Connection lost",
|
||||
"isRpcDeliveryUnknown": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"3002f45fd1db": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "rejected",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"error": {
|
||||
"category": "LogicalClientCutoverError",
|
||||
"message": "RPC interrupted by connection migration",
|
||||
"isRpcDeliveryUnknown": true,
|
||||
"cause": {
|
||||
"category": "Error",
|
||||
"message": "Connection closed",
|
||||
"isRpcDeliveryUnknown": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"44136fa355b3": {},
|
||||
"4777b5dc4e66": {
|
||||
"name": "preflight.check#1",
|
||||
"ordinal": 2,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.check"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"534badf93f95": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "rejected",
|
||||
"startedAt": 0,
|
||||
"settledAt": 30000,
|
||||
"error": {
|
||||
"category": "Error",
|
||||
"message": "Request timed out: settings.get",
|
||||
"isRpcDeliveryUnknown": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"540740375fe4": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "rejected",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"error": {
|
||||
"category": "Error",
|
||||
"message": "Connection lost",
|
||||
"isRpcDeliveryUnknown": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"571a5f07f058": {
|
||||
"name": "linear.status#1",
|
||||
"ordinal": 3,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "linear.status"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "rejected",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"error": {
|
||||
"category": "LogicalClientCutoverError",
|
||||
"message": "RPC interrupted by connection migration",
|
||||
"isRpcDeliveryUnknown": true,
|
||||
"cause": {
|
||||
"category": "Error",
|
||||
"message": "Connection closed",
|
||||
"isRpcDeliveryUnknown": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"58995bbfcb17": {
|
||||
"name": "linear.status#1",
|
||||
"ordinal": 3,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "linear.status"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"722f2edb4231": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "rejected",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"error": {
|
||||
"category": "Error",
|
||||
"message": "settings.get#1 rejected",
|
||||
"isRpcDeliveryUnknown": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"776ed8372613": {
|
||||
"name": "preflight.check#1",
|
||||
"ordinal": 2,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.check"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-2",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"glab": {
|
||||
"installed": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"79b8c1b0d1d1": {
|
||||
"host-1": ["github"]
|
||||
},
|
||||
"84170a267808": {
|
||||
"name": "preflight.check#1",
|
||||
"ordinal": 2,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.check"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "rejected",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"error": {
|
||||
"category": "Error",
|
||||
"message": "Connection lost",
|
||||
"isRpcDeliveryUnknown": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"8fce29bab175": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-1",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"settings": {
|
||||
"defaultTuiAgent": "codex",
|
||||
"disabledTuiAgents": ["claude"],
|
||||
"hostSettingOverrides": {},
|
||||
"prBotAuthorOverrides": ["bot-user"],
|
||||
"visibleTaskProviders": ["github", "linear"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"9037ac778ace": {
|
||||
"name": "providers",
|
||||
"ordinal": 7,
|
||||
"value": {
|
||||
"host-1": ["github"]
|
||||
}
|
||||
},
|
||||
"908e43fdefae": {
|
||||
"name": "preflight.check#1",
|
||||
"ordinal": 5,
|
||||
"json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.check\"}"
|
||||
},
|
||||
"975f2ff9a730": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 4,
|
||||
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
|
||||
},
|
||||
"aa3490f8f6f8": {
|
||||
"name": "linear.status#1",
|
||||
"ordinal": 6,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"linear.status\"}"
|
||||
},
|
||||
"b67ae9ca9783": {
|
||||
"name": "preflight.check#1",
|
||||
"ordinal": 2,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.check"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "rejected",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"error": {
|
||||
"category": "Error",
|
||||
"message": "preflight.check#1 rejected",
|
||||
"isRpcDeliveryUnknown": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"d1204a893729": {
|
||||
"name": "preflight.check#1",
|
||||
"ordinal": 2,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.check"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "rejected",
|
||||
"startedAt": 0,
|
||||
"settledAt": 30000,
|
||||
"error": {
|
||||
"category": "Error",
|
||||
"message": "Request timed out: preflight.check",
|
||||
"isRpcDeliveryUnknown": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"d9e806b6981a": {
|
||||
"name": "linear.status#1",
|
||||
"ordinal": 3,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "linear.status"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "rejected",
|
||||
"startedAt": 0,
|
||||
"settledAt": 30000,
|
||||
"error": {
|
||||
"category": "Error",
|
||||
"message": "Request timed out: linear.status",
|
||||
"isRpcDeliveryUnknown": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"eb79a9b3682a": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
"ec8967c2d560": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"recording": {
|
||||
"scenario": "schedules-settings-home-providers-fulfilled",
|
||||
"checkpoints": [
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.forward:sibling-pending",
|
||||
"observation": {
|
||||
"sender": ["8fce29bab175", "4777b5dc4e66", "58995bbfcb17"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "44136fa355b3",
|
||||
"effects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.forward:settled",
|
||||
"observation": {
|
||||
"sender": ["8fce29bab175", "776ed8372613", "0a5b778e224a"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.reverse:sibling-pending",
|
||||
"observation": {
|
||||
"sender": ["ec8967c2d560", "776ed8372613", "58995bbfcb17"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "44136fa355b3",
|
||||
"effects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.reverse:settled",
|
||||
"observation": {
|
||||
"sender": ["8fce29bab175", "776ed8372613", "0a5b778e224a"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.both-reject-forward:sibling-pending",
|
||||
"observation": {
|
||||
"sender": ["722f2edb4231", "4777b5dc4e66", "58995bbfcb17"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.both-reject-forward:settled",
|
||||
"observation": {
|
||||
"sender": ["722f2edb4231", "b67ae9ca9783", "0a5b778e224a"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.both-reject-reverse:sibling-pending",
|
||||
"observation": {
|
||||
"sender": ["ec8967c2d560", "b67ae9ca9783", "58995bbfcb17"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.both-reject-reverse:settled",
|
||||
"observation": {
|
||||
"sender": ["722f2edb4231", "b67ae9ca9783", "0a5b778e224a"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.reject-peer-pending:sibling-pending",
|
||||
"observation": {
|
||||
"sender": ["722f2edb4231", "4777b5dc4e66", "58995bbfcb17"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.reject-peer-pending:settled",
|
||||
"observation": {
|
||||
"sender": ["722f2edb4231", "4777b5dc4e66", "0a5b778e224a"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.timeout:settled",
|
||||
"observation": {
|
||||
"sender": ["534badf93f95", "d1204a893729", "d9e806b6981a"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.disconnect:settled",
|
||||
"observation": {
|
||||
"sender": ["540740375fe4", "84170a267808", "2470587460aa"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a",
|
||||
"disconnect": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled.client-cutover:settled",
|
||||
"observation": {
|
||||
"sender": ["3002f45fd1db", "014c74879acc", "571a5f07f058"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a",
|
||||
"cutover": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,639 +0,0 @@
|
||||
{
|
||||
"goldenFormatVersion": 6,
|
||||
"operation": "settings.home-providers",
|
||||
"family": "settings.home-providers",
|
||||
"namedDeltas": [],
|
||||
"values": {
|
||||
"0a5b778e224a": {
|
||||
"name": "linear.status#1",
|
||||
"ordinal": 3,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "linear.status"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"connected": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"1425cdcf73af": {
|
||||
"name": "settings.get#3",
|
||||
"ordinal": 18,
|
||||
"json": "{\"id\":\"frame-7\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
|
||||
},
|
||||
"1578fb797a9b": {
|
||||
"name": "providers",
|
||||
"ordinal": 13,
|
||||
"value": {
|
||||
"host-1": ["github"]
|
||||
}
|
||||
},
|
||||
"1eef16c4ad65": {
|
||||
"name": "preflight.check#2",
|
||||
"ordinal": 9,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.check"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-5",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"glab": {
|
||||
"installed": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"235eb9cfa7e0": {
|
||||
"name": "settings.get#2",
|
||||
"ordinal": 8,
|
||||
"json": "{\"id\":\"frame-4\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
|
||||
},
|
||||
"2bb81a9fc867": {
|
||||
"name": "providers",
|
||||
"ordinal": 14,
|
||||
"value": {
|
||||
"host-1": ["github"]
|
||||
}
|
||||
},
|
||||
"44136fa355b3": {},
|
||||
"4777b5dc4e66": {
|
||||
"name": "preflight.check#1",
|
||||
"ordinal": 2,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.check"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"48ef1d658168": {
|
||||
"name": "preflight.check#2",
|
||||
"ordinal": 10,
|
||||
"json": "{\"id\":\"frame-5\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.check\"}"
|
||||
},
|
||||
"4ff081739994": {
|
||||
"name": "preflight.check#2",
|
||||
"ordinal": 9,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.check"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"525e8231aebc": {
|
||||
"name": "preflight.check#3",
|
||||
"ordinal": 19,
|
||||
"json": "{\"id\":\"frame-8\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.check\"}"
|
||||
},
|
||||
"58995bbfcb17": {
|
||||
"name": "linear.status#1",
|
||||
"ordinal": 3,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "linear.status"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"596151a25a84": {
|
||||
"name": "linear.status#2",
|
||||
"ordinal": 12,
|
||||
"json": "{\"id\":\"frame-6\",\"deviceToken\":\"recording-device\",\"method\":\"linear.status\"}"
|
||||
},
|
||||
"599b30e9d8ec": {
|
||||
"name": "linear.status#3",
|
||||
"ordinal": 17,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "linear.status"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"776ed8372613": {
|
||||
"name": "preflight.check#1",
|
||||
"ordinal": 2,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.check"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-2",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"glab": {
|
||||
"installed": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"79b8c1b0d1d1": {
|
||||
"host-1": ["github"]
|
||||
},
|
||||
"7a145a4c7f2f": {
|
||||
"name": "linear.status#2",
|
||||
"ordinal": 11,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "linear.status"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"8e3ababdaa74": {
|
||||
"name": "preflight.check#3",
|
||||
"ordinal": 16,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.check"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"8fce29bab175": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-1",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"settings": {
|
||||
"defaultTuiAgent": "codex",
|
||||
"disabledTuiAgents": ["claude"],
|
||||
"hostSettingOverrides": {},
|
||||
"prBotAuthorOverrides": ["bot-user"],
|
||||
"visibleTaskProviders": ["github", "linear"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"908e43fdefae": {
|
||||
"name": "preflight.check#1",
|
||||
"ordinal": 5,
|
||||
"json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.check\"}"
|
||||
},
|
||||
"93f2060336d8": {
|
||||
"name": "linear.status#2",
|
||||
"ordinal": 11,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "linear.status"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-6",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"connected": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"975f2ff9a730": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 4,
|
||||
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
|
||||
},
|
||||
"a7075803ae4d": {
|
||||
"name": "linear.status#3",
|
||||
"ordinal": 20,
|
||||
"json": "{\"id\":\"frame-9\",\"deviceToken\":\"recording-device\",\"method\":\"linear.status\"}"
|
||||
},
|
||||
"aa3490f8f6f8": {
|
||||
"name": "linear.status#1",
|
||||
"ordinal": 6,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"linear.status\"}"
|
||||
},
|
||||
"eb79a9b3682a": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
"ec8967c2d560": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"f6bd43dd03d3": {
|
||||
"name": "settings.get#3",
|
||||
"ordinal": 15,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"faeb2251338b": {
|
||||
"name": "settings.get#2",
|
||||
"ordinal": 7,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"ff1b4ecd513c": {
|
||||
"name": "settings.get#2",
|
||||
"ordinal": 7,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-4",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"settings": {
|
||||
"defaultTuiAgent": "codex",
|
||||
"disabledTuiAgents": ["claude"],
|
||||
"hostSettingOverrides": {},
|
||||
"prBotAuthorOverrides": ["bot-user"],
|
||||
"visibleTaskProviders": ["github", "linear"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"recording": {
|
||||
"scenario": "settings-home-coalesced",
|
||||
"checkpoints": [
|
||||
{
|
||||
"id": "settings-pending",
|
||||
"observation": {
|
||||
"sender": ["ec8967c2d560", "4777b5dc4e66", "58995bbfcb17"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a",
|
||||
"overlapping-load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "44136fa355b3",
|
||||
"effects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settled",
|
||||
"observation": {
|
||||
"sender": [
|
||||
"8fce29bab175",
|
||||
"776ed8372613",
|
||||
"0a5b778e224a",
|
||||
"faeb2251338b",
|
||||
"4ff081739994",
|
||||
"7a145a4c7f2f"
|
||||
],
|
||||
"payloads": [
|
||||
"975f2ff9a730",
|
||||
"908e43fdefae",
|
||||
"aa3490f8f6f8",
|
||||
"235eb9cfa7e0",
|
||||
"48ef1d658168",
|
||||
"596151a25a84"
|
||||
],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a",
|
||||
"overlapping-load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["1578fb797a9b"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "follow-up-settled",
|
||||
"observation": {
|
||||
"sender": [
|
||||
"8fce29bab175",
|
||||
"776ed8372613",
|
||||
"0a5b778e224a",
|
||||
"ff1b4ecd513c",
|
||||
"1eef16c4ad65",
|
||||
"93f2060336d8"
|
||||
],
|
||||
"payloads": [
|
||||
"975f2ff9a730",
|
||||
"908e43fdefae",
|
||||
"aa3490f8f6f8",
|
||||
"235eb9cfa7e0",
|
||||
"48ef1d658168",
|
||||
"596151a25a84"
|
||||
],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a",
|
||||
"overlapping-load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["1578fb797a9b", "2bb81a9fc867"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "third-query",
|
||||
"observation": {
|
||||
"sender": [
|
||||
"8fce29bab175",
|
||||
"776ed8372613",
|
||||
"0a5b778e224a",
|
||||
"ff1b4ecd513c",
|
||||
"1eef16c4ad65",
|
||||
"93f2060336d8",
|
||||
"f6bd43dd03d3",
|
||||
"8e3ababdaa74",
|
||||
"599b30e9d8ec"
|
||||
],
|
||||
"payloads": [
|
||||
"975f2ff9a730",
|
||||
"908e43fdefae",
|
||||
"aa3490f8f6f8",
|
||||
"235eb9cfa7e0",
|
||||
"48ef1d658168",
|
||||
"596151a25a84",
|
||||
"1425cdcf73af",
|
||||
"525e8231aebc",
|
||||
"a7075803ae4d"
|
||||
],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a",
|
||||
"overlapping-load": "eb79a9b3682a",
|
||||
"third-load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["1578fb797a9b", "2bb81a9fc867"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,259 +0,0 @@
|
||||
{
|
||||
"goldenFormatVersion": 6,
|
||||
"operation": "settings.home-providers",
|
||||
"family": "settings.home-providers",
|
||||
"namedDeltas": [],
|
||||
"values": {
|
||||
"0a5b778e224a": {
|
||||
"name": "linear.status#1",
|
||||
"ordinal": 3,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "linear.status"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"connected": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"44136fa355b3": {},
|
||||
"4777b5dc4e66": {
|
||||
"name": "preflight.check#1",
|
||||
"ordinal": 2,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.check"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"58995bbfcb17": {
|
||||
"name": "linear.status#1",
|
||||
"ordinal": 3,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "linear.status"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"776ed8372613": {
|
||||
"name": "preflight.check#1",
|
||||
"ordinal": 2,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.check"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-2",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"glab": {
|
||||
"installed": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"79b8c1b0d1d1": {
|
||||
"host-1": ["github"]
|
||||
},
|
||||
"8fce29bab175": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-1",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"settings": {
|
||||
"defaultTuiAgent": "codex",
|
||||
"disabledTuiAgents": ["claude"],
|
||||
"hostSettingOverrides": {},
|
||||
"prBotAuthorOverrides": ["bot-user"],
|
||||
"visibleTaskProviders": ["github", "linear"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"9037ac778ace": {
|
||||
"name": "providers",
|
||||
"ordinal": 7,
|
||||
"value": {
|
||||
"host-1": ["github"]
|
||||
}
|
||||
},
|
||||
"908e43fdefae": {
|
||||
"name": "preflight.check#1",
|
||||
"ordinal": 5,
|
||||
"json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.check\"}"
|
||||
},
|
||||
"975f2ff9a730": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 4,
|
||||
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
|
||||
},
|
||||
"aa3490f8f6f8": {
|
||||
"name": "linear.status#1",
|
||||
"ordinal": 6,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"linear.status\"}"
|
||||
},
|
||||
"eb79a9b3682a": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
"ec8967c2d560": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"recording": {
|
||||
"scenario": "settings-home-providers-fulfilled",
|
||||
"checkpoints": [
|
||||
{
|
||||
"id": "settings-pending",
|
||||
"observation": {
|
||||
"sender": ["ec8967c2d560", "4777b5dc4e66", "58995bbfcb17"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "44136fa355b3",
|
||||
"effects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settled",
|
||||
"observation": {
|
||||
"sender": ["8fce29bab175", "776ed8372613", "0a5b778e224a"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,533 +0,0 @@
|
||||
{
|
||||
"goldenFormatVersion": 6,
|
||||
"operation": "settings.home-providers",
|
||||
"family": "settings.home-providers",
|
||||
"namedDeltas": [],
|
||||
"values": {
|
||||
"0a5b778e224a": {
|
||||
"name": "linear.status#1",
|
||||
"ordinal": 3,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "linear.status"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"connected": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"1594e2fa3a9c": {
|
||||
"name": "preflight.check#2",
|
||||
"ordinal": 12,
|
||||
"json": "{\"id\":\"frame-5\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.check\"}"
|
||||
},
|
||||
"177d9dc43a98": {
|
||||
"name": "linear.status#2",
|
||||
"ordinal": 10,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "linear.status"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-6",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"connected": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"1eef16c4ad65": {
|
||||
"name": "preflight.check#2",
|
||||
"ordinal": 9,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.check"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-5",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"glab": {
|
||||
"installed": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"44136fa355b3": {},
|
||||
"44e1776d841d": {
|
||||
"name": "settings.get#2",
|
||||
"ordinal": 8,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"4777b5dc4e66": {
|
||||
"name": "preflight.check#1",
|
||||
"ordinal": 2,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.check"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"4ff081739994": {
|
||||
"name": "preflight.check#2",
|
||||
"ordinal": 9,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.check"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"56227393d82f": {
|
||||
"name": "providers",
|
||||
"ordinal": 14,
|
||||
"value": {
|
||||
"host-1": ["github", "jira"]
|
||||
}
|
||||
},
|
||||
"58995bbfcb17": {
|
||||
"name": "linear.status#1",
|
||||
"ordinal": 3,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "linear.status"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"5d9b2e6167a0": {
|
||||
"name": "linear.status#2",
|
||||
"ordinal": 10,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "linear.status"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"6198eef8dbfc": {
|
||||
"name": "settings.get#2",
|
||||
"ordinal": 8,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"error": {
|
||||
"code": "refused",
|
||||
"message": "settings refused"
|
||||
},
|
||||
"id": "frame-4",
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"776ed8372613": {
|
||||
"name": "preflight.check#1",
|
||||
"ordinal": 2,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.check"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-2",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"glab": {
|
||||
"installed": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"79b8c1b0d1d1": {
|
||||
"host-1": ["github"]
|
||||
},
|
||||
"868d9c7eed63": {
|
||||
"host-1": ["github", "jira"]
|
||||
},
|
||||
"8fce29bab175": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-1",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"settings": {
|
||||
"defaultTuiAgent": "codex",
|
||||
"disabledTuiAgents": ["claude"],
|
||||
"hostSettingOverrides": {},
|
||||
"prBotAuthorOverrides": ["bot-user"],
|
||||
"visibleTaskProviders": ["github", "linear"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"9037ac778ace": {
|
||||
"name": "providers",
|
||||
"ordinal": 7,
|
||||
"value": {
|
||||
"host-1": ["github"]
|
||||
}
|
||||
},
|
||||
"908e43fdefae": {
|
||||
"name": "preflight.check#1",
|
||||
"ordinal": 5,
|
||||
"json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.check\"}"
|
||||
},
|
||||
"975f2ff9a730": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 4,
|
||||
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
|
||||
},
|
||||
"9b19d4e082d8": {
|
||||
"name": "settings.get#2",
|
||||
"ordinal": 11,
|
||||
"json": "{\"id\":\"frame-4\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
|
||||
},
|
||||
"aa3490f8f6f8": {
|
||||
"name": "linear.status#1",
|
||||
"ordinal": 6,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"linear.status\"}"
|
||||
},
|
||||
"eb79a9b3682a": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
"ec8967c2d560": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"fd11a094b656": {
|
||||
"name": "linear.status#2",
|
||||
"ordinal": 13,
|
||||
"json": "{\"id\":\"frame-6\",\"deviceToken\":\"recording-device\",\"method\":\"linear.status\"}"
|
||||
}
|
||||
},
|
||||
"recording": {
|
||||
"scenario": "settings-home-providers-refuse-after-data",
|
||||
"checkpoints": [
|
||||
{
|
||||
"id": "settings-pending",
|
||||
"observation": {
|
||||
"sender": ["ec8967c2d560", "4777b5dc4e66", "58995bbfcb17"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "44136fa355b3",
|
||||
"effects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settled",
|
||||
"observation": {
|
||||
"sender": ["8fce29bab175", "776ed8372613", "0a5b778e224a"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "data-present",
|
||||
"observation": {
|
||||
"sender": ["8fce29bab175", "776ed8372613", "0a5b778e224a"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "refresh-pending",
|
||||
"observation": {
|
||||
"sender": [
|
||||
"8fce29bab175",
|
||||
"776ed8372613",
|
||||
"0a5b778e224a",
|
||||
"44e1776d841d",
|
||||
"4ff081739994",
|
||||
"5d9b2e6167a0"
|
||||
],
|
||||
"payloads": [
|
||||
"975f2ff9a730",
|
||||
"908e43fdefae",
|
||||
"aa3490f8f6f8",
|
||||
"9b19d4e082d8",
|
||||
"1594e2fa3a9c",
|
||||
"fd11a094b656"
|
||||
],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a",
|
||||
"reload": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "refused-after-data",
|
||||
"observation": {
|
||||
"sender": [
|
||||
"8fce29bab175",
|
||||
"776ed8372613",
|
||||
"0a5b778e224a",
|
||||
"6198eef8dbfc",
|
||||
"1eef16c4ad65",
|
||||
"177d9dc43a98"
|
||||
],
|
||||
"payloads": [
|
||||
"975f2ff9a730",
|
||||
"908e43fdefae",
|
||||
"aa3490f8f6f8",
|
||||
"9b19d4e082d8",
|
||||
"1594e2fa3a9c",
|
||||
"fd11a094b656"
|
||||
],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a",
|
||||
"reload": "eb79a9b3682a"
|
||||
},
|
||||
"state": "868d9c7eed63",
|
||||
"effects": ["9037ac778ace", "56227393d82f"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,254 +0,0 @@
|
||||
{
|
||||
"goldenFormatVersion": 6,
|
||||
"operation": "settings.home-providers",
|
||||
"family": "settings.home-providers",
|
||||
"namedDeltas": [],
|
||||
"values": {
|
||||
"0a5b778e224a": {
|
||||
"name": "linear.status#1",
|
||||
"ordinal": 3,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "linear.status"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"connected": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"44136fa355b3": {},
|
||||
"4777b5dc4e66": {
|
||||
"name": "preflight.check#1",
|
||||
"ordinal": 2,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.check"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"58995bbfcb17": {
|
||||
"name": "linear.status#1",
|
||||
"ordinal": 3,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "linear.status"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"776ed8372613": {
|
||||
"name": "preflight.check#1",
|
||||
"ordinal": 2,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.check"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-2",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"glab": {
|
||||
"installed": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"868d9c7eed63": {
|
||||
"host-1": ["github", "jira"]
|
||||
},
|
||||
"908e43fdefae": {
|
||||
"name": "preflight.check#1",
|
||||
"ordinal": 5,
|
||||
"json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.check\"}"
|
||||
},
|
||||
"975f2ff9a730": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 4,
|
||||
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
|
||||
},
|
||||
"a940d6fc7d14": {
|
||||
"name": "providers",
|
||||
"ordinal": 7,
|
||||
"value": {
|
||||
"host-1": ["github", "jira"]
|
||||
}
|
||||
},
|
||||
"aa3490f8f6f8": {
|
||||
"name": "linear.status#1",
|
||||
"ordinal": 6,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"linear.status\"}"
|
||||
},
|
||||
"ba6fa6c1c0e6": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"error": {
|
||||
"code": "refused",
|
||||
"message": "settings refused"
|
||||
},
|
||||
"id": "frame-1",
|
||||
"ok": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"eb79a9b3682a": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
"ec8967c2d560": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"recording": {
|
||||
"scenario": "settings-home-providers-refused",
|
||||
"checkpoints": [
|
||||
{
|
||||
"id": "settings-pending",
|
||||
"observation": {
|
||||
"sender": ["ec8967c2d560", "4777b5dc4e66", "58995bbfcb17"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "44136fa355b3",
|
||||
"effects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settled",
|
||||
"observation": {
|
||||
"sender": ["ba6fa6c1c0e6", "776ed8372613", "0a5b778e224a"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "868d9c7eed63",
|
||||
"effects": ["a940d6fc7d14"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,251 +0,0 @@
|
||||
{
|
||||
"goldenFormatVersion": 6,
|
||||
"operation": "settings.home-providers",
|
||||
"family": "settings.home-providers",
|
||||
"namedDeltas": [],
|
||||
"values": {
|
||||
"0a5b778e224a": {
|
||||
"name": "linear.status#1",
|
||||
"ordinal": 3,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "linear.status"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-3",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"connected": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"30e5e23fe0a9": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "rejected",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"error": {
|
||||
"category": "Error",
|
||||
"message": "settings disconnected",
|
||||
"isRpcDeliveryUnknown": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"44136fa355b3": {},
|
||||
"4777b5dc4e66": {
|
||||
"name": "preflight.check#1",
|
||||
"ordinal": 2,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.check"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"58995bbfcb17": {
|
||||
"name": "linear.status#1",
|
||||
"ordinal": 3,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "linear.status"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"776ed8372613": {
|
||||
"name": "preflight.check#1",
|
||||
"ordinal": 2,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "preflight.check"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"id": "frame-2",
|
||||
"ok": true,
|
||||
"result": {
|
||||
"glab": {
|
||||
"installed": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"79b8c1b0d1d1": {
|
||||
"host-1": ["github"]
|
||||
},
|
||||
"9037ac778ace": {
|
||||
"name": "providers",
|
||||
"ordinal": 7,
|
||||
"value": {
|
||||
"host-1": ["github"]
|
||||
}
|
||||
},
|
||||
"908e43fdefae": {
|
||||
"name": "preflight.check#1",
|
||||
"ordinal": 5,
|
||||
"json": "{\"id\":\"frame-2\",\"deviceToken\":\"recording-device\",\"method\":\"preflight.check\"}"
|
||||
},
|
||||
"975f2ff9a730": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 4,
|
||||
"json": "{\"id\":\"frame-1\",\"deviceToken\":\"recording-device\",\"method\":\"settings.get\"}"
|
||||
},
|
||||
"aa3490f8f6f8": {
|
||||
"name": "linear.status#1",
|
||||
"ordinal": 6,
|
||||
"json": "{\"id\":\"frame-3\",\"deviceToken\":\"recording-device\",\"method\":\"linear.status\"}"
|
||||
},
|
||||
"eb79a9b3682a": {
|
||||
"status": "fulfilled",
|
||||
"startedAt": 0,
|
||||
"settledAt": 0,
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
"ec8967c2d560": {
|
||||
"name": "settings.get#1",
|
||||
"ordinal": 1,
|
||||
"args": [
|
||||
{
|
||||
"name": "method",
|
||||
"value": "settings.get"
|
||||
},
|
||||
{
|
||||
"name": "params",
|
||||
"value": {
|
||||
"$rpc": "undefined"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "options",
|
||||
"value": {
|
||||
"$rpc": "absent"
|
||||
}
|
||||
}
|
||||
],
|
||||
"settlement": {
|
||||
"status": "pending",
|
||||
"startedAt": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"recording": {
|
||||
"scenario": "settings-home-providers-transport-error",
|
||||
"checkpoints": [
|
||||
{
|
||||
"id": "settings-pending",
|
||||
"observation": {
|
||||
"sender": ["ec8967c2d560", "4777b5dc4e66", "58995bbfcb17"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "44136fa355b3",
|
||||
"effects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "settled",
|
||||
"observation": {
|
||||
"sender": ["30e5e23fe0a9", "776ed8372613", "0a5b778e224a"],
|
||||
"payloads": ["975f2ff9a730", "908e43fdefae", "aa3490f8f6f8"],
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "79b8c1b0d1d1",
|
||||
"effects": ["9037ac778ace"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -4,12 +4,29 @@
|
||||
"family": "worktree.home-catalog",
|
||||
"namedDeltas": [],
|
||||
"values": {
|
||||
"07d517ec3da6": {
|
||||
"0d02c4637af4": {
|
||||
"name": "info",
|
||||
"ordinal": 3,
|
||||
"value": {
|
||||
"host-1": {
|
||||
"activeCount": 2,
|
||||
"attention": [
|
||||
{
|
||||
"branch": {
|
||||
"$rpc": "undefined"
|
||||
},
|
||||
"displayName": "One",
|
||||
"dotStatus": "working",
|
||||
"lastOutputAt": {
|
||||
"$rpc": "undefined"
|
||||
},
|
||||
"repo": "Repo",
|
||||
"workingMode": {
|
||||
"$rpc": "undefined"
|
||||
},
|
||||
"worktreeId": "w-1"
|
||||
}
|
||||
],
|
||||
"countsProvenAt": 1767225600000,
|
||||
"hostId": "host-1",
|
||||
"lastActiveWorktree": {
|
||||
@@ -96,9 +113,26 @@
|
||||
"startedAt": 0
|
||||
}
|
||||
},
|
||||
"6624fb97ea31": {
|
||||
"87aae336d391": {
|
||||
"host-1": {
|
||||
"activeCount": 2,
|
||||
"attention": [
|
||||
{
|
||||
"branch": {
|
||||
"$rpc": "undefined"
|
||||
},
|
||||
"displayName": "One",
|
||||
"dotStatus": "working",
|
||||
"lastOutputAt": {
|
||||
"$rpc": "undefined"
|
||||
},
|
||||
"repo": "Repo",
|
||||
"workingMode": {
|
||||
"$rpc": "undefined"
|
||||
},
|
||||
"worktreeId": "w-1"
|
||||
}
|
||||
],
|
||||
"countsProvenAt": 1767225600000,
|
||||
"hostId": "host-1",
|
||||
"lastActiveWorktree": {
|
||||
@@ -151,8 +185,8 @@
|
||||
"settlements": {
|
||||
"load": "eb79a9b3682a"
|
||||
},
|
||||
"state": "6624fb97ea31",
|
||||
"effects": ["07d517ec3da6"]
|
||||
"state": "87aae336d391",
|
||||
"effects": ["0d02c4637af4"]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -681,182 +681,6 @@
|
||||
],
|
||||
"namedDeltas": ["new-workspace-runtime-context-null-results-degrade-to-absent"]
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-fulfilled",
|
||||
"operation": "settings.home-providers",
|
||||
"family": "settings.home-providers",
|
||||
"sites": ["mobile/src/home/mobile-home-host-requests.ts"],
|
||||
"schedules": ["settings-first"],
|
||||
"steps": [
|
||||
{
|
||||
"action": "load",
|
||||
"id": "load"
|
||||
},
|
||||
{
|
||||
"checkpoint": "settings-pending"
|
||||
},
|
||||
{
|
||||
"complete": "settings.get#1",
|
||||
"params": {
|
||||
"$undefined": true
|
||||
},
|
||||
"reply": {
|
||||
"ok": true,
|
||||
"result": {
|
||||
"settings": {
|
||||
"disabledTuiAgents": ["claude"],
|
||||
"defaultTuiAgent": "codex",
|
||||
"prBotAuthorOverrides": ["bot-user"],
|
||||
"visibleTaskProviders": ["github", "linear"],
|
||||
"hostSettingOverrides": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"complete": "preflight.check#1",
|
||||
"params": {
|
||||
"$undefined": true
|
||||
},
|
||||
"reply": {
|
||||
"ok": true,
|
||||
"result": {
|
||||
"glab": {
|
||||
"installed": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"complete": "linear.status#1",
|
||||
"params": {
|
||||
"$undefined": true
|
||||
},
|
||||
"reply": {
|
||||
"ok": true,
|
||||
"result": {
|
||||
"connected": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"checkpoint": "settled"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-refused",
|
||||
"operation": "settings.home-providers",
|
||||
"family": "settings.home-providers",
|
||||
"sites": ["mobile/src/home/mobile-home-host-requests.ts"],
|
||||
"schedules": ["settings-first"],
|
||||
"steps": [
|
||||
{
|
||||
"action": "load",
|
||||
"id": "load"
|
||||
},
|
||||
{
|
||||
"checkpoint": "settings-pending"
|
||||
},
|
||||
{
|
||||
"complete": "settings.get#1",
|
||||
"params": {
|
||||
"$undefined": true
|
||||
},
|
||||
"reply": {
|
||||
"ok": false,
|
||||
"error": {
|
||||
"code": "refused",
|
||||
"message": "settings refused"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"complete": "preflight.check#1",
|
||||
"params": {
|
||||
"$undefined": true
|
||||
},
|
||||
"reply": {
|
||||
"ok": true,
|
||||
"result": {
|
||||
"glab": {
|
||||
"installed": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"complete": "linear.status#1",
|
||||
"params": {
|
||||
"$undefined": true
|
||||
},
|
||||
"reply": {
|
||||
"ok": true,
|
||||
"result": {
|
||||
"connected": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"checkpoint": "settled"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-transport-error",
|
||||
"operation": "settings.home-providers",
|
||||
"family": "settings.home-providers",
|
||||
"sites": ["mobile/src/home/mobile-home-host-requests.ts"],
|
||||
"schedules": ["settings-first"],
|
||||
"steps": [
|
||||
{
|
||||
"action": "load",
|
||||
"id": "load"
|
||||
},
|
||||
{
|
||||
"checkpoint": "settings-pending"
|
||||
},
|
||||
{
|
||||
"complete": "settings.get#1",
|
||||
"params": {
|
||||
"$undefined": true
|
||||
},
|
||||
"reject": {
|
||||
"message": "settings disconnected",
|
||||
"deliveryUnknown": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"complete": "preflight.check#1",
|
||||
"params": {
|
||||
"$undefined": true
|
||||
},
|
||||
"reply": {
|
||||
"ok": true,
|
||||
"result": {
|
||||
"glab": {
|
||||
"installed": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"complete": "linear.status#1",
|
||||
"params": {
|
||||
"$undefined": true
|
||||
},
|
||||
"reply": {
|
||||
"ok": true,
|
||||
"result": {
|
||||
"connected": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"checkpoint": "settled"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "settings-resume-metadata-fulfilled",
|
||||
"operation": "settings.resume-metadata",
|
||||
@@ -2263,127 +2087,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "settings-home-coalesced",
|
||||
"operation": "settings.home-providers",
|
||||
"family": "settings.home-providers",
|
||||
"sites": ["mobile/src/home/mobile-home-host-requests.ts"],
|
||||
"schedules": ["stale-inflight-cleanup", "follow-up-cache-observation"],
|
||||
"steps": [
|
||||
{
|
||||
"action": "load",
|
||||
"id": "load"
|
||||
},
|
||||
{
|
||||
"action": "load",
|
||||
"id": "overlapping-load"
|
||||
},
|
||||
{
|
||||
"checkpoint": "settings-pending"
|
||||
},
|
||||
{
|
||||
"complete": "settings.get#1",
|
||||
"params": {
|
||||
"$undefined": true
|
||||
},
|
||||
"reply": {
|
||||
"ok": true,
|
||||
"result": {
|
||||
"settings": {
|
||||
"disabledTuiAgents": ["claude"],
|
||||
"defaultTuiAgent": "codex",
|
||||
"prBotAuthorOverrides": ["bot-user"],
|
||||
"visibleTaskProviders": ["github", "linear"],
|
||||
"hostSettingOverrides": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"complete": "preflight.check#1",
|
||||
"params": {
|
||||
"$undefined": true
|
||||
},
|
||||
"reply": {
|
||||
"ok": true,
|
||||
"result": {
|
||||
"glab": {
|
||||
"installed": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"complete": "linear.status#1",
|
||||
"params": {
|
||||
"$undefined": true
|
||||
},
|
||||
"reply": {
|
||||
"ok": true,
|
||||
"result": {
|
||||
"connected": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"checkpoint": "settled"
|
||||
},
|
||||
{
|
||||
"complete": "settings.get#2",
|
||||
"params": {
|
||||
"$undefined": true
|
||||
},
|
||||
"reply": {
|
||||
"ok": true,
|
||||
"result": {
|
||||
"settings": {
|
||||
"disabledTuiAgents": ["claude"],
|
||||
"defaultTuiAgent": "codex",
|
||||
"prBotAuthorOverrides": ["bot-user"],
|
||||
"visibleTaskProviders": ["github", "linear"],
|
||||
"hostSettingOverrides": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"complete": "preflight.check#2",
|
||||
"params": {
|
||||
"$undefined": true
|
||||
},
|
||||
"reply": {
|
||||
"ok": true,
|
||||
"result": {
|
||||
"glab": {
|
||||
"installed": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"complete": "linear.status#2",
|
||||
"params": {
|
||||
"$undefined": true
|
||||
},
|
||||
"reply": {
|
||||
"ok": true,
|
||||
"result": {
|
||||
"connected": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"checkpoint": "follow-up-settled"
|
||||
},
|
||||
{
|
||||
"action": "load",
|
||||
"id": "third-load"
|
||||
},
|
||||
{
|
||||
"checkpoint": "third-query"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "settings-repo-cache-expiry",
|
||||
"operation": "settings.repo-metadata",
|
||||
@@ -2977,121 +2680,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "settings-home-providers-refuse-after-data",
|
||||
"operation": "settings.home-providers",
|
||||
"family": "settings.home-providers",
|
||||
"sites": ["mobile/src/home/mobile-home-host-requests.ts"],
|
||||
"schedules": ["success-then-refused"],
|
||||
"steps": [
|
||||
{
|
||||
"action": "load",
|
||||
"id": "load"
|
||||
},
|
||||
{
|
||||
"checkpoint": "settings-pending"
|
||||
},
|
||||
{
|
||||
"complete": "settings.get#1",
|
||||
"params": {
|
||||
"$undefined": true
|
||||
},
|
||||
"reply": {
|
||||
"ok": true,
|
||||
"result": {
|
||||
"settings": {
|
||||
"disabledTuiAgents": ["claude"],
|
||||
"defaultTuiAgent": "codex",
|
||||
"prBotAuthorOverrides": ["bot-user"],
|
||||
"visibleTaskProviders": ["github", "linear"],
|
||||
"hostSettingOverrides": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"complete": "preflight.check#1",
|
||||
"params": {
|
||||
"$undefined": true
|
||||
},
|
||||
"reply": {
|
||||
"ok": true,
|
||||
"result": {
|
||||
"glab": {
|
||||
"installed": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"complete": "linear.status#1",
|
||||
"params": {
|
||||
"$undefined": true
|
||||
},
|
||||
"reply": {
|
||||
"ok": true,
|
||||
"result": {
|
||||
"connected": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"checkpoint": "settled"
|
||||
},
|
||||
{
|
||||
"checkpoint": "data-present"
|
||||
},
|
||||
{
|
||||
"action": "load",
|
||||
"id": "reload"
|
||||
},
|
||||
{
|
||||
"checkpoint": "refresh-pending"
|
||||
},
|
||||
{
|
||||
"complete": "settings.get#2",
|
||||
"params": {
|
||||
"$undefined": true
|
||||
},
|
||||
"reply": {
|
||||
"ok": false,
|
||||
"error": {
|
||||
"code": "refused",
|
||||
"message": "settings refused"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"complete": "preflight.check#2",
|
||||
"params": {
|
||||
"$undefined": true
|
||||
},
|
||||
"reply": {
|
||||
"ok": true,
|
||||
"result": {
|
||||
"glab": {
|
||||
"installed": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"complete": "linear.status#2",
|
||||
"params": {
|
||||
"$undefined": true
|
||||
},
|
||||
"reply": {
|
||||
"ok": true,
|
||||
"result": {
|
||||
"connected": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"checkpoint": "refused-after-data"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "settings-resume-metadata-refuse-after-data",
|
||||
"operation": "settings.resume-metadata",
|
||||
@@ -8058,38 +7646,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "home-host-stats",
|
||||
"operation": "home.host-stats",
|
||||
"family": "home.host-stats",
|
||||
"sites": ["mobile/src/home/mobile-home-host-requests.ts"],
|
||||
"schedules": [],
|
||||
"steps": [
|
||||
{
|
||||
"action": "load",
|
||||
"id": "load"
|
||||
},
|
||||
{
|
||||
"checkpoint": "stats-pending"
|
||||
},
|
||||
{
|
||||
"complete": "stats.summary#1",
|
||||
"params": {
|
||||
"$undefined": true
|
||||
},
|
||||
"reply": {
|
||||
"ok": true,
|
||||
"result": {
|
||||
"totalWorktrees": 3,
|
||||
"activeWorktrees": 1
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"checkpoint": "settled"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "worktree-home-catalog",
|
||||
"operation": "worktree.home-catalog",
|
||||
@@ -19858,56 +19414,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "home-host-accounts",
|
||||
"operation": "home.host-accounts",
|
||||
"family": "home.host-accounts",
|
||||
"sites": ["mobile/src/home/mobile-home-host-requests.ts"],
|
||||
"schedules": [],
|
||||
"steps": [
|
||||
{
|
||||
"action": "load",
|
||||
"id": "load"
|
||||
},
|
||||
{
|
||||
"checkpoint": "accounts-pending"
|
||||
},
|
||||
{
|
||||
"complete": "accounts.list#1",
|
||||
"params": {
|
||||
"$undefined": true
|
||||
},
|
||||
"reply": {
|
||||
"ok": true,
|
||||
"result": {
|
||||
"claude": {
|
||||
"accounts": [
|
||||
{
|
||||
"id": "claude-1",
|
||||
"email": "claude@example.test",
|
||||
"updatedAt": 1700000000000
|
||||
}
|
||||
],
|
||||
"activeAccountId": "claude-1"
|
||||
},
|
||||
"codex": {
|
||||
"accounts": [],
|
||||
"activeAccountId": null
|
||||
},
|
||||
"rateLimits": {
|
||||
"claude": null,
|
||||
"codex": null,
|
||||
"inactiveClaudeAccounts": [],
|
||||
"inactiveCodexAccounts": []
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"checkpoint": "accounts-published"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "notifications-display-test-accepted",
|
||||
"operation": "notifications.display-test-screen",
|
||||
|
||||
@@ -42,19 +42,6 @@
|
||||
"settings-workspace-context-transport-error"
|
||||
]
|
||||
},
|
||||
{
|
||||
"manifestOperation": "main:mobile/src/home/mobile-home-host-requests.ts:fetchMobileHomeTaskProviders:settings.get:76",
|
||||
"manifestScenarioIds": [
|
||||
"main:mobile/src/home/mobile-home-host-requests.ts:fetchMobileHomeTaskProviders:settings.get:76:fulfilled",
|
||||
"main:mobile/src/home/mobile-home-host-requests.ts:fetchMobileHomeTaskProviders:settings.get:76:refused",
|
||||
"main:mobile/src/home/mobile-home-host-requests.ts:fetchMobileHomeTaskProviders:settings.get:76:transport-error"
|
||||
],
|
||||
"scenarios": [
|
||||
"settings-home-providers-fulfilled",
|
||||
"settings-home-providers-refused",
|
||||
"settings-home-providers-transport-error"
|
||||
]
|
||||
},
|
||||
{
|
||||
"manifestOperation": "main:mobile/src/host-screen/use-host-repo-metadata.ts:fetchRepoMetadata:settings.get:124",
|
||||
"manifestScenarioIds": [
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
import { useCallback } from 'react'
|
||||
import { useOpenHostStackRoute } from '../navigation/use-open-host-stack-route'
|
||||
import { mobileAccountsRouteTarget } from './mobile-accounts-route'
|
||||
|
||||
export function useOpenMobileAccounts(): (hostId: string) => void {
|
||||
const openHostStackRoute = useOpenHostStackRoute()
|
||||
|
||||
return useCallback(
|
||||
(hostId) => {
|
||||
openHostStackRoute(hostId, mobileAccountsRouteTarget(hostId))
|
||||
},
|
||||
[openHostStackRoute]
|
||||
)
|
||||
}
|
||||
+1
-3
@@ -1,6 +1,5 @@
|
||||
// Seed cold-start/resume cards from persisted last-good snapshot, then update via RPC; avoids ~1s empty flash while reconnecting.
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage'
|
||||
import type { AccountsSnapshot } from '../components/AccountUsage'
|
||||
// Persist canonical countsProvenAt so rehydrated counts retain freshness evidence.
|
||||
import type { HostWorktreeInfo } from '../worktree/home-worktree-info'
|
||||
|
||||
@@ -8,7 +7,6 @@ const STORAGE_KEY = 'orca:home-snapshot:v1'
|
||||
|
||||
export type HomeSnapshot = {
|
||||
worktreeInfo: Record<string, HostWorktreeInfo>
|
||||
accountsByHost: Record<string, AccountsSnapshot>
|
||||
savedAt: number
|
||||
}
|
||||
|
||||
@@ -29,7 +27,7 @@ export async function loadHomeSnapshot(): Promise<HomeSnapshot | null> {
|
||||
typeof parsed !== 'object' ||
|
||||
parsed === null ||
|
||||
typeof parsed.worktreeInfo !== 'object' ||
|
||||
typeof parsed.accountsByHost !== 'object'
|
||||
parsed.worktreeInfo === null
|
||||
) {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import { useMobileColorScheme } from '../theme/use-mobile-color-scheme'
|
||||
import { useWorkingRingRotation } from './use-working-ring-rotation'
|
||||
|
||||
// Match desktop sidebar StatusIndicator colors/sizes for the worktree rollup; palette distinguishes moving/alive/completed states.
|
||||
const statusColors = (): Record<WorktreeStatus, string> => ({
|
||||
export const agentStatusColors = (): Record<WorktreeStatus, string> => ({
|
||||
working: '#eab308',
|
||||
monitoring: '#eab308',
|
||||
active: '#10b981',
|
||||
@@ -34,7 +34,7 @@ export function AgentSpinner({
|
||||
status === 'monitoring' || (status === 'working' && workingMode === 'monitoring')
|
||||
const rotate = useWorkingRingRotation(status === 'working' && !monitoring)
|
||||
|
||||
const palette = statusColors()
|
||||
const palette = agentStatusColors()
|
||||
const color = palette[status] ?? palette.inactive
|
||||
|
||||
if (monitoring) {
|
||||
@@ -93,7 +93,7 @@ const styles = themedStyleSheet(() => ({
|
||||
borderRadius: radii.pill,
|
||||
borderWidth: 1.5,
|
||||
borderStyle: 'dashed',
|
||||
borderColor: statusColors().unconfirmed
|
||||
borderColor: agentStatusColors().unconfirmed
|
||||
},
|
||||
spinner: {
|
||||
width: 8,
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
import { createElement } from 'react'
|
||||
import { act, create, type ReactTestRenderer } from 'react-test-renderer'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { HostProfile } from '../transport/types'
|
||||
import { MobileHomeQuickActions } from './MobileHomeQuickActions'
|
||||
import { findAllByHostType } from '../test-support/react-test-renderer-host-nodes'
|
||||
|
||||
vi.mock('react-native', () => ({
|
||||
Pressable: 'Pressable',
|
||||
StyleSheet: { create: (styles: unknown) => styles },
|
||||
Text: 'Text',
|
||||
View: 'View'
|
||||
}))
|
||||
|
||||
vi.mock('lucide-react-native', () => ({
|
||||
Plus: 'Plus',
|
||||
QrCode: 'QrCode'
|
||||
}))
|
||||
|
||||
describe('MobileHomeQuickActions', () => {
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
|
||||
afterEach(() => {
|
||||
act(() => renderer?.unmount())
|
||||
renderer = null
|
||||
vi.restoreAllMocks()
|
||||
})
|
||||
|
||||
async function renderQuickActions(connectedHosts: HostProfile[]) {
|
||||
const onPairDesktop = vi.fn()
|
||||
const onCreateWorkspace = vi.fn()
|
||||
const consoleError = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
if (typeof args[0] !== 'string' || !args[0].includes('react-test-renderer is deprecated')) {
|
||||
throw new Error(String(args[0]))
|
||||
}
|
||||
})
|
||||
await act(async () => {
|
||||
renderer = create(
|
||||
createElement(MobileHomeQuickActions, { connectedHosts, onPairDesktop, onCreateWorkspace })
|
||||
)
|
||||
})
|
||||
consoleError.mockRestore()
|
||||
}
|
||||
|
||||
function newWorkspaceButton() {
|
||||
return findAllByHostType(renderer!.root, 'Pressable')[1]
|
||||
}
|
||||
|
||||
it('disables workspace creation without a connected host', async () => {
|
||||
await renderQuickActions([])
|
||||
|
||||
expect(newWorkspaceButton().props.disabled).toBe(true)
|
||||
expect(newWorkspaceButton().props.accessibilityState).toEqual({ disabled: true })
|
||||
})
|
||||
})
|
||||
@@ -1,107 +0,0 @@
|
||||
import { Plus, QrCode } from 'lucide-react-native'
|
||||
import { Pressable, Text, View } from 'react-native'
|
||||
import { useMobileTranslation } from '../i18n/use-mobile-translation'
|
||||
import type { HostProfile } from '../transport/types'
|
||||
import { colors, radii, spacing } from '../theme/mobile-theme'
|
||||
import { themedStyleSheet } from '../theme/themed-styles'
|
||||
import { useMobileColorScheme } from '../theme/use-mobile-color-scheme'
|
||||
|
||||
type Props = {
|
||||
connectedHosts: HostProfile[]
|
||||
onPairDesktop: () => void
|
||||
onCreateWorkspace: () => void
|
||||
}
|
||||
|
||||
export function MobileHomeQuickActions(props: Props) {
|
||||
const t = useMobileTranslation()
|
||||
useMobileColorScheme()
|
||||
const canCreateWorkspace = props.connectedHosts.length > 0
|
||||
|
||||
return (
|
||||
<>
|
||||
<Text style={styles.sectionHeading}>
|
||||
{t('components.homeQuickActions.title', 'Quick Actions')}
|
||||
</Text>
|
||||
<View style={styles.quickActions}>
|
||||
<Pressable
|
||||
accessibilityRole="button"
|
||||
style={({ pressed }) => [styles.quickAction, pressed && styles.quickActionPressed]}
|
||||
onPress={props.onPairDesktop}
|
||||
>
|
||||
<View style={styles.quickActionIcon}>
|
||||
<QrCode size={16} color={colors.textSecondary} />
|
||||
</View>
|
||||
<Text style={styles.quickActionLabel}>
|
||||
{t('components.homeQuickActions.pairDesktop', 'Pair Desktop')}
|
||||
</Text>
|
||||
</Pressable>
|
||||
<Pressable
|
||||
accessibilityRole="button"
|
||||
accessibilityState={{ disabled: !canCreateWorkspace }}
|
||||
disabled={!canCreateWorkspace}
|
||||
style={({ pressed }) => [
|
||||
styles.quickAction,
|
||||
!canCreateWorkspace && styles.quickActionDisabled,
|
||||
pressed && styles.quickActionPressed
|
||||
]}
|
||||
onPress={props.onCreateWorkspace}
|
||||
>
|
||||
<View style={styles.quickActionIcon}>
|
||||
<Plus size={16} color={colors.textSecondary} />
|
||||
</View>
|
||||
<Text style={styles.quickActionLabel}>
|
||||
{t('components.homeQuickActions.newWorkspace', 'New Workspace')}
|
||||
</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = themedStyleSheet(() => ({
|
||||
sectionHeading: {
|
||||
marginTop: spacing.xl,
|
||||
marginBottom: spacing.sm,
|
||||
paddingHorizontal: spacing.xs,
|
||||
color: colors.textMuted,
|
||||
fontSize: 11,
|
||||
fontWeight: '600',
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: 0.6
|
||||
},
|
||||
quickActions: {
|
||||
flexDirection: 'row',
|
||||
gap: spacing.sm
|
||||
},
|
||||
quickAction: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: spacing.sm + 2,
|
||||
paddingHorizontal: spacing.md,
|
||||
paddingVertical: spacing.sm + 2,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.borderSubtle,
|
||||
borderRadius: radii.surface,
|
||||
backgroundColor: colors.bgPanel
|
||||
},
|
||||
quickActionPressed: {
|
||||
backgroundColor: colors.bgRaised
|
||||
},
|
||||
quickActionDisabled: {
|
||||
opacity: 0.45
|
||||
},
|
||||
quickActionIcon: {
|
||||
width: 28,
|
||||
height: 28,
|
||||
borderRadius: radii.tile,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: colors.bgRaised
|
||||
},
|
||||
quickActionLabel: {
|
||||
color: colors.textSecondary,
|
||||
fontSize: 12,
|
||||
fontWeight: '600'
|
||||
}
|
||||
}))
|
||||
@@ -1,166 +0,0 @@
|
||||
import { createElement } from 'react'
|
||||
import { act, create, type ReactTestRenderer } from 'react-test-renderer'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { ConnectionVerdict } from '../transport/connection-health'
|
||||
import type { MobileConnectionPath } from '../transport/stable-logical-rpc-client'
|
||||
import type { ConnectionState, HostCredentialStatus, HostProfile } from '../transport/types'
|
||||
import {
|
||||
markHomeWorktreeCatalogUnavailable,
|
||||
type HostWorktreeInfo
|
||||
} from '../worktree/home-worktree-info'
|
||||
import { MobileHostCard } from './MobileHostCard'
|
||||
import { findAllByHostType } from '../test-support/react-test-renderer-host-nodes'
|
||||
|
||||
vi.mock('react-native', () => ({
|
||||
Pressable: 'Pressable',
|
||||
StyleSheet: { create: <T,>(styles: T) => styles },
|
||||
Text: 'Text',
|
||||
View: 'View'
|
||||
}))
|
||||
vi.mock('lucide-react-native', () => ({ Monitor: 'Monitor', MoreVertical: 'MoreVertical' }))
|
||||
vi.mock('./StatusDot', () => ({ StatusDot: 'StatusDot' }))
|
||||
|
||||
const host: HostProfile = {
|
||||
id: 'host-1',
|
||||
name: 'Studio',
|
||||
endpoint: 'ws://studio.local:8765',
|
||||
deviceToken: 'token',
|
||||
publicKeyB64: 'key',
|
||||
lastConnected: 0
|
||||
}
|
||||
const verdict: ConnectionVerdict = { kind: 'normal', label: 'Connected' }
|
||||
const loaded: HostWorktreeInfo = {
|
||||
hostId: 'host-1',
|
||||
totalWorktrees: 12,
|
||||
activeCount: 2,
|
||||
lastActiveWorktree: null,
|
||||
countsProvenAt: Date.now()
|
||||
}
|
||||
|
||||
describe('MobileHostCard', () => {
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
|
||||
afterEach(() => {
|
||||
act(() => renderer?.unmount())
|
||||
renderer = null
|
||||
})
|
||||
|
||||
async function renderCard(
|
||||
worktreeInfo: HostWorktreeInfo | undefined,
|
||||
overrides?: {
|
||||
state?: ConnectionState
|
||||
verdict?: ConnectionVerdict
|
||||
path?: MobileConnectionPath
|
||||
credentialStatus?: HostCredentialStatus
|
||||
}
|
||||
): Promise<string[]> {
|
||||
await act(async () => {
|
||||
renderer = create(
|
||||
createElement(MobileHostCard, {
|
||||
host,
|
||||
state: overrides?.state ?? 'connected',
|
||||
verdict: overrides?.verdict ?? verdict,
|
||||
path: overrides?.path ?? 'lan',
|
||||
credentialStatus: overrides?.credentialStatus,
|
||||
worktreeInfo,
|
||||
onPress: () => {},
|
||||
onLongPress: () => {},
|
||||
onOpenActions: () => {}
|
||||
})
|
||||
)
|
||||
})
|
||||
return findAllByHostType(renderer!.root, 'Text').flatMap((node) =>
|
||||
node.children.filter((child) => typeof child === 'string')
|
||||
)
|
||||
}
|
||||
|
||||
it('renders the counts the host proved', async () => {
|
||||
expect(await renderCard(loaded)).toContain('12 worktrees · 2 active')
|
||||
})
|
||||
|
||||
it('keeps rendering the last proven counts after a failed refresh', async () => {
|
||||
// Regress caller dropping counts deliberately preserved on failure.
|
||||
expect(await renderCard(markHomeWorktreeCatalogUnavailable(loaded, 'host-1'))).toContain(
|
||||
'Last known: 12 worktrees · 2 active'
|
||||
)
|
||||
})
|
||||
|
||||
it('never asserts a count for a catalog that failed with nothing proven', async () => {
|
||||
expect(await renderCard(markHomeWorktreeCatalogUnavailable(undefined, 'host-1'))).toContain(
|
||||
'Worktree list unavailable'
|
||||
)
|
||||
})
|
||||
|
||||
it('names the relay while the dial is still in flight', async () => {
|
||||
const lines = await renderCard(undefined, {
|
||||
state: 'connecting',
|
||||
verdict: { kind: 'normal', label: 'Connecting via Relay…', dialing: true },
|
||||
path: 'relay'
|
||||
})
|
||||
|
||||
expect(lines).toContain('Connecting via Relay…')
|
||||
expect(lines).not.toContain(' · Orca Relay')
|
||||
})
|
||||
|
||||
it('names the relay while a failed direct dial is still retrying', async () => {
|
||||
const lines = await renderCard(undefined, {
|
||||
state: 'reconnecting',
|
||||
verdict: { kind: 'normal', label: 'Connecting via Relay…', dialing: true },
|
||||
path: 'relay'
|
||||
})
|
||||
|
||||
expect(lines).toContain('Connecting via Relay…')
|
||||
expect(lines).not.toContain(' · Orca Relay')
|
||||
})
|
||||
|
||||
it('leaves an idle disconnected host unlabelled', async () => {
|
||||
const lines = await renderCard(undefined, {
|
||||
state: 'disconnected',
|
||||
verdict: { kind: 'normal', label: 'Disconnected' },
|
||||
path: 'relay'
|
||||
})
|
||||
|
||||
expect(lines).not.toContain(' · Orca Relay')
|
||||
})
|
||||
|
||||
it('does not guess a direct path before the dial resolves', async () => {
|
||||
const lines = await renderCard(undefined, {
|
||||
state: 'connecting',
|
||||
verdict: { kind: 'normal', label: 'Connecting…', dialing: true },
|
||||
path: 'lan'
|
||||
})
|
||||
|
||||
expect(lines).not.toContain(' · Direct · LAN')
|
||||
})
|
||||
|
||||
it('shows no worktree line before the first read lands', async () => {
|
||||
const lines = await renderCard(undefined)
|
||||
|
||||
expect(lines).not.toContain('0 worktrees')
|
||||
expect(lines).not.toContain('Worktree list unavailable')
|
||||
})
|
||||
|
||||
it('offers re-pairing when the credential is missing', async () => {
|
||||
const lines = await renderCard(loaded, {
|
||||
state: 'connected',
|
||||
verdict: { kind: 'auth-failed', label: 'Pairing invalid' },
|
||||
credentialStatus: 'missing'
|
||||
})
|
||||
|
||||
expect(lines).toContain('Pairing invalid')
|
||||
expect(lines).toContain('Tap to re-pair with your desktop')
|
||||
expect(lines).not.toContain('12 worktrees · 2 active')
|
||||
})
|
||||
|
||||
it('offers a retry without declaring a transient read failure invalid', async () => {
|
||||
const lines = await renderCard(undefined, {
|
||||
state: 'disconnected',
|
||||
verdict: { kind: 'normal', label: 'Disconnected' },
|
||||
credentialStatus: 'temporarily-unavailable'
|
||||
})
|
||||
|
||||
expect(lines).toContain('Pairing temporarily unavailable')
|
||||
expect(lines).toContain('Unlock your phone, then tap to retry')
|
||||
expect(lines).not.toContain('Pairing invalid')
|
||||
})
|
||||
})
|
||||
@@ -1,350 +0,0 @@
|
||||
import { createElement, type ReactElement } from 'react'
|
||||
import { act, create, type ReactTestRenderer } from 'react-test-renderer'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { MobileHostCard } from './MobileHostCard'
|
||||
import {
|
||||
recordHostDescriptor,
|
||||
resetHostDescriptorStoreForTests
|
||||
} from '../transport/host-descriptor-store'
|
||||
import { findAllByHostType } from '../test-support/react-test-renderer-host-nodes'
|
||||
|
||||
vi.mock('react-native', () => ({
|
||||
Pressable: 'Pressable',
|
||||
StyleSheet: { create: (styles: unknown) => styles },
|
||||
Text: 'Text',
|
||||
View: 'View'
|
||||
}))
|
||||
|
||||
vi.mock('lucide-react-native', () => ({
|
||||
Monitor: 'Monitor',
|
||||
MoreVertical: 'MoreVertical'
|
||||
}))
|
||||
|
||||
vi.mock('./StatusDot', () => ({
|
||||
StatusDot: 'StatusDot'
|
||||
}))
|
||||
|
||||
function suppressRendererDeprecation() {
|
||||
return vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
if (typeof args[0] !== 'string' || !args[0].includes('react-test-renderer is deprecated')) {
|
||||
throw new Error(String(args[0]))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function render(element: ReactElement): Promise<ReactTestRenderer> {
|
||||
const consoleError = suppressRendererDeprecation()
|
||||
const mounted: { renderer?: ReactTestRenderer } = {}
|
||||
await act(async () => {
|
||||
mounted.renderer = create(element)
|
||||
})
|
||||
consoleError.mockRestore()
|
||||
if (!mounted.renderer) {
|
||||
throw new Error('MobileHostCard did not mount')
|
||||
}
|
||||
return mounted.renderer
|
||||
}
|
||||
|
||||
describe('MobileHostCard', () => {
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
|
||||
afterEach(() => {
|
||||
act(() => renderer?.unmount())
|
||||
renderer = null
|
||||
resetHostDescriptorStoreForTests()
|
||||
vi.restoreAllMocks()
|
||||
})
|
||||
|
||||
it('keeps host navigation and actions as separate accessible controls', async () => {
|
||||
const onPress = vi.fn()
|
||||
const onLongPress = vi.fn()
|
||||
const onOpenActions = vi.fn()
|
||||
renderer = await render(
|
||||
createElement(MobileHostCard, {
|
||||
host: {
|
||||
id: 'desk',
|
||||
name: 'Desk',
|
||||
endpoint: 'ws://192.168.1.2:6768',
|
||||
deviceToken: 'token',
|
||||
publicKeyB64: 'key',
|
||||
lastConnected: 1
|
||||
},
|
||||
state: 'disconnected',
|
||||
verdict: { kind: 'normal', label: 'Disconnected' },
|
||||
path: 'lan',
|
||||
onPress,
|
||||
onLongPress,
|
||||
onOpenActions
|
||||
})
|
||||
)
|
||||
|
||||
const buttons = findAllByHostType(renderer.root, 'Pressable')
|
||||
expect(buttons).toHaveLength(2)
|
||||
expect(buttons[0].props.accessibilityRole).toBe('button')
|
||||
expect(buttons[0].props.accessibilityLabel).toBe('Open Desk, Disconnected')
|
||||
expect(buttons[1].props.accessibilityRole).toBe('button')
|
||||
expect(buttons[1].props.accessibilityLabel).toBe('Actions for Desk')
|
||||
|
||||
act(() => buttons[1].props.onPress())
|
||||
expect(onOpenActions).toHaveBeenCalledOnce()
|
||||
expect(onPress).not.toHaveBeenCalled()
|
||||
|
||||
act(() => buttons[0].props.onPress())
|
||||
act(() => buttons[0].props.onLongPress())
|
||||
expect(onPress).toHaveBeenCalledOnce()
|
||||
expect(onLongPress).toHaveBeenCalledOnce()
|
||||
})
|
||||
|
||||
it('announces the connection path without the visual separator', async () => {
|
||||
renderer = await render(
|
||||
createElement(MobileHostCard, {
|
||||
host: {
|
||||
id: 'desk',
|
||||
name: 'Desk',
|
||||
endpoint: 'ws://192.168.1.2:6768',
|
||||
deviceToken: 'token',
|
||||
publicKeyB64: 'key',
|
||||
lastConnected: 1
|
||||
},
|
||||
state: 'connected',
|
||||
verdict: { kind: 'normal', label: 'Connected' },
|
||||
path: 'tailscale',
|
||||
worktreeInfo: {
|
||||
hostId: 'desk',
|
||||
totalWorktrees: 3,
|
||||
activeCount: 2,
|
||||
lastActiveWorktree: null,
|
||||
countsProvenAt: Date.now()
|
||||
},
|
||||
onPress: vi.fn(),
|
||||
onLongPress: vi.fn(),
|
||||
onOpenActions: vi.fn()
|
||||
})
|
||||
)
|
||||
|
||||
const navigationButton = findAllByHostType(renderer.root, 'Pressable')[0]
|
||||
expect(navigationButton.props.accessibilityLabel).toBe(
|
||||
'Open Desk, Connected, Direct via Tailscale, 3 worktrees, 2 active'
|
||||
)
|
||||
})
|
||||
|
||||
it('keeps a phone rename above a disagreeing machine descriptor', async () => {
|
||||
recordHostDescriptor('desk', { machineName: 'm4airs-Air', platform: 'darwin' })
|
||||
renderer = await render(
|
||||
createElement(MobileHostCard, {
|
||||
host: {
|
||||
id: 'desk',
|
||||
name: 'Windows-Low Spec',
|
||||
personalName: 'Windows-Low Spec',
|
||||
endpoint: 'ws://192.168.1.2:6768',
|
||||
deviceToken: 'token',
|
||||
publicKeyB64: 'key',
|
||||
lastConnected: 1
|
||||
},
|
||||
state: 'connected',
|
||||
verdict: { kind: 'normal', label: 'Connected' },
|
||||
path: 'lan',
|
||||
onPress: vi.fn(),
|
||||
onLongPress: vi.fn(),
|
||||
onOpenActions: vi.fn()
|
||||
})
|
||||
)
|
||||
|
||||
const texts = findAllByHostType(renderer.root, 'Text').map((node) => node.children.join(''))
|
||||
expect(texts).toContain('Windows-Low Spec')
|
||||
expect(texts).toContain('macOS · m4airs-Air')
|
||||
})
|
||||
|
||||
it('titles an unrenamed host with the live machine name before its stored name catches up', async () => {
|
||||
// The desktop was renamed; the stored `name` still holds the old machine name until reload.
|
||||
recordHostDescriptor('desk', { machineName: 'Studio 2', platform: 'darwin' })
|
||||
renderer = await render(
|
||||
createElement(MobileHostCard, {
|
||||
host: {
|
||||
id: 'desk',
|
||||
name: 'Studio',
|
||||
lastKnownMachineName: 'Studio',
|
||||
lastKnownHostPlatform: 'darwin',
|
||||
endpoint: 'ws://192.168.1.2:6768',
|
||||
deviceToken: 'token',
|
||||
publicKeyB64: 'key',
|
||||
lastConnected: 1
|
||||
},
|
||||
state: 'connected',
|
||||
verdict: { kind: 'normal', label: 'Connected' },
|
||||
path: 'lan',
|
||||
onPress: vi.fn(),
|
||||
onLongPress: vi.fn(),
|
||||
onOpenActions: vi.fn()
|
||||
})
|
||||
)
|
||||
|
||||
const texts = findAllByHostType(renderer.root, 'Text').map((node) => node.children.join(''))
|
||||
expect(texts).toContain('Studio 2')
|
||||
expect(texts).toContain('macOS')
|
||||
expect(texts).not.toContain('Studio')
|
||||
})
|
||||
|
||||
it('shows the stored descriptor when the host is offline, as after a restart', async () => {
|
||||
// No live descriptor is recorded: the row has only what the stored profile carries.
|
||||
renderer = await render(
|
||||
createElement(MobileHostCard, {
|
||||
host: {
|
||||
id: 'desk',
|
||||
name: 'Desk',
|
||||
personalName: 'Desk',
|
||||
lastKnownMachineName: 'm4airs-Air',
|
||||
lastKnownHostPlatform: 'darwin',
|
||||
endpoint: 'ws://192.168.1.2:6768',
|
||||
deviceToken: 'token',
|
||||
publicKeyB64: 'key',
|
||||
lastConnected: 1
|
||||
},
|
||||
state: 'disconnected',
|
||||
verdict: { kind: 'normal', label: 'Disconnected' },
|
||||
path: 'lan',
|
||||
onPress: vi.fn(),
|
||||
onLongPress: vi.fn(),
|
||||
onOpenActions: vi.fn()
|
||||
})
|
||||
)
|
||||
|
||||
expect(
|
||||
findAllByHostType(renderer.root, 'Text').map((node) => node.children.join(''))
|
||||
).toContain('macOS · m4airs-Air')
|
||||
})
|
||||
|
||||
it('collapses the machine name into the OS line when it is the shown name', async () => {
|
||||
recordHostDescriptor('desk', { machineName: 'Desk', platform: 'darwin' })
|
||||
renderer = await render(
|
||||
createElement(MobileHostCard, {
|
||||
host: {
|
||||
id: 'desk',
|
||||
name: 'Desk',
|
||||
endpoint: 'ws://192.168.1.2:6768',
|
||||
deviceToken: 'token',
|
||||
publicKeyB64: 'key',
|
||||
lastConnected: 1
|
||||
},
|
||||
state: 'connected',
|
||||
verdict: { kind: 'normal', label: 'Connected' },
|
||||
path: 'lan',
|
||||
onPress: vi.fn(),
|
||||
onLongPress: vi.fn(),
|
||||
onOpenActions: vi.fn()
|
||||
})
|
||||
)
|
||||
|
||||
const texts = findAllByHostType(renderer.root, 'Text').map((node) => node.children.join(''))
|
||||
expect(texts).toContain('macOS')
|
||||
expect(texts).not.toContain('macOS · Desk')
|
||||
})
|
||||
|
||||
it('preserves the connected worktree-catalog failure state', async () => {
|
||||
renderer = await render(
|
||||
createElement(MobileHostCard, {
|
||||
host: {
|
||||
id: 'desk',
|
||||
name: 'Desk',
|
||||
endpoint: 'ws://192.168.1.2:6768',
|
||||
deviceToken: 'token',
|
||||
publicKeyB64: 'key',
|
||||
lastConnected: 1
|
||||
},
|
||||
state: 'connected',
|
||||
verdict: { kind: 'normal', label: 'Connected' },
|
||||
path: 'relay',
|
||||
worktreeInfo: {
|
||||
hostId: 'desk',
|
||||
totalWorktrees: 0,
|
||||
activeCount: 0,
|
||||
lastActiveWorktree: null,
|
||||
catalogUnavailable: true
|
||||
},
|
||||
onPress: vi.fn(),
|
||||
onLongPress: vi.fn(),
|
||||
onOpenActions: vi.fn()
|
||||
})
|
||||
)
|
||||
|
||||
const navigationButton = findAllByHostType(renderer.root, 'Pressable')[0]
|
||||
expect(navigationButton.props.accessibilityLabel).toBe(
|
||||
'Open Desk, Connected, Orca Relay, Worktree list unavailable'
|
||||
)
|
||||
expect(
|
||||
findAllByHostType(renderer.root, 'Text').some((node) =>
|
||||
node.children.includes('Worktree list unavailable')
|
||||
)
|
||||
).toBe(true)
|
||||
})
|
||||
|
||||
it('includes visible offline recovery guidance in the navigation label', async () => {
|
||||
renderer = await render(
|
||||
createElement(MobileHostCard, {
|
||||
host: {
|
||||
id: 'desk',
|
||||
name: 'Desk',
|
||||
endpoint: 'ws://192.168.1.2:6768',
|
||||
deviceToken: 'token',
|
||||
publicKeyB64: 'key',
|
||||
lastConnected: 1
|
||||
},
|
||||
state: 'reconnecting',
|
||||
verdict: {
|
||||
kind: 'unreachable',
|
||||
label: "Can't reach desktop",
|
||||
reason: 'never-connected'
|
||||
},
|
||||
path: 'lan',
|
||||
onPress: vi.fn(),
|
||||
onLongPress: vi.fn(),
|
||||
onOpenActions: vi.fn()
|
||||
})
|
||||
)
|
||||
|
||||
const navigationButton = findAllByHostType(renderer.root, 'Pressable')[0]
|
||||
expect(navigationButton.props.accessibilityLabel).toBe(
|
||||
"Open Desk, Can't reach desktop, Update desktop Orca and sign in to connect from anywhere"
|
||||
)
|
||||
})
|
||||
|
||||
it('renders the verdict detail as a second line and announces it', async () => {
|
||||
renderer = await render(
|
||||
createElement(MobileHostCard, {
|
||||
host: {
|
||||
id: 'desk',
|
||||
name: 'Host 1',
|
||||
endpoint: 'ws://192.168.1.2:6768',
|
||||
deviceToken: 'token',
|
||||
publicKeyB64: 'key',
|
||||
lastConnected: 1,
|
||||
relay: {
|
||||
v: 1 as const,
|
||||
directorUrl: 'https://relay-staging.onorca.dev',
|
||||
cellUrl: 'https://c1.relay-staging.onorca.dev',
|
||||
assignmentEpoch: 4,
|
||||
relayHostId: 'AbCdEf0123_-xyZ9',
|
||||
e2eeFraming: 2 as const
|
||||
}
|
||||
},
|
||||
state: 'connecting',
|
||||
verdict: {
|
||||
kind: 'unreachable',
|
||||
label: 'Host 1 is offline',
|
||||
reason: 'never-connected',
|
||||
detail: "Check it's awake, Orca is running, and you're signed in"
|
||||
},
|
||||
path: 'lan',
|
||||
onPress: vi.fn(),
|
||||
onLongPress: vi.fn(),
|
||||
onOpenActions: vi.fn()
|
||||
})
|
||||
)
|
||||
|
||||
const texts = findAllByHostType(renderer.root, 'Text').map((node) => node.props.children)
|
||||
expect(texts).toContainEqual("Check it's awake, Orca is running, and you're signed in")
|
||||
expect(findAllByHostType(renderer.root, 'Pressable')[0]?.props.accessibilityLabel).toBe(
|
||||
"Open Host 1, Host 1 is offline, Check it's awake, Orca is running, and you're signed in"
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -1,231 +0,0 @@
|
||||
import { Monitor, MoreVertical } from 'lucide-react-native'
|
||||
import { Pressable, Text, View } from 'react-native'
|
||||
import type { Translate } from '../i18n/mobile-i18n'
|
||||
import { useMobileTranslation } from '../i18n/use-mobile-translation'
|
||||
import type { ConnectionVerdict } from '../transport/connection-health'
|
||||
import { verdictDisplayLabel } from '../transport/connection-health'
|
||||
import {
|
||||
mobileConnectionPathAccessibilityLabel,
|
||||
mobileConnectionPathLabel
|
||||
} from '../transport/mobile-connection-path-label'
|
||||
import { useHostDisplay } from '../transport/use-host-display'
|
||||
import type { MobileConnectionPath } from '../transport/stable-logical-rpc-client'
|
||||
import type { ConnectionState, HostCatalogEntry, HostProfile } from '../transport/types'
|
||||
import { colors, radii, spacing } from '../theme/mobile-theme'
|
||||
import { themedStyleSheet } from '../theme/themed-styles'
|
||||
import { useMobileColorScheme } from '../theme/use-mobile-color-scheme'
|
||||
import { homeHostWorktreeSummary, type HostWorktreeInfo } from '../worktree/home-worktree-info'
|
||||
import { StatusDot } from './StatusDot'
|
||||
|
||||
function hostCardWorktreeSummaries(info: HostWorktreeInfo | undefined, t: Translate) {
|
||||
const now = Date.now()
|
||||
const visual = homeHostWorktreeSummary(info, now)
|
||||
const accessibility =
|
||||
info && info.activeCount > 0 && (!info.catalogUnavailable || info.staleCounts)
|
||||
? t(
|
||||
'components.hostCard.worktreeAccessibilitySummary',
|
||||
'{{summary}}, {{activeCount}} active',
|
||||
{
|
||||
summary: homeHostWorktreeSummary({ ...info, activeCount: 0 }, now),
|
||||
activeCount: info.activeCount
|
||||
}
|
||||
)
|
||||
: visual
|
||||
return { visual, accessibility }
|
||||
}
|
||||
|
||||
export function MobileHostCard(props: {
|
||||
host: HostProfile | HostCatalogEntry
|
||||
credentialStatus?: HostCatalogEntry['credentialStatus']
|
||||
state: ConnectionState
|
||||
verdict: ConnectionVerdict
|
||||
path: MobileConnectionPath
|
||||
// Card owns fresh/stale/unavailable wording so callers cannot hide retained counts again (STA-3123).
|
||||
worktreeInfo?: HostWorktreeInfo
|
||||
onPress: () => void
|
||||
onLongPress: () => void
|
||||
onOpenActions: () => void
|
||||
}) {
|
||||
const t = useMobileTranslation()
|
||||
useMobileColorScheme()
|
||||
const credentialUnavailable = props.credentialStatus === 'temporarily-unavailable'
|
||||
const credentialMissing = props.credentialStatus === 'missing'
|
||||
const connected = props.state === 'connected' && !credentialUnavailable && !credentialMissing
|
||||
const isError =
|
||||
credentialMissing || ['warning', 'unreachable', 'auth-failed'].includes(props.verdict.kind)
|
||||
const statusLabel = credentialMissing
|
||||
? t('components.hostCard.pairingInvalid', 'Pairing invalid')
|
||||
: credentialUnavailable
|
||||
? t('components.hostCard.pairingTemporarilyUnavailable', 'Pairing temporarily unavailable')
|
||||
: verdictDisplayLabel(props.verdict)
|
||||
const statusVerdict: ConnectionVerdict = credentialMissing
|
||||
? { kind: 'auth-failed', label: statusLabel }
|
||||
: credentialUnavailable
|
||||
? { kind: 'warning', label: statusLabel }
|
||||
: props.verdict
|
||||
const worktreeSummary = hostCardWorktreeSummaries(props.worktreeInfo, t)
|
||||
const display = useHostDisplay(props.host)
|
||||
const descriptorText = display.descriptorLine
|
||||
const connectionPathLabel =
|
||||
!credentialMissing && !credentialUnavailable && connected
|
||||
? mobileConnectionPathLabel(props.path)
|
||||
: null
|
||||
const discoveryHint =
|
||||
props.verdict.kind === 'unreachable' && !props.host.relay
|
||||
? t(
|
||||
'components.hostCard.updateDesktopHint',
|
||||
'Update desktop Orca and sign in to connect from anywhere'
|
||||
)
|
||||
: null
|
||||
const credentialHint = credentialMissing
|
||||
? t('components.hostCard.repairHint', 'Tap to re-pair with your desktop')
|
||||
: credentialUnavailable
|
||||
? t('components.hostCard.unlockHint', 'Unlock your phone, then tap to retry')
|
||||
: null
|
||||
// The verdict's own second line (what to check on the desktop); credential copy wins.
|
||||
const verdictDetail =
|
||||
credentialHint === null && 'detail' in props.verdict ? (props.verdict.detail ?? null) : null
|
||||
const accessibilityLabel = [
|
||||
t('components.hostCard.openHost', 'Open {{title}}', { title: display.title }),
|
||||
descriptorText,
|
||||
statusLabel,
|
||||
connectionPathLabel ? mobileConnectionPathAccessibilityLabel(props.path) : null,
|
||||
connected ? worktreeSummary.accessibility : null,
|
||||
discoveryHint,
|
||||
credentialHint,
|
||||
verdictDetail
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(', ')
|
||||
return (
|
||||
<View style={styles.card}>
|
||||
<Pressable
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={accessibilityLabel}
|
||||
style={({ pressed }) => [styles.cardMain, pressed && styles.cardPressed]}
|
||||
onPress={props.onPress}
|
||||
onLongPress={props.onLongPress}
|
||||
delayLongPress={400}
|
||||
>
|
||||
<View style={styles.icon}>
|
||||
<Monitor size={20} color={connected ? colors.textPrimary : colors.textSecondary} />
|
||||
</View>
|
||||
<View style={styles.main}>
|
||||
<Text
|
||||
style={[styles.name, !connected && { color: colors.textSecondary }]}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{display.title}
|
||||
</Text>
|
||||
{descriptorText ? (
|
||||
<Text style={styles.platformText} numberOfLines={1}>
|
||||
{descriptorText}
|
||||
</Text>
|
||||
) : null}
|
||||
<View style={styles.meta}>
|
||||
<StatusDot state={props.state} verdict={statusVerdict} />
|
||||
<Text
|
||||
style={[
|
||||
styles.metaText,
|
||||
isError && { color: colors.statusRed },
|
||||
credentialUnavailable && { color: colors.statusAmber }
|
||||
]}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{statusLabel}
|
||||
{connectionPathLabel ? ` · ${connectionPathLabel}` : ''}
|
||||
</Text>
|
||||
</View>
|
||||
{connected && worktreeSummary.visual ? (
|
||||
<Text style={styles.worktreeMetaText} numberOfLines={1}>
|
||||
{worktreeSummary.visual}
|
||||
</Text>
|
||||
) : null}
|
||||
{discoveryHint ? (
|
||||
<Text style={styles.discoveryHint} numberOfLines={2}>
|
||||
{discoveryHint}
|
||||
</Text>
|
||||
) : null}
|
||||
{credentialHint ? (
|
||||
<Text style={styles.discoveryHint} numberOfLines={2}>
|
||||
{credentialHint}
|
||||
</Text>
|
||||
) : null}
|
||||
{verdictDetail ? (
|
||||
<Text style={styles.discoveryHint} numberOfLines={2}>
|
||||
{verdictDetail}
|
||||
</Text>
|
||||
) : null}
|
||||
</View>
|
||||
</Pressable>
|
||||
<Pressable
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={t('components.hostCard.hostActions', 'Actions for {{title}}', {
|
||||
title: display.title
|
||||
})}
|
||||
hitSlop={8}
|
||||
style={({ pressed }) => [styles.actionButton, pressed && styles.actionButtonPressed]}
|
||||
onPress={props.onOpenActions}
|
||||
>
|
||||
<MoreVertical size={18} color={colors.textSecondary} />
|
||||
</Pressable>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = themedStyleSheet(() => ({
|
||||
card: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
borderRadius: radii.surface,
|
||||
backgroundColor: colors.bgPanel,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.borderSubtle,
|
||||
overflow: 'hidden'
|
||||
},
|
||||
cardMain: {
|
||||
flex: 1,
|
||||
minWidth: 0,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
paddingLeft: spacing.md,
|
||||
paddingVertical: 12
|
||||
},
|
||||
cardPressed: { backgroundColor: colors.bgRaised },
|
||||
icon: {
|
||||
width: 46,
|
||||
height: 46,
|
||||
borderRadius: radii.tile,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: colors.bgRaised,
|
||||
marginRight: 14
|
||||
},
|
||||
main: { flex: 1, minWidth: 0, marginRight: spacing.sm },
|
||||
name: { color: colors.textPrimary, fontSize: 15, fontWeight: '600', lineHeight: 20 },
|
||||
platformText: { color: colors.textSecondary, fontSize: 12, lineHeight: 16 },
|
||||
meta: { flexDirection: 'row', alignItems: 'center', gap: 6, marginTop: 3, minWidth: 0 },
|
||||
metaText: { flex: 1, fontSize: 12, color: colors.textSecondary },
|
||||
worktreeMetaText: {
|
||||
marginTop: 2,
|
||||
fontSize: 12,
|
||||
color: colors.textMuted
|
||||
},
|
||||
discoveryHint: {
|
||||
marginTop: spacing.xs,
|
||||
fontSize: 11,
|
||||
lineHeight: 15,
|
||||
color: colors.textMuted
|
||||
},
|
||||
actionButton: {
|
||||
width: 40,
|
||||
height: 40,
|
||||
marginHorizontal: spacing.xs,
|
||||
borderRadius: radii.control,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
actionButtonPressed: {
|
||||
backgroundColor: colors.bgRaised
|
||||
}
|
||||
}))
|
||||
@@ -27,16 +27,12 @@ export function AppUpdateCard(props: {
|
||||
style={({ pressed }) => [styles.cardMain, pressed && styles.cardPressed]}
|
||||
onPress={props.onPress}
|
||||
>
|
||||
<View style={styles.icon}>
|
||||
<ArrowUpFromLine size={20} strokeWidth={1.75} color={colors.textPrimary} />
|
||||
</View>
|
||||
<View style={styles.main}>
|
||||
<Text style={styles.title} numberOfLines={1}>
|
||||
{title}
|
||||
</Text>
|
||||
<Text style={styles.subtitle} numberOfLines={1}>
|
||||
{t('home.appUpdateCard.updateHint', 'Tap to update')}
|
||||
</Text>
|
||||
<ArrowUpFromLine size={20} color={colors.accentBlue} />
|
||||
<Text style={styles.title} numberOfLines={1}>
|
||||
{title}
|
||||
</Text>
|
||||
<View style={styles.updateButton}>
|
||||
<Text style={styles.updateLabel}>{t('home.appUpdateCard.update', 'Update')}</Text>
|
||||
</View>
|
||||
</Pressable>
|
||||
<Pressable
|
||||
@@ -50,7 +46,7 @@ export function AppUpdateCard(props: {
|
||||
style={({ pressed }) => [styles.dismissButton, pressed && styles.dismissPressed]}
|
||||
onPress={props.onDismiss}
|
||||
>
|
||||
<X size={16} color={colors.textSecondary} />
|
||||
<X size={17} color={colors.textMuted} />
|
||||
</Pressable>
|
||||
</View>
|
||||
)
|
||||
@@ -60,10 +56,9 @@ const styles = themedStyleSheet(() => ({
|
||||
card: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginTop: 26,
|
||||
borderRadius: radii.surface,
|
||||
backgroundColor: colors.bgPanel,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.borderSubtle,
|
||||
overflow: 'hidden'
|
||||
},
|
||||
cardMain: {
|
||||
@@ -71,22 +66,20 @@ const styles = themedStyleSheet(() => ({
|
||||
minWidth: 0,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
paddingLeft: spacing.md,
|
||||
paddingVertical: 12
|
||||
gap: spacing.md,
|
||||
paddingLeft: spacing.lg,
|
||||
paddingVertical: spacing.md
|
||||
},
|
||||
cardPressed: { backgroundColor: colors.bgRaised },
|
||||
icon: {
|
||||
width: 46,
|
||||
height: 46,
|
||||
borderRadius: radii.tile,
|
||||
alignItems: 'center',
|
||||
title: { flex: 1, minWidth: 0, color: colors.textPrimary, fontSize: 15, fontWeight: '600' },
|
||||
updateButton: {
|
||||
minHeight: 32,
|
||||
justifyContent: 'center',
|
||||
backgroundColor: colors.bgRaised,
|
||||
marginRight: 14
|
||||
paddingHorizontal: spacing.md,
|
||||
borderRadius: radii.control,
|
||||
backgroundColor: colors.bgRaised
|
||||
},
|
||||
main: { flex: 1, minWidth: 0, marginRight: spacing.sm },
|
||||
title: { color: colors.textPrimary, fontSize: 15, fontWeight: '600', lineHeight: 20 },
|
||||
subtitle: { color: colors.textSecondary, fontSize: 12, lineHeight: 16 },
|
||||
updateLabel: { color: colors.accentBlue, fontSize: 15, fontWeight: '600' },
|
||||
dismissButton: {
|
||||
width: 40,
|
||||
height: 40,
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
import { Pressable, Text, View } from 'react-native'
|
||||
import { useMobileTranslation } from '../i18n/use-mobile-translation'
|
||||
import { ClaudeIcon, OpenAIIcon } from '../components/AgentIcons'
|
||||
import {
|
||||
getActiveProviderRateLimits,
|
||||
getUsageBarState,
|
||||
hasActiveProviderUsage,
|
||||
UsageBar,
|
||||
type AccountsSnapshot,
|
||||
type ProviderKey
|
||||
} from '../components/AccountUsage'
|
||||
import { colors, radii, spacing } from '../theme/mobile-theme'
|
||||
import { themedStyleSheet } from '../theme/themed-styles'
|
||||
import { useMobileColorScheme } from '../theme/use-mobile-color-scheme'
|
||||
import type { HostProfile } from '../transport/types'
|
||||
|
||||
export function MobileHomeAccountUsageCards(props: {
|
||||
items: { host: HostProfile; snapshot: AccountsSnapshot }[]
|
||||
onOpen: (hostId: string) => void
|
||||
}) {
|
||||
const t = useMobileTranslation()
|
||||
useMobileColorScheme()
|
||||
if (props.items.length === 0) {
|
||||
return null
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<Text style={styles.sectionHeading}>
|
||||
{t('home.accountUsageCards.sectionHeading', 'Account usage')}
|
||||
</Text>
|
||||
{props.items.map(({ host, snapshot }) => {
|
||||
const claudeActive =
|
||||
snapshot.claude.accounts.find(
|
||||
(account) => account.id === snapshot.claude.activeAccountId
|
||||
) ?? null
|
||||
const codexActive =
|
||||
snapshot.codex.accounts.find(
|
||||
(account) => account.id === snapshot.codex.activeAccountId
|
||||
) ?? null
|
||||
return (
|
||||
<Pressable
|
||||
key={host.id}
|
||||
style={({ pressed }) => [styles.card, pressed && styles.cardPressed]}
|
||||
onPress={() => props.onOpen(host.id)}
|
||||
>
|
||||
{props.items.length > 1 ? (
|
||||
<Text style={styles.hostLabel} numberOfLines={1}>
|
||||
{host.name}
|
||||
</Text>
|
||||
) : null}
|
||||
{(['claude', 'codex'] as ProviderKey[]).map((provider) => {
|
||||
const active = provider === 'claude' ? claudeActive : codexActive
|
||||
const accounts =
|
||||
provider === 'claude' ? snapshot.claude.accounts : snapshot.codex.accounts
|
||||
const limits = getActiveProviderRateLimits(snapshot, provider)
|
||||
if (accounts.length === 0 && !hasActiveProviderUsage(limits)) {
|
||||
return null
|
||||
}
|
||||
const sessionBar = getUsageBarState(limits, 'session')
|
||||
const weeklyBar = getUsageBarState(limits, 'weekly')
|
||||
return (
|
||||
<View key={provider} style={styles.row}>
|
||||
<View style={styles.icon}>
|
||||
{provider === 'claude' ? (
|
||||
<ClaudeIcon size={18} />
|
||||
) : (
|
||||
<OpenAIIcon size={18} color={colors.textPrimary} />
|
||||
)}
|
||||
</View>
|
||||
<View style={styles.info}>
|
||||
<Text style={styles.email} numberOfLines={1}>
|
||||
{active?.email ?? t('home.accountUsageCards.systemDefault', 'System default')}
|
||||
</Text>
|
||||
<View style={styles.bars}>
|
||||
<UsageBar
|
||||
label={t('home.accountUsageCards.sessionWindow', '5h')}
|
||||
usedPercent={sessionBar.usedPercent}
|
||||
unavailable={sessionBar.unavailable}
|
||||
loading={sessionBar.loading}
|
||||
/>
|
||||
<UsageBar
|
||||
label={t('home.accountUsageCards.weeklyWindow', '7d')}
|
||||
usedPercent={weeklyBar.usedPercent}
|
||||
unavailable={weeklyBar.unavailable}
|
||||
loading={weeklyBar.loading}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
})}
|
||||
</Pressable>
|
||||
)
|
||||
})}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = themedStyleSheet(() => ({
|
||||
sectionHeading: {
|
||||
fontSize: 11,
|
||||
fontWeight: '600',
|
||||
color: colors.textMuted,
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: 0.6,
|
||||
marginTop: spacing.xl,
|
||||
marginBottom: spacing.sm,
|
||||
paddingHorizontal: spacing.xs
|
||||
},
|
||||
card: {
|
||||
backgroundColor: colors.bgPanel,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.borderSubtle,
|
||||
borderRadius: radii.surface,
|
||||
paddingHorizontal: spacing.md,
|
||||
paddingVertical: spacing.sm + 2,
|
||||
gap: spacing.sm,
|
||||
marginBottom: spacing.sm
|
||||
},
|
||||
cardPressed: { backgroundColor: colors.bgRaised },
|
||||
hostLabel: {
|
||||
fontSize: 11,
|
||||
color: colors.textMuted,
|
||||
fontWeight: '500',
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: 0.4
|
||||
},
|
||||
row: { flexDirection: 'row', alignItems: 'center', gap: spacing.sm + 2 },
|
||||
icon: {
|
||||
width: 32,
|
||||
height: 32,
|
||||
borderRadius: radii.tile,
|
||||
backgroundColor: colors.bgRaised,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
info: { flex: 1, minWidth: 0, gap: 2 },
|
||||
email: { fontSize: 13, fontWeight: '600', color: colors.textPrimary },
|
||||
bars: { flexDirection: 'row', gap: spacing.md, marginTop: 4 }
|
||||
}))
|
||||
@@ -1,4 +1,3 @@
|
||||
import { StyleSheet, View } from 'react-native'
|
||||
import { undismissedAppUpdate } from '../app-update/app-update-checker'
|
||||
import {
|
||||
appUpdateChecker,
|
||||
@@ -6,7 +5,6 @@ import {
|
||||
useAppUpdateState
|
||||
} from '../app-update/app-update-runtime'
|
||||
import { AppUpdateCard } from './AppUpdateCard'
|
||||
import { spacing } from '../theme/mobile-theme'
|
||||
|
||||
export function MobileHomeAppUpdateCard() {
|
||||
const available = undismissedAppUpdate(useAppUpdateState())
|
||||
@@ -14,14 +12,10 @@ export function MobileHomeAppUpdateCard() {
|
||||
return null
|
||||
}
|
||||
return (
|
||||
<View style={styles.slot}>
|
||||
<AppUpdateCard
|
||||
version={available.version}
|
||||
onPress={() => openAppUpdate(available.url)}
|
||||
onDismiss={() => appUpdateChecker.dismiss(available.version)}
|
||||
/>
|
||||
</View>
|
||||
<AppUpdateCard
|
||||
version={available.version}
|
||||
onPress={() => openAppUpdate(available.url)}
|
||||
onDismiss={() => appUpdateChecker.dismiss(available.version)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({ slot: { marginBottom: spacing.lg } })
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
import { ListTodo, Plus, QrCode, type LucideIcon } from 'lucide-react-native'
|
||||
import type { ReactNode } from 'react'
|
||||
import { Pressable, Text, View } from 'react-native'
|
||||
import { useMobileTranslation } from '../i18n/use-mobile-translation'
|
||||
import { PickerModal } from '../components/PickerModal'
|
||||
import { useConnectedHostPicker } from '../components/use-connected-host-picker'
|
||||
import { colors, radii, spacing } from '../theme/mobile-theme'
|
||||
import { themedStyleSheet } from '../theme/themed-styles'
|
||||
import { useMobileColorScheme } from '../theme/use-mobile-color-scheme'
|
||||
import type { HostProfile } from '../transport/types'
|
||||
|
||||
/** Home's actions, pinned within thumb reach above the home indicator. */
|
||||
export function MobileHomeBottomBar(props: {
|
||||
bottomInset: number
|
||||
contentMaxWidth: number
|
||||
isWideLayout: boolean
|
||||
connectedHosts: HostProfile[]
|
||||
onOpenTasks: (hostId: string) => void
|
||||
onCreateWorkspace: () => void
|
||||
}) {
|
||||
const t = useMobileTranslation()
|
||||
useMobileColorScheme()
|
||||
const tasksHostPicker = useConnectedHostPicker(props.connectedHosts)
|
||||
const enabled = props.connectedHosts.length > 0
|
||||
return (
|
||||
<MobileHomeBottomBarFrame {...props}>
|
||||
{enabled ? null : (
|
||||
<Text style={styles.note}>
|
||||
{t('home.bottomBar.connectFirst', 'Connect a desktop to start new work.')}
|
||||
</Text>
|
||||
)}
|
||||
<View style={styles.buttons}>
|
||||
<BarButton
|
||||
icon={ListTodo}
|
||||
label={t('home.bottomBar.tasks', 'Tasks')}
|
||||
variant="secondary"
|
||||
disabled={!enabled}
|
||||
onPress={() => tasksHostPicker.pickHost(props.onOpenTasks)}
|
||||
/>
|
||||
<BarButton
|
||||
icon={Plus}
|
||||
label={t('home.bottomBar.newWorkspace', 'New workspace')}
|
||||
variant="primary"
|
||||
disabled={!enabled}
|
||||
onPress={props.onCreateWorkspace}
|
||||
/>
|
||||
</View>
|
||||
<PickerModal
|
||||
title={t('home.listFooter.hostPickerTitle', 'Open Tasks On')}
|
||||
{...tasksHostPicker.pickerProps}
|
||||
/>
|
||||
</MobileHomeBottomBarFrame>
|
||||
)
|
||||
}
|
||||
|
||||
/** First run's single action, in the same pinned spot. */
|
||||
export function MobileHomePairBar(props: {
|
||||
bottomInset: number
|
||||
contentMaxWidth: number
|
||||
isWideLayout: boolean
|
||||
onPairDesktop: () => void
|
||||
}) {
|
||||
const t = useMobileTranslation()
|
||||
useMobileColorScheme()
|
||||
return (
|
||||
<MobileHomeBottomBarFrame {...props}>
|
||||
<View style={styles.buttons}>
|
||||
<BarButton
|
||||
icon={QrCode}
|
||||
label={t('home.emptyState.pairDesktopButton', 'Pair desktop')}
|
||||
variant="primary"
|
||||
onPress={props.onPairDesktop}
|
||||
/>
|
||||
</View>
|
||||
</MobileHomeBottomBarFrame>
|
||||
)
|
||||
}
|
||||
|
||||
function MobileHomeBottomBarFrame(props: {
|
||||
bottomInset: number
|
||||
contentMaxWidth: number
|
||||
isWideLayout: boolean
|
||||
children: ReactNode
|
||||
}) {
|
||||
useMobileColorScheme()
|
||||
return (
|
||||
<View style={[styles.bar, { paddingBottom: props.bottomInset + spacing.sm }]}>
|
||||
<View
|
||||
style={
|
||||
props.isWideLayout
|
||||
? { maxWidth: props.contentMaxWidth, width: '100%', alignSelf: 'center' }
|
||||
: null
|
||||
}
|
||||
>
|
||||
{props.children}
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
function BarButton(props: {
|
||||
icon: LucideIcon
|
||||
label: string
|
||||
variant: 'primary' | 'secondary'
|
||||
disabled?: boolean
|
||||
onPress: () => void
|
||||
}) {
|
||||
useMobileColorScheme()
|
||||
const primary = props.variant === 'primary'
|
||||
const foreground = primary ? colors.bgBase : colors.textPrimary
|
||||
const Icon = props.icon
|
||||
return (
|
||||
<Pressable
|
||||
accessibilityRole="button"
|
||||
accessibilityState={{ disabled: Boolean(props.disabled) }}
|
||||
disabled={props.disabled}
|
||||
style={({ pressed }) => [
|
||||
styles.button,
|
||||
primary ? styles.primaryButton : styles.secondaryButton,
|
||||
pressed && styles.buttonPressed,
|
||||
props.disabled && styles.buttonDisabled
|
||||
]}
|
||||
onPress={props.onPress}
|
||||
>
|
||||
<Icon size={19} color={foreground} strokeWidth={2.2} />
|
||||
<Text style={[styles.buttonLabel, { color: foreground }]} numberOfLines={1}>
|
||||
{props.label}
|
||||
</Text>
|
||||
</Pressable>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = themedStyleSheet(() => ({
|
||||
bar: {
|
||||
backgroundColor: colors.bgBase,
|
||||
borderTopWidth: 1,
|
||||
borderTopColor: colors.borderSubtle,
|
||||
paddingTop: 10,
|
||||
paddingHorizontal: spacing.lg
|
||||
},
|
||||
note: {
|
||||
fontSize: 13,
|
||||
lineHeight: 18,
|
||||
color: colors.textSecondary,
|
||||
textAlign: 'center',
|
||||
marginBottom: 10
|
||||
},
|
||||
buttons: { flexDirection: 'row', gap: 10 },
|
||||
button: {
|
||||
flex: 1,
|
||||
minHeight: 50,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: spacing.sm,
|
||||
paddingHorizontal: spacing.md,
|
||||
borderRadius: radii.control
|
||||
},
|
||||
primaryButton: { backgroundColor: colors.surfaceBright },
|
||||
secondaryButton: { backgroundColor: colors.bgRaised },
|
||||
buttonPressed: { opacity: 0.72, transform: [{ scale: 0.98 }] },
|
||||
buttonDisabled: { opacity: 0.4 },
|
||||
buttonLabel: { fontSize: 17, fontWeight: '600' }
|
||||
}))
|
||||
@@ -0,0 +1,158 @@
|
||||
import { createElement } from 'react'
|
||||
import { act, create, type ReactTestRenderer } from 'react-test-renderer'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { findAllByHostType } from '../test-support/react-test-renderer-host-nodes'
|
||||
import { colors } from '../theme/mobile-theme'
|
||||
import type { ConnectionVerdict } from '../transport/connection-health'
|
||||
import type { ConnectionState, HostCredentialStatus, HostProfile } from '../transport/types'
|
||||
import {
|
||||
markHomeWorktreeCatalogUnavailable,
|
||||
type HostWorktreeInfo
|
||||
} from '../worktree/home-worktree-info'
|
||||
import { MobileHomeDesktopRow } from './MobileHomeDesktopRow'
|
||||
|
||||
vi.mock('react-native', () => ({
|
||||
Pressable: 'Pressable',
|
||||
StyleSheet: { create: <T,>(styles: T) => styles, hairlineWidth: 0.5 },
|
||||
Text: 'Text',
|
||||
View: 'View'
|
||||
}))
|
||||
vi.mock('lucide-react-native', () => ({ ChevronRight: 'ChevronRight' }))
|
||||
vi.mock('../components/StatusDot', async (importOriginal) => ({
|
||||
...(await importOriginal<Record<string, unknown>>()),
|
||||
StatusDot: 'StatusDot'
|
||||
}))
|
||||
|
||||
const host: HostProfile = {
|
||||
id: 'host-1',
|
||||
name: 'Studio',
|
||||
endpoint: 'ws://studio.local:8765',
|
||||
deviceToken: 'token',
|
||||
publicKeyB64: 'key',
|
||||
lastConnected: 0
|
||||
}
|
||||
const NOW = 1_800_000_000_000
|
||||
const connectedVerdict: ConnectionVerdict = { kind: 'normal', label: 'Connected' }
|
||||
const loaded: HostWorktreeInfo = {
|
||||
hostId: 'host-1',
|
||||
totalWorktrees: 12,
|
||||
activeCount: 2,
|
||||
lastActiveWorktree: null,
|
||||
countsProvenAt: NOW
|
||||
}
|
||||
|
||||
describe('MobileHomeDesktopRow', () => {
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
|
||||
// The row ages counts against the clock; pin it so "proven just now" stays true.
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers({ toFake: ['Date'], now: NOW })
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
act(() => renderer?.unmount())
|
||||
renderer = null
|
||||
vi.useRealTimers()
|
||||
})
|
||||
|
||||
async function renderRow(
|
||||
worktreeInfo: HostWorktreeInfo | undefined,
|
||||
overrides?: {
|
||||
state?: ConnectionState
|
||||
verdict?: ConnectionVerdict
|
||||
credentialStatus?: HostCredentialStatus
|
||||
host?: HostProfile
|
||||
onLongPress?: () => void
|
||||
}
|
||||
) {
|
||||
await act(async () => {
|
||||
renderer = create(
|
||||
createElement(MobileHomeDesktopRow, {
|
||||
host: overrides?.host ?? host,
|
||||
state: overrides?.state ?? 'connected',
|
||||
verdict: overrides?.verdict ?? connectedVerdict,
|
||||
path: 'lan',
|
||||
credentialStatus: overrides?.credentialStatus,
|
||||
worktreeInfo,
|
||||
onPress: () => {},
|
||||
onLongPress: overrides?.onLongPress ?? (() => {})
|
||||
})
|
||||
)
|
||||
})
|
||||
const root = renderer!.root
|
||||
const texts = findAllByHostType(root, 'Text')
|
||||
return {
|
||||
lines: texts.flatMap((node) => node.children.filter((child) => typeof child === 'string')),
|
||||
texts,
|
||||
row: findAllByHostType(root, 'Pressable')[0]
|
||||
}
|
||||
}
|
||||
|
||||
it('renders the counts the host proved', async () => {
|
||||
expect((await renderRow(loaded)).lines).toContain('12 worktrees · 2 active')
|
||||
})
|
||||
|
||||
it('keeps rendering the last proven counts after a failed refresh', async () => {
|
||||
const { lines } = await renderRow(markHomeWorktreeCatalogUnavailable(loaded, 'host-1'))
|
||||
expect(lines).toContain('Last known: 12 worktrees · 2 active')
|
||||
})
|
||||
|
||||
it('never asserts a count for a catalog that failed with nothing proven', async () => {
|
||||
const { lines } = await renderRow(markHomeWorktreeCatalogUnavailable(undefined, 'host-1'))
|
||||
expect(lines).toContain('Worktree list unavailable')
|
||||
})
|
||||
|
||||
it('offers re-pairing when the credential is missing, instead of counts', async () => {
|
||||
const { lines } = await renderRow(loaded, {
|
||||
verdict: { kind: 'auth-failed', label: 'Pairing invalid' },
|
||||
credentialStatus: 'missing'
|
||||
})
|
||||
expect(lines).toContain('Pairing invalid')
|
||||
expect(lines).toContain('Tap to re-pair with your desktop')
|
||||
expect(lines).not.toContain('12 worktrees · 2 active')
|
||||
})
|
||||
|
||||
it('offers a retry without declaring a transient read failure invalid', async () => {
|
||||
const { lines } = await renderRow(undefined, {
|
||||
state: 'disconnected',
|
||||
verdict: { kind: 'normal', label: 'Disconnected' },
|
||||
credentialStatus: 'temporarily-unavailable'
|
||||
})
|
||||
expect(lines).toContain('Pairing temporarily unavailable')
|
||||
expect(lines).toContain('Unlock your phone, then tap to retry')
|
||||
expect(lines).not.toContain('Pairing invalid')
|
||||
})
|
||||
|
||||
it('colours a warning amber like its dot, not red', async () => {
|
||||
const { texts } = await renderRow(undefined, {
|
||||
state: 'reconnecting',
|
||||
verdict: { kind: 'warning', label: "Can't connect", detail: 'Check Tailscale' }
|
||||
})
|
||||
const status = texts.find((node) => node.children.includes("Can't connect"))
|
||||
expect(status?.props.style).toContainEqual({ color: colors.statusAmber })
|
||||
})
|
||||
|
||||
it('shows and announces the fix for an offline desktop', async () => {
|
||||
const { lines, row } = await renderRow(undefined, {
|
||||
host: { ...host, name: 'Desk' },
|
||||
state: 'reconnecting',
|
||||
verdict: {
|
||||
kind: 'unreachable',
|
||||
label: "Can't reach desktop",
|
||||
reason: 'never-connected',
|
||||
detail: "Check it's awake, Orca is running, and you're signed in"
|
||||
}
|
||||
})
|
||||
expect(lines).toContain("Check it's awake, Orca is running, and you're signed in")
|
||||
expect(row.props.accessibilityLabel).toBe(
|
||||
"Open Desk, Can't reach desktop, Update desktop Orca and sign in to connect from anywhere, Check it's awake, Orca is running, and you're signed in"
|
||||
)
|
||||
})
|
||||
|
||||
it('opens the desktop menu from the accessibility long-press action', async () => {
|
||||
const onLongPress = vi.fn()
|
||||
const { row } = await renderRow(loaded, { onLongPress })
|
||||
act(() => row.props.onAccessibilityAction({ nativeEvent: { actionName: 'longpress' } }))
|
||||
expect(onLongPress).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,143 @@
|
||||
import { ChevronRight } from 'lucide-react-native'
|
||||
import { Pressable, Text, View } from 'react-native'
|
||||
import type { Translate } from '../i18n/mobile-i18n'
|
||||
import { useMobileTranslation } from '../i18n/use-mobile-translation'
|
||||
import { StatusDot, statusDotColor } from '../components/StatusDot'
|
||||
import type { ConnectionVerdict } from '../transport/connection-health'
|
||||
import { verdictDisplayLabel } from '../transport/connection-health'
|
||||
import { mobileConnectionPathAccessibilityLabel } from '../transport/mobile-connection-path-label'
|
||||
import { useHostDisplay } from '../transport/use-host-display'
|
||||
import type { MobileConnectionPath } from '../transport/stable-logical-rpc-client'
|
||||
import type { ConnectionState, HostCatalogEntry, HostProfile } from '../transport/types'
|
||||
import { colors } from '../theme/mobile-theme'
|
||||
import { useMobileColorScheme } from '../theme/use-mobile-color-scheme'
|
||||
import { homeHostWorktreeSummary, type HostWorktreeInfo } from '../worktree/home-worktree-info'
|
||||
import { homeListStyles as styles } from './home-list-styles'
|
||||
|
||||
const PROBLEM_VERDICTS: ReadonlySet<ConnectionVerdict['kind']> = new Set([
|
||||
'warning',
|
||||
'unreachable',
|
||||
'auth-failed'
|
||||
])
|
||||
|
||||
function worktreeSummaries(info: HostWorktreeInfo | undefined, t: Translate) {
|
||||
const now = Date.now()
|
||||
const visual = homeHostWorktreeSummary(info, now)
|
||||
const accessibility =
|
||||
info && info.activeCount > 0 && (!info.catalogUnavailable || info.staleCounts)
|
||||
? t(
|
||||
'components.hostCard.worktreeAccessibilitySummary',
|
||||
'{{summary}}, {{activeCount}} active',
|
||||
{
|
||||
summary: homeHostWorktreeSummary({ ...info, activeCount: 0 }, now),
|
||||
activeCount: info.activeCount
|
||||
}
|
||||
)
|
||||
: visual
|
||||
return { visual, accessibility }
|
||||
}
|
||||
|
||||
export function MobileHomeDesktopRow(props: {
|
||||
host: HostProfile | HostCatalogEntry
|
||||
credentialStatus?: HostCatalogEntry['credentialStatus']
|
||||
state: ConnectionState
|
||||
verdict: ConnectionVerdict
|
||||
path: MobileConnectionPath
|
||||
// The row owns fresh/stale/unavailable wording so callers cannot hide retained counts again (STA-3123).
|
||||
worktreeInfo?: HostWorktreeInfo
|
||||
onPress: () => void
|
||||
onLongPress: () => void
|
||||
}) {
|
||||
const t = useMobileTranslation()
|
||||
useMobileColorScheme()
|
||||
const credentialUnavailable = props.credentialStatus === 'temporarily-unavailable'
|
||||
const credentialMissing = props.credentialStatus === 'missing'
|
||||
const connected = props.state === 'connected' && !credentialUnavailable && !credentialMissing
|
||||
const statusLabel = credentialMissing
|
||||
? t('components.hostCard.pairingInvalid', 'Pairing invalid')
|
||||
: credentialUnavailable
|
||||
? t('components.hostCard.pairingTemporarilyUnavailable', 'Pairing temporarily unavailable')
|
||||
: verdictDisplayLabel(props.verdict)
|
||||
const statusVerdict: ConnectionVerdict = credentialMissing
|
||||
? { kind: 'auth-failed', label: statusLabel }
|
||||
: credentialUnavailable
|
||||
? { kind: 'warning', label: statusLabel }
|
||||
: props.verdict
|
||||
// The words take the dot's severity, so an amber warning never reads red.
|
||||
const statusColor = PROBLEM_VERDICTS.has(statusVerdict.kind)
|
||||
? statusDotColor(props.state, statusVerdict)
|
||||
: colors.textSecondary
|
||||
const worktreeSummary = worktreeSummaries(props.worktreeInfo, t)
|
||||
const display = useHostDisplay(props.host)
|
||||
const discoveryHint =
|
||||
props.verdict.kind === 'unreachable' && !props.host.relay
|
||||
? t(
|
||||
'components.hostCard.updateDesktopHint',
|
||||
'Update desktop Orca and sign in to connect from anywhere'
|
||||
)
|
||||
: null
|
||||
const credentialHint = credentialMissing
|
||||
? t('components.hostCard.repairHint', 'Tap to re-pair with your desktop')
|
||||
: credentialUnavailable
|
||||
? t('components.hostCard.unlockHint', 'Unlock your phone, then tap to retry')
|
||||
: null
|
||||
// The verdict's own second line (what to check on the desktop); credential copy wins.
|
||||
const verdictDetail =
|
||||
credentialHint === null && 'detail' in props.verdict ? (props.verdict.detail ?? null) : null
|
||||
const details = [discoveryHint, credentialHint, verdictDetail].filter((line): line is string =>
|
||||
Boolean(line)
|
||||
)
|
||||
const subtitle = connected && worktreeSummary.visual ? worktreeSummary.visual : statusLabel
|
||||
const accessibilityLabel = [
|
||||
t('components.hostCard.openHost', 'Open {{title}}', { title: display.title }),
|
||||
display.descriptorLine,
|
||||
statusLabel,
|
||||
connected ? mobileConnectionPathAccessibilityLabel(props.path) : null,
|
||||
connected ? worktreeSummary.accessibility : null,
|
||||
...details
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(', ')
|
||||
const optionsLabel = t('components.hostCard.hostActions', 'Actions for {{title}}', {
|
||||
title: display.title
|
||||
})
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={accessibilityLabel}
|
||||
accessibilityActions={[{ name: 'longpress', label: optionsLabel }]}
|
||||
onAccessibilityAction={(event) => {
|
||||
if (event.nativeEvent.actionName === 'longpress') {
|
||||
props.onLongPress()
|
||||
}
|
||||
}}
|
||||
style={({ pressed }) => [styles.row, pressed && styles.rowPressed]}
|
||||
onPress={props.onPress}
|
||||
onLongPress={props.onLongPress}
|
||||
delayLongPress={400}
|
||||
>
|
||||
<View style={styles.status}>
|
||||
<View style={styles.statusDotBox}>
|
||||
<StatusDot state={props.state} verdict={statusVerdict} />
|
||||
</View>
|
||||
</View>
|
||||
<View style={styles.text}>
|
||||
<Text style={styles.title} numberOfLines={1}>
|
||||
{display.title}
|
||||
</Text>
|
||||
<Text style={[styles.subtitle, { color: statusColor }]} numberOfLines={connected ? 1 : 2}>
|
||||
{subtitle}
|
||||
</Text>
|
||||
{details.map((line) => (
|
||||
<Text key={line} style={styles.detail} numberOfLines={3}>
|
||||
{line}
|
||||
</Text>
|
||||
))}
|
||||
</View>
|
||||
<View style={styles.trailing}>
|
||||
<ChevronRight size={16} color={colors.textMuted} />
|
||||
</View>
|
||||
</Pressable>
|
||||
)
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { QrCode } from 'lucide-react-native'
|
||||
import { Pressable, ScrollView, Text, View } from 'react-native'
|
||||
import { Fragment } from 'react'
|
||||
import { ScrollView, StyleSheet, Text, View } from 'react-native'
|
||||
import { OrcaLogo } from '../components/OrcaLogo'
|
||||
import type { Translate } from '../i18n/mobile-i18n'
|
||||
import { useMobileTranslation } from '../i18n/use-mobile-translation'
|
||||
import { colors, radii, spacing } from '../theme/mobile-theme'
|
||||
@@ -20,8 +21,8 @@ function onboardingSteps(t: Translate) {
|
||||
id: 'scan-code',
|
||||
title: t('home.emptyState.scanCodeTitle', 'Scan the code'),
|
||||
desc: t(
|
||||
'home.emptyState.scanCodeDescription',
|
||||
'Tap the button above to open the scanner. Point at the QR code on your screen.'
|
||||
'home.emptyState.scanCodeBelowDescription',
|
||||
'Tap Pair desktop below, then point your camera at the QR code on your screen.'
|
||||
)
|
||||
},
|
||||
{
|
||||
@@ -35,20 +36,14 @@ function onboardingSteps(t: Translate) {
|
||||
]
|
||||
}
|
||||
|
||||
export function MobileHomeEmptyState(props: {
|
||||
bottomInset: number
|
||||
contentMaxWidth: number
|
||||
isWideLayout: boolean
|
||||
onPairDesktop: () => void
|
||||
}) {
|
||||
export function MobileHomeEmptyState(props: { contentMaxWidth: number; isWideLayout: boolean }) {
|
||||
const t = useMobileTranslation()
|
||||
useMobileColorScheme()
|
||||
// Scrolls because a phone in landscape is shorter than the hero and the steps together.
|
||||
return (
|
||||
<ScrollView
|
||||
contentContainerStyle={[
|
||||
styles.emptyContainer,
|
||||
{ paddingBottom: props.bottomInset },
|
||||
styles.container,
|
||||
props.isWideLayout && {
|
||||
maxWidth: props.contentMaxWidth,
|
||||
width: '100%',
|
||||
@@ -56,109 +51,75 @@ export function MobileHomeEmptyState(props: {
|
||||
}
|
||||
]}
|
||||
>
|
||||
<View style={styles.emptyHero}>
|
||||
<Text style={styles.emptyTitle}>{t('home.emptyState.title', 'Connect your desktop')}</Text>
|
||||
<Text style={styles.emptyBody}>
|
||||
<View style={styles.hero}>
|
||||
<OrcaLogo size={44} />
|
||||
<Text style={styles.title} accessibilityRole="header">
|
||||
{t('home.emptyState.title', 'Connect your desktop')}
|
||||
</Text>
|
||||
<Text style={styles.body}>
|
||||
{t(
|
||||
'home.emptyState.description',
|
||||
'Pair with Orca on your computer to check on your agents, jump into any terminal, and drive work from your phone.'
|
||||
)}
|
||||
</Text>
|
||||
<Pressable style={styles.primaryButton} onPress={props.onPairDesktop}>
|
||||
<QrCode size={17} color={colors.bgBase} />
|
||||
<Text style={styles.primaryButtonText}>
|
||||
{t('home.emptyState.pairDesktop', 'Pair Desktop')}
|
||||
</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
<View style={styles.stepsSection}>
|
||||
<Text style={styles.sectionHeading}>
|
||||
{t('home.emptyState.stepsHeading', 'How it works')}
|
||||
</Text>
|
||||
<View style={styles.steps}>
|
||||
{onboardingSteps(t).map((step, index) => (
|
||||
<View key={step.id} style={[styles.stepRow, index > 0 && styles.stepRowBorder]}>
|
||||
<View style={styles.stepNum}>
|
||||
<Text style={styles.stepNumText}>{index + 1}</Text>
|
||||
<Fragment key={step.id}>
|
||||
{index > 0 ? <View style={styles.separator} /> : null}
|
||||
<View style={styles.step}>
|
||||
<View style={styles.stepNumber}>
|
||||
<Text style={styles.stepNumberText}>{index + 1}</Text>
|
||||
</View>
|
||||
<View style={styles.stepText}>
|
||||
<Text style={styles.stepTitle}>{step.title}</Text>
|
||||
<Text style={styles.stepDescription}>{step.desc}</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View style={styles.stepText}>
|
||||
<Text style={styles.stepTitle}>{step.title}</Text>
|
||||
<Text style={styles.stepDesc}>{step.desc}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</Fragment>
|
||||
))}
|
||||
</View>
|
||||
</ScrollView>
|
||||
)
|
||||
}
|
||||
|
||||
const STEP_NUMBER_SIZE = 26
|
||||
|
||||
const styles = themedStyleSheet(() => ({
|
||||
emptyContainer: { flexGrow: 1 },
|
||||
emptyHero: {
|
||||
flexGrow: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
paddingHorizontal: 32,
|
||||
paddingBottom: 40
|
||||
},
|
||||
emptyTitle: {
|
||||
fontSize: 22,
|
||||
container: { flexGrow: 1, justifyContent: 'center', padding: spacing.lg },
|
||||
hero: { alignItems: 'center', paddingHorizontal: spacing.sm, marginBottom: 34 },
|
||||
title: {
|
||||
fontSize: 28,
|
||||
fontWeight: '700',
|
||||
letterSpacing: -0.4,
|
||||
color: colors.textPrimary,
|
||||
textAlign: 'center',
|
||||
marginTop: 26,
|
||||
marginBottom: 10
|
||||
},
|
||||
emptyBody: {
|
||||
fontSize: 15,
|
||||
color: colors.textSecondary,
|
||||
textAlign: 'center',
|
||||
lineHeight: 22,
|
||||
marginBottom: 32
|
||||
body: { fontSize: 17, lineHeight: 24, color: colors.textSecondary, textAlign: 'center' },
|
||||
steps: { backgroundColor: colors.bgPanel, borderRadius: radii.surface, overflow: 'hidden' },
|
||||
separator: {
|
||||
height: StyleSheet.hairlineWidth,
|
||||
backgroundColor: colors.borderSubtle,
|
||||
marginLeft: spacing.lg + STEP_NUMBER_SIZE + 14
|
||||
},
|
||||
primaryButton: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: 10,
|
||||
backgroundColor: colors.textPrimary,
|
||||
paddingHorizontal: 28,
|
||||
paddingVertical: 14,
|
||||
borderRadius: radii.control
|
||||
},
|
||||
primaryButtonText: { color: colors.bgBase, fontSize: 15, fontWeight: '700' },
|
||||
stepsSection: { paddingHorizontal: spacing.xl },
|
||||
sectionHeading: {
|
||||
fontSize: 11,
|
||||
fontWeight: '600',
|
||||
color: colors.textMuted,
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: 0.6,
|
||||
marginBottom: spacing.sm,
|
||||
paddingHorizontal: spacing.xs
|
||||
},
|
||||
stepRow: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'flex-start',
|
||||
gap: 14,
|
||||
paddingVertical: spacing.lg
|
||||
},
|
||||
stepRowBorder: { borderTopWidth: 1, borderTopColor: colors.borderSubtle },
|
||||
stepNum: {
|
||||
width: 28,
|
||||
height: 28,
|
||||
step: { flexDirection: 'row', alignItems: 'flex-start', gap: 14, padding: spacing.lg },
|
||||
stepNumber: {
|
||||
width: STEP_NUMBER_SIZE,
|
||||
height: STEP_NUMBER_SIZE,
|
||||
borderRadius: radii.pill,
|
||||
backgroundColor: colors.bgPanel,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.borderSubtle,
|
||||
backgroundColor: colors.bgRaised,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginTop: 1
|
||||
justifyContent: 'center'
|
||||
},
|
||||
stepNumText: { fontSize: 12, fontWeight: '700', color: colors.textSecondary },
|
||||
stepText: { flex: 1 },
|
||||
stepTitle: {
|
||||
stepNumberText: {
|
||||
fontSize: 14,
|
||||
fontWeight: '600',
|
||||
color: colors.textPrimary,
|
||||
marginBottom: 3
|
||||
fontVariant: ['tabular-nums']
|
||||
},
|
||||
stepDesc: { fontSize: 12, color: colors.textMuted, lineHeight: 17 }
|
||||
stepText: { flex: 1, minWidth: 0 },
|
||||
stepTitle: { fontSize: 17, lineHeight: 22, color: colors.textPrimary },
|
||||
stepDescription: { fontSize: 15, lineHeight: 20, color: colors.textSecondary, marginTop: 2 }
|
||||
}))
|
||||
|
||||
@@ -1,20 +1,25 @@
|
||||
import { memo, useCallback, type ReactElement } from 'react'
|
||||
import { FlatList, StyleSheet, View } from 'react-native'
|
||||
import type { ListRenderItemInfo } from 'react-native'
|
||||
import { MobileHostCard } from '../components/MobileHostCard'
|
||||
import type { HomeStatsSummary } from '../stats/home-stats-total'
|
||||
import { spacing } from '../theme/mobile-theme'
|
||||
import { Plus } from 'lucide-react-native'
|
||||
import { Fragment, memo, useCallback, type ReactElement } from 'react'
|
||||
import { Pressable, ScrollView, Text, View } from 'react-native'
|
||||
import { useMobileTranslation } from '../i18n/use-mobile-translation'
|
||||
import { colors, spacing } from '../theme/mobile-theme'
|
||||
import { themedStyleSheet } from '../theme/themed-styles'
|
||||
import { useMobileColorScheme } from '../theme/use-mobile-color-scheme'
|
||||
import { classifyConnection } from '../transport/connection-health'
|
||||
import { resolveHomeHostConnectionState } from '../transport/home-host-auto-connect'
|
||||
import type { ConnectionState, HostCatalogEntry } from '../transport/types'
|
||||
import type { HostWorktreeInfo } from '../worktree/home-worktree-info'
|
||||
import type { HomeHostConnections } from './home-host-connection-projection'
|
||||
import { MobileHomeListHeader } from './MobileHomeListHeader'
|
||||
import { homeListStyles } from './home-list-styles'
|
||||
import { MobileHomeDesktopRow } from './MobileHomeDesktopRow'
|
||||
|
||||
type MobileHomeHostListProps = {
|
||||
autoConnectHostIds: string[]
|
||||
bottomInset: number
|
||||
contentMaxWidth: number
|
||||
/** Sections above Desktops (Jump back in); null when there's nothing to resume. */
|
||||
header: ReactElement | null
|
||||
/** Rows after Desktops (the app update notice). */
|
||||
footer: ReactElement
|
||||
hostAttempts: Record<string, number>
|
||||
hostLastConnected: Record<string, number | null>
|
||||
@@ -22,48 +27,19 @@ type MobileHomeHostListProps = {
|
||||
hosts: HostCatalogEntry[]
|
||||
hostStates: Record<string, ConnectionState>
|
||||
isWideLayout: boolean
|
||||
stats: HomeStatsSummary | null
|
||||
worktreeInfo: Record<string, HostWorktreeInfo>
|
||||
onOpen: (host: HostCatalogEntry) => void
|
||||
onLongPress: (host: HostCatalogEntry) => void
|
||||
onOpenActions: (host: HostCatalogEntry) => void
|
||||
onPairDesktop: () => void
|
||||
}
|
||||
|
||||
export function MobileHomeHostList(props: MobileHomeHostListProps) {
|
||||
const renderHost = useCallback(
|
||||
({ item }: ListRenderItemInfo<HostCatalogEntry>) => (
|
||||
<MobileHomeHostRow
|
||||
item={item}
|
||||
autoConnectHostIds={props.autoConnectHostIds}
|
||||
hostAttempts={props.hostAttempts}
|
||||
hostLastConnected={props.hostLastConnected}
|
||||
hostConnections={props.hostConnections}
|
||||
hostStates={props.hostStates}
|
||||
worktreeInfo={props.worktreeInfo}
|
||||
onOpen={props.onOpen}
|
||||
onLongPress={props.onLongPress}
|
||||
onOpenActions={props.onOpenActions}
|
||||
/>
|
||||
),
|
||||
[
|
||||
props.autoConnectHostIds,
|
||||
props.hostAttempts,
|
||||
props.hostLastConnected,
|
||||
props.hostConnections,
|
||||
props.hostStates,
|
||||
props.onLongPress,
|
||||
props.onOpen,
|
||||
props.onOpenActions,
|
||||
props.worktreeInfo
|
||||
]
|
||||
)
|
||||
|
||||
const t = useMobileTranslation()
|
||||
useMobileColorScheme()
|
||||
return (
|
||||
<FlatList
|
||||
data={props.hosts}
|
||||
keyExtractor={(host) => host.id}
|
||||
<ScrollView
|
||||
contentContainerStyle={[
|
||||
styles.list,
|
||||
styles.content,
|
||||
{ paddingBottom: spacing.xl + props.bottomInset },
|
||||
props.isWideLayout && {
|
||||
maxWidth: props.contentMaxWidth,
|
||||
@@ -71,11 +47,49 @@ export function MobileHomeHostList(props: MobileHomeHostListProps) {
|
||||
alignSelf: 'center'
|
||||
}
|
||||
]}
|
||||
ListHeaderComponent={<MobileHomeListHeader stats={props.stats} />}
|
||||
ItemSeparatorComponent={CardGap}
|
||||
renderItem={renderHost}
|
||||
ListFooterComponent={props.footer}
|
||||
/>
|
||||
>
|
||||
{props.header}
|
||||
<Text
|
||||
style={[homeListStyles.sectionHeading, !props.header && homeListStyles.firstSectionHeading]}
|
||||
accessibilityRole="header"
|
||||
>
|
||||
{t('home.listHeader.desktopsHeading', 'Desktops')}
|
||||
</Text>
|
||||
<View style={homeListStyles.group}>
|
||||
{props.hosts.map((host, index) => (
|
||||
<Fragment key={host.id}>
|
||||
<MobileHomeHostRow
|
||||
item={host}
|
||||
autoConnectHostIds={props.autoConnectHostIds}
|
||||
hostAttempts={props.hostAttempts}
|
||||
hostLastConnected={props.hostLastConnected}
|
||||
hostConnections={props.hostConnections}
|
||||
hostStates={props.hostStates}
|
||||
worktreeInfo={props.worktreeInfo}
|
||||
onOpen={props.onOpen}
|
||||
onOpenActions={props.onOpenActions}
|
||||
/>
|
||||
<View
|
||||
style={[
|
||||
homeListStyles.separator,
|
||||
index === props.hosts.length - 1 && homeListStyles.fullSeparator
|
||||
]}
|
||||
/>
|
||||
</Fragment>
|
||||
))}
|
||||
<Pressable
|
||||
accessibilityRole="button"
|
||||
style={({ pressed }) => [homeListStyles.actionRow, pressed && homeListStyles.rowPressed]}
|
||||
onPress={props.onPairDesktop}
|
||||
>
|
||||
<Plus size={20} color={colors.accentBlue} />
|
||||
<Text style={homeListStyles.actionLabel}>
|
||||
{t('home.desktops.pairDesktop', 'Pair a desktop')}
|
||||
</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
{props.footer}
|
||||
</ScrollView>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -88,12 +102,11 @@ type MobileHomeHostRowProps = Pick<
|
||||
| 'hostStates'
|
||||
| 'worktreeInfo'
|
||||
| 'onOpen'
|
||||
| 'onLongPress'
|
||||
| 'onOpenActions'
|
||||
> & { item: HostCatalogEntry }
|
||||
|
||||
const MobileHomeHostRow = memo(function MobileHomeHostRow(props: MobileHomeHostRowProps) {
|
||||
const { item, onLongPress, onOpen, onOpenActions } = props
|
||||
const { item, onOpen, onOpenActions } = props
|
||||
const state = resolveHomeHostConnectionState(
|
||||
item.id,
|
||||
props.hostStates[item.id],
|
||||
@@ -111,11 +124,10 @@ const MobileHomeHostRow = memo(function MobileHomeHostRow(props: MobileHomeHostR
|
||||
hostName: item.name
|
||||
})
|
||||
const open = useCallback(() => onOpen(item), [item, onOpen])
|
||||
const longPress = useCallback(() => onLongPress(item), [item, onLongPress])
|
||||
const openActions = useCallback(() => onOpenActions(item), [item, onOpenActions])
|
||||
|
||||
return (
|
||||
<MobileHostCard
|
||||
<MobileHomeDesktopRow
|
||||
host={item}
|
||||
credentialStatus={item.credentialStatus}
|
||||
state={state}
|
||||
@@ -123,17 +135,11 @@ const MobileHomeHostRow = memo(function MobileHomeHostRow(props: MobileHomeHostR
|
||||
path={connection?.path ?? 'lan'}
|
||||
worktreeInfo={props.worktreeInfo[item.id]}
|
||||
onPress={open}
|
||||
onLongPress={longPress}
|
||||
onOpenActions={openActions}
|
||||
onLongPress={openActions}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
function CardGap() {
|
||||
return <View style={styles.cardGap} />
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
list: { paddingHorizontal: spacing.lg, paddingBottom: spacing.xl },
|
||||
cardGap: { height: spacing.sm }
|
||||
})
|
||||
const styles = themedStyleSheet(() => ({
|
||||
content: { paddingHorizontal: spacing.lg }
|
||||
}))
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
import { ChevronRight } from 'lucide-react-native'
|
||||
import { Fragment } from 'react'
|
||||
import { Pressable, Text, View } from 'react-native'
|
||||
import type { WorktreeStatus } from '../../../src/shared/worktree-status-rollup'
|
||||
import { AgentSpinner, agentStatusColors } from '../components/AgentSpinner'
|
||||
import { useNow } from '../hooks/use-now'
|
||||
import { useMobileTranslation } from '../i18n/use-mobile-translation'
|
||||
import { colors } from '../theme/mobile-theme'
|
||||
import { useMobileColorScheme } from '../theme/use-mobile-color-scheme'
|
||||
import { useHostDisplay } from '../transport/use-host-display'
|
||||
import type { HostCatalogEntry } from '../transport/types'
|
||||
import type { HomeJumpBackInRow } from '../worktree/home-jump-back-in'
|
||||
import { agentStateLabel, formatTimeAgo, type AgentDotState } from '../worktree/agent-row-display'
|
||||
import { homeListStyles as styles } from './home-list-styles'
|
||||
|
||||
const LABEL_STATE: Partial<Record<WorktreeStatus, AgentDotState>> = {
|
||||
permission: 'waiting',
|
||||
failed: 'failed',
|
||||
done: 'done',
|
||||
working: 'working',
|
||||
monitoring: 'monitoring'
|
||||
}
|
||||
|
||||
// Only states that ask something of the user take the dot's colour; running work stays quiet.
|
||||
const TINTED_STATUSES: ReadonlySet<WorktreeStatus> = new Set(['permission', 'failed', 'done'])
|
||||
|
||||
export function MobileHomeJumpBackIn(props: {
|
||||
rows: readonly HomeJumpBackInRow[]
|
||||
hosts: readonly HostCatalogEntry[]
|
||||
onOpen: (row: HomeJumpBackInRow) => void
|
||||
}) {
|
||||
const t = useMobileTranslation()
|
||||
useMobileColorScheme()
|
||||
const now = useNow()
|
||||
if (props.rows.length === 0) {
|
||||
return null
|
||||
}
|
||||
// One desktop needs no naming; the repo says more.
|
||||
const nameDesktops = props.hosts.length > 1
|
||||
return (
|
||||
<>
|
||||
<Text style={[styles.sectionHeading, styles.firstSectionHeading]} accessibilityRole="header">
|
||||
{t('home.jumpBackIn.heading', 'Jump back in')}
|
||||
</Text>
|
||||
<View style={styles.group}>
|
||||
{props.rows.map((row, index) => (
|
||||
<Fragment key={`${row.hostId}:${row.worktree.worktreeId}`}>
|
||||
{index > 0 ? <View style={styles.separator} /> : null}
|
||||
<JumpBackInRow
|
||||
row={row}
|
||||
host={props.hosts.find((host) => host.id === row.hostId) ?? null}
|
||||
nameDesktop={nameDesktops}
|
||||
now={now}
|
||||
onOpen={props.onOpen}
|
||||
/>
|
||||
</Fragment>
|
||||
))}
|
||||
</View>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function JumpBackInRow(props: {
|
||||
row: HomeJumpBackInRow
|
||||
host: HostCatalogEntry | null
|
||||
nameDesktop: boolean
|
||||
now: number
|
||||
onOpen: (row: HomeJumpBackInRow) => void
|
||||
}) {
|
||||
const t = useMobileTranslation()
|
||||
useMobileColorScheme()
|
||||
const { row } = props
|
||||
const desktopName = useHostDisplay(props.host).title
|
||||
const name = row.worktree.displayName || row.worktree.repo
|
||||
const labelState = LABEL_STATE[row.worktree.dotStatus]
|
||||
const status = row.lastOpened
|
||||
? row.actionable
|
||||
? t('home.jumpBackIn.lastOpened', 'Last opened')
|
||||
: t('home.jumpBackIn.connectToOpen', 'Connect {{desktop}} to open', { desktop: desktopName })
|
||||
: labelState
|
||||
? agentStateLabel(labelState)
|
||||
: ''
|
||||
const where = props.nameDesktop ? desktopName : row.worktree.repo
|
||||
const showWhere = Boolean(where) && (row.actionable || !row.lastOpened)
|
||||
const statusColor =
|
||||
!row.lastOpened && TINTED_STATUSES.has(row.worktree.dotStatus)
|
||||
? agentStatusColors()[row.worktree.dotStatus]
|
||||
: colors.textSecondary
|
||||
const time =
|
||||
typeof row.worktree.lastOutputAt === 'number'
|
||||
? formatTimeAgo(row.worktree.lastOutputAt, props.now)
|
||||
: null
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={[name, status, showWhere ? where : null, time].filter(Boolean).join(', ')}
|
||||
accessibilityState={{ disabled: !row.actionable }}
|
||||
disabled={!row.actionable}
|
||||
style={({ pressed }) => [styles.row, pressed && styles.rowPressed]}
|
||||
onPress={() => props.onOpen(row)}
|
||||
>
|
||||
<View style={[styles.status, !row.actionable && styles.rowDisabled]}>
|
||||
<AgentSpinner status={row.worktree.dotStatus} workingMode={row.worktree.workingMode} />
|
||||
</View>
|
||||
<View style={[styles.text, !row.actionable && styles.rowDisabled]}>
|
||||
<Text style={styles.title} numberOfLines={1}>
|
||||
{name}
|
||||
</Text>
|
||||
<Text style={styles.subtitle} numberOfLines={1}>
|
||||
<Text style={{ color: statusColor }}>{status}</Text>
|
||||
{showWhere ? ` · ${where}` : ''}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={styles.trailing}>
|
||||
{time ? <Text style={styles.time}>{time}</Text> : null}
|
||||
<ChevronRight size={16} color={colors.textMuted} />
|
||||
</View>
|
||||
</Pressable>
|
||||
)
|
||||
}
|
||||
@@ -1,188 +0,0 @@
|
||||
import { createElement } from 'react'
|
||||
import { act, create, type ReactTestInstance, type ReactTestRenderer } from 'react-test-renderer'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { TaskProvider } from '../../../src/shared/task-providers'
|
||||
import type { HostProfile } from '../transport/types'
|
||||
import { MobileHomeListFooter } from './MobileHomeListFooter'
|
||||
|
||||
vi.mock('react-native', () => ({
|
||||
Pressable: 'Pressable',
|
||||
StyleSheet: { create: <T,>(styles: T) => styles },
|
||||
Text: 'Text',
|
||||
View: 'View'
|
||||
}))
|
||||
|
||||
vi.mock('lucide-react-native', () => ({
|
||||
ChevronRight: 'ChevronRight',
|
||||
ListTodo: 'ListTodo',
|
||||
Plus: 'Plus',
|
||||
QrCode: 'QrCode'
|
||||
}))
|
||||
|
||||
vi.mock('../components/TaskProviderLogo', () => ({ TaskProviderLogo: 'TaskProviderLogo' }))
|
||||
vi.mock('./MobileHomeResumeCard', () => ({ MobileHomeResumeCard: 'MobileHomeResumeCard' }))
|
||||
vi.mock('./MobileHomeAccountUsageCards', () => ({
|
||||
MobileHomeAccountUsageCards: 'MobileHomeAccountUsageCards'
|
||||
}))
|
||||
|
||||
vi.mock('../components/PickerModal', () => ({ PickerModal: 'PickerModal' }))
|
||||
|
||||
function host(id: string, name: string, endpoint: string): HostProfile {
|
||||
return {
|
||||
id,
|
||||
name,
|
||||
endpoint,
|
||||
deviceToken: `token-${id}`,
|
||||
publicKeyB64: `key-${id}`,
|
||||
lastConnected: 1
|
||||
}
|
||||
}
|
||||
|
||||
const desk = host('desk', 'Desk', 'ws://192.168.1.2:6768')
|
||||
const laptop = host('laptop', 'Laptop', 'wss://relay.example.com/mobile')
|
||||
|
||||
const server = host('server', 'Server', 'wss://ssh.example.com/mobile')
|
||||
|
||||
describe('MobileHomeListFooter tasks', () => {
|
||||
let renderer: ReactTestRenderer | null = null
|
||||
|
||||
afterEach(() => {
|
||||
act(() => renderer?.unmount())
|
||||
renderer = null
|
||||
vi.restoreAllMocks()
|
||||
})
|
||||
|
||||
async function renderFooter(
|
||||
connectedHosts: HostProfile[],
|
||||
taskProvidersByHost: Record<string, TaskProvider[]> = {}
|
||||
) {
|
||||
const onOpenTasks = vi.fn()
|
||||
const onCreateWorkspace = vi.fn()
|
||||
const consoleError = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
||||
if (typeof args[0] !== 'string' || !args[0].includes('react-test-renderer is deprecated')) {
|
||||
throw new Error(String(args[0]))
|
||||
}
|
||||
})
|
||||
await act(async () => {
|
||||
renderer = create(
|
||||
createElement(MobileHomeListFooter, {
|
||||
accountsHosts: [],
|
||||
connectedHosts,
|
||||
resumeCard: null,
|
||||
taskProvidersByHost,
|
||||
onCreateWorkspace,
|
||||
onOpenAccounts: vi.fn(),
|
||||
onOpenResume: vi.fn(),
|
||||
onOpenTasks,
|
||||
onPairDesktop: vi.fn()
|
||||
})
|
||||
)
|
||||
})
|
||||
consoleError.mockRestore()
|
||||
return { onOpenTasks, onCreateWorkspace }
|
||||
}
|
||||
|
||||
function pressable(matches: (node: ReactTestInstance) => boolean) {
|
||||
const node = renderer!.root
|
||||
.findAll((candidate) => String(candidate.type) === 'Pressable')
|
||||
.find(matches)
|
||||
if (!node) {
|
||||
throw new Error('Pressable not rendered')
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
function pressableWithText(text: string) {
|
||||
return pressable((node) =>
|
||||
node
|
||||
.findAll((candidate) => String(candidate.type) === 'Text')
|
||||
.some((candidate) => candidate.props.children === text)
|
||||
)
|
||||
}
|
||||
|
||||
function providerChip(label: string) {
|
||||
return pressable((node) => node.props.accessibilityLabel === `Open ${label} tasks`)
|
||||
}
|
||||
|
||||
function picker(title: string) {
|
||||
return renderer!.root
|
||||
.findAll((node) => String(node.type) === 'PickerModal')
|
||||
.find((node) => node.props.title === title)!
|
||||
}
|
||||
|
||||
function tasksPicker() {
|
||||
return picker('Open Tasks On')
|
||||
}
|
||||
|
||||
it('opens the only connected host directly', async () => {
|
||||
const callbacks = await renderFooter([desk])
|
||||
|
||||
act(() => pressableWithText('Tasks').props.onPress())
|
||||
|
||||
expect(callbacks.onOpenTasks).toHaveBeenCalledWith('desk', undefined)
|
||||
expect(tasksPicker().props.visible).toBe(false)
|
||||
})
|
||||
|
||||
it('asks which connected host to open from the card', async () => {
|
||||
const callbacks = await renderFooter([desk, laptop])
|
||||
|
||||
act(() => pressableWithText('Tasks').props.onPress())
|
||||
|
||||
expect(callbacks.onOpenTasks).not.toHaveBeenCalled()
|
||||
expect(tasksPicker().props.visible).toBe(true)
|
||||
expect(tasksPicker().props.options).toEqual([
|
||||
{ value: 'desk', label: 'Desk', subtitle: '192.168.1.2:6768' },
|
||||
{ value: 'laptop', label: 'Laptop', subtitle: 'relay.example.com' }
|
||||
])
|
||||
|
||||
act(() => tasksPicker().props.onSelect('laptop'))
|
||||
act(() => tasksPicker().props.onAfterClose())
|
||||
|
||||
expect(callbacks.onOpenTasks).toHaveBeenCalledWith('laptop', undefined)
|
||||
})
|
||||
|
||||
it('lists each provider once, in the order connected hosts report them', async () => {
|
||||
await renderFooter([desk, laptop, server], {
|
||||
desk: ['github', 'jira'],
|
||||
laptop: ['gitlab', 'github'],
|
||||
server: ['jira', 'linear']
|
||||
})
|
||||
|
||||
const labels = renderer!.root
|
||||
.findAll((node) => String(node.type) === 'TaskProviderLogo')
|
||||
.map((node) => node.props.provider)
|
||||
expect(labels).toEqual(['github', 'jira', 'gitlab', 'linear'])
|
||||
})
|
||||
|
||||
it('opens a provider directly on the only host that has it', async () => {
|
||||
const callbacks = await renderFooter([desk, laptop], {
|
||||
desk: ['github'],
|
||||
laptop: ['github', 'jira']
|
||||
})
|
||||
|
||||
act(() => providerChip('Jira').props.onPress({ stopPropagation: () => {} }))
|
||||
|
||||
expect(tasksPicker().props.visible).toBe(false)
|
||||
expect(callbacks.onOpenTasks).toHaveBeenCalledWith('laptop', 'jira')
|
||||
})
|
||||
|
||||
it('offers only the hosts that have the tapped provider', async () => {
|
||||
const callbacks = await renderFooter([desk, laptop, server], {
|
||||
desk: ['github', 'jira'],
|
||||
laptop: ['github'],
|
||||
server: ['jira']
|
||||
})
|
||||
|
||||
act(() => providerChip('Jira').props.onPress({ stopPropagation: () => {} }))
|
||||
|
||||
expect(tasksPicker().props.visible).toBe(true)
|
||||
expect(tasksPicker().props.options.map((option: { value: string }) => option.value)).toEqual([
|
||||
'desk',
|
||||
'server'
|
||||
])
|
||||
act(() => tasksPicker().props.onSelect('server'))
|
||||
act(() => tasksPicker().props.onAfterClose())
|
||||
|
||||
expect(callbacks.onOpenTasks).toHaveBeenCalledWith('server', 'jira')
|
||||
})
|
||||
})
|
||||
@@ -1,88 +0,0 @@
|
||||
import { Text, View } from 'react-native'
|
||||
import { useMobileTranslation } from '../i18n/use-mobile-translation'
|
||||
import type { AccountsSnapshot } from '../components/AccountUsage'
|
||||
import { MobileHomeQuickActions } from '../components/MobileHomeQuickActions'
|
||||
import { PickerModal } from '../components/PickerModal'
|
||||
import { useConnectedHostPicker } from '../components/use-connected-host-picker'
|
||||
import type { TaskProvider } from '../../../src/shared/task-providers'
|
||||
import { colors, spacing } from '../theme/mobile-theme'
|
||||
import { themedStyleSheet } from '../theme/themed-styles'
|
||||
import { useMobileColorScheme } from '../theme/use-mobile-color-scheme'
|
||||
import type { HostProfile } from '../transport/types'
|
||||
import type { HomeResumeCard } from '../worktree/home-resume-card'
|
||||
import { MobileHomeAccountUsageCards } from './MobileHomeAccountUsageCards'
|
||||
import { MobileHomeResumeCard } from './MobileHomeResumeCard'
|
||||
import { MobileHomeTasksCard } from './MobileHomeTasksCard'
|
||||
|
||||
const DEFAULT_TASK_PROVIDERS: TaskProvider[] = ['github']
|
||||
|
||||
export function MobileHomeListFooter(props: {
|
||||
accountsHosts: { host: HostProfile; snapshot: AccountsSnapshot }[]
|
||||
connectedHosts: HostProfile[]
|
||||
resumeCard: HomeResumeCard | null
|
||||
taskProvidersByHost: Record<string, TaskProvider[]>
|
||||
onCreateWorkspace: () => void
|
||||
onOpenAccounts: (hostId: string) => void
|
||||
onOpenResume: (card: HomeResumeCard) => void
|
||||
onOpenTasks: (hostId: string, provider?: TaskProvider) => void
|
||||
onPairDesktop: () => void
|
||||
}) {
|
||||
const t = useMobileTranslation()
|
||||
useMobileColorScheme()
|
||||
const tasksHostPicker = useConnectedHostPicker(props.connectedHosts)
|
||||
const hostProviders = props.connectedHosts.map((host) => ({
|
||||
host,
|
||||
providers: props.taskProvidersByHost[host.id] ?? DEFAULT_TASK_PROVIDERS
|
||||
}))
|
||||
const providers = [...new Set(hostProviders.flatMap((entry) => entry.providers))]
|
||||
|
||||
function openTasks(provider?: TaskProvider) {
|
||||
// Why: a provider chip offers only desktops that have it, so it never opens a different one.
|
||||
const hosts = provider
|
||||
? hostProviders
|
||||
.filter((entry) => entry.providers.includes(provider))
|
||||
.map((entry) => entry.host)
|
||||
: props.connectedHosts
|
||||
tasksHostPicker.pickHost((hostId) => props.onOpenTasks(hostId, provider), hosts)
|
||||
}
|
||||
|
||||
return (
|
||||
<View>
|
||||
{props.resumeCard ? (
|
||||
<>
|
||||
<Text style={styles.sectionHeading}>{t('home.listFooter.resumeHeading', 'Resume')}</Text>
|
||||
<MobileHomeResumeCard card={props.resumeCard} onOpen={props.onOpenResume} />
|
||||
</>
|
||||
) : null}
|
||||
<Text style={styles.sectionHeading}>{t('home.listFooter.tasksHeading', 'Tasks')}</Text>
|
||||
<MobileHomeTasksCard
|
||||
enabled={props.connectedHosts.length > 0}
|
||||
providers={providers}
|
||||
onOpen={openTasks}
|
||||
/>
|
||||
<PickerModal
|
||||
title={t('home.listFooter.hostPickerTitle', 'Open Tasks On')}
|
||||
{...tasksHostPicker.pickerProps}
|
||||
/>
|
||||
<MobileHomeQuickActions
|
||||
connectedHosts={props.connectedHosts}
|
||||
onPairDesktop={props.onPairDesktop}
|
||||
onCreateWorkspace={props.onCreateWorkspace}
|
||||
/>
|
||||
<MobileHomeAccountUsageCards items={props.accountsHosts} onOpen={props.onOpenAccounts} />
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = themedStyleSheet(() => ({
|
||||
sectionHeading: {
|
||||
fontSize: 11,
|
||||
fontWeight: '600',
|
||||
color: colors.textMuted,
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: 0.6,
|
||||
marginTop: spacing.lg,
|
||||
marginBottom: spacing.sm,
|
||||
paddingHorizontal: spacing.xs
|
||||
}
|
||||
}))
|
||||
@@ -1,95 +0,0 @@
|
||||
import { Text, View } from 'react-native'
|
||||
import type { Translate } from '../i18n/mobile-i18n'
|
||||
import { getUiIntlLocale } from '../i18n/ui-intl-locale'
|
||||
import { useMobileTranslation } from '../i18n/use-mobile-translation'
|
||||
import type { HomeStatsSummary } from '../stats/home-stats-total'
|
||||
import { colors, radii, spacing } from '../theme/mobile-theme'
|
||||
import { themedStyleSheet } from '../theme/themed-styles'
|
||||
import { useMobileColorScheme } from '../theme/use-mobile-color-scheme'
|
||||
import { MobileHomeAppUpdateCard } from './MobileHomeAppUpdateCard'
|
||||
|
||||
function formatDuration(ms: number, t: Translate): string {
|
||||
const totalMinutes = Math.floor(ms / 60_000)
|
||||
const totalHours = Math.floor(totalMinutes / 60)
|
||||
const days = Math.floor(totalHours / 24)
|
||||
const hours = totalHours % 24
|
||||
if (days > 0) {
|
||||
return t('home.listHeader.daysHours', '{{days}}d {{hours}}h', { days, hours })
|
||||
}
|
||||
const minutes = totalMinutes % 60
|
||||
return totalHours > 0
|
||||
? t('home.listHeader.hoursMinutes', '{{hours}}h {{minutes}}m', { hours: totalHours, minutes })
|
||||
: t('home.listHeader.minutes', '{{minutes}}m', { minutes: totalMinutes })
|
||||
}
|
||||
|
||||
export function MobileHomeListHeader({ stats }: { stats: HomeStatsSummary | null }) {
|
||||
const t = useMobileTranslation()
|
||||
useMobileColorScheme()
|
||||
return (
|
||||
<View>
|
||||
<View style={styles.hero}>
|
||||
<Text style={styles.heroTitle}>{t('home.listHeader.title', 'Welcome back')}</Text>
|
||||
</View>
|
||||
{stats ? (
|
||||
<View style={styles.statsRow}>
|
||||
<View style={styles.statCard}>
|
||||
<Text style={styles.statValue}>
|
||||
{stats.totalAgentsSpawned.toLocaleString(getUiIntlLocale())}
|
||||
</Text>
|
||||
<Text style={styles.statLabel}>
|
||||
{t('home.listHeader.agentsSpawned', 'Agents spawned')}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={styles.statCard}>
|
||||
<Text style={styles.statValue}>{formatDuration(stats.totalAgentTimeMs, t)}</Text>
|
||||
<Text style={styles.statLabel}>{t('home.listHeader.agentTime', 'Agent time')}</Text>
|
||||
</View>
|
||||
<View style={styles.statCard}>
|
||||
<Text style={styles.statValue}>
|
||||
{stats.totalPRsCreated.toLocaleString(getUiIntlLocale())}
|
||||
</Text>
|
||||
<Text style={styles.statLabel}>{t('home.listHeader.prsCreated', 'PRs created')}</Text>
|
||||
</View>
|
||||
</View>
|
||||
) : null}
|
||||
<MobileHomeAppUpdateCard />
|
||||
<Text style={styles.sectionHeading}>{t('home.listHeader.desktopsHeading', 'Desktops')}</Text>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = themedStyleSheet(() => ({
|
||||
hero: { paddingTop: spacing.xs, paddingBottom: spacing.md },
|
||||
heroTitle: {
|
||||
color: colors.textPrimary,
|
||||
fontSize: 24,
|
||||
fontWeight: '800',
|
||||
letterSpacing: -0.3
|
||||
},
|
||||
statsRow: { flexDirection: 'row', gap: 10, marginBottom: spacing.lg },
|
||||
statCard: {
|
||||
flex: 1,
|
||||
backgroundColor: colors.bgPanel,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.borderSubtle,
|
||||
borderRadius: radii.surface,
|
||||
paddingVertical: 8,
|
||||
paddingHorizontal: spacing.md
|
||||
},
|
||||
statValue: {
|
||||
color: colors.textPrimary,
|
||||
fontSize: 18,
|
||||
fontWeight: '700',
|
||||
letterSpacing: -0.3
|
||||
},
|
||||
statLabel: { color: colors.textMuted, fontSize: 11, fontWeight: '500', marginTop: 2 },
|
||||
sectionHeading: {
|
||||
fontSize: 11,
|
||||
fontWeight: '600',
|
||||
color: colors.textMuted,
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: 0.6,
|
||||
marginBottom: spacing.sm,
|
||||
paddingHorizontal: spacing.xs
|
||||
}
|
||||
}))
|
||||
@@ -1,87 +0,0 @@
|
||||
import { ChevronRight, Terminal } from 'lucide-react-native'
|
||||
import { Pressable, Text, View } from 'react-native'
|
||||
import { colors, radii, spacing } from '../theme/mobile-theme'
|
||||
import { themedStyleSheet } from '../theme/themed-styles'
|
||||
import { useMobileColorScheme } from '../theme/use-mobile-color-scheme'
|
||||
import type { HomeResumeCard } from '../worktree/home-resume-card'
|
||||
|
||||
const REPO_COLORS = ['#8b5cf6', '#3b82f6', '#22c55e', '#f59e0b', '#ef4444', '#ec4899', '#06b6d4']
|
||||
|
||||
function homeResumeRepoColor(name: string): string {
|
||||
let hash = 0
|
||||
for (let index = 0; index < name.length; index += 1) {
|
||||
hash = (hash * 31 + name.charCodeAt(index)) | 0
|
||||
}
|
||||
return REPO_COLORS[Math.abs(hash) % REPO_COLORS.length]
|
||||
}
|
||||
|
||||
export function MobileHomeResumeCard(props: {
|
||||
card: HomeResumeCard
|
||||
onOpen: (card: HomeResumeCard) => void
|
||||
}) {
|
||||
useMobileColorScheme()
|
||||
return (
|
||||
<Pressable
|
||||
disabled={!props.card.actionable}
|
||||
style={({ pressed }) => [
|
||||
styles.resumeCard,
|
||||
!props.card.actionable && styles.cardDisabled,
|
||||
pressed && styles.cardPressed
|
||||
]}
|
||||
onPress={() => props.onOpen(props.card)}
|
||||
>
|
||||
<View style={styles.resumeIcon}>
|
||||
<Terminal size={18} color={colors.textSecondary} />
|
||||
</View>
|
||||
<View style={styles.resumeMain}>
|
||||
<Text style={styles.resumeTitle} numberOfLines={1}>
|
||||
{props.card.worktree.displayName}
|
||||
</Text>
|
||||
<View style={styles.resumeSub}>
|
||||
<View
|
||||
style={[
|
||||
styles.repoDot,
|
||||
{ backgroundColor: homeResumeRepoColor(props.card.worktree.repo) }
|
||||
]}
|
||||
/>
|
||||
<Text style={styles.resumeSubText} numberOfLines={1}>
|
||||
{props.card.worktree.repo}
|
||||
{' · '}
|
||||
{props.card.worktree.branch}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
<ChevronRight size={16} color={colors.textMuted} />
|
||||
</Pressable>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = themedStyleSheet(() => ({
|
||||
resumeCard: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: colors.bgPanel,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.borderSubtle,
|
||||
borderRadius: radii.surface,
|
||||
paddingLeft: spacing.md,
|
||||
paddingRight: spacing.md,
|
||||
paddingVertical: 12
|
||||
},
|
||||
cardDisabled: { opacity: 0.45 },
|
||||
cardPressed: { backgroundColor: colors.bgRaised },
|
||||
resumeIcon: {
|
||||
width: 46,
|
||||
height: 46,
|
||||
borderRadius: radii.tile,
|
||||
backgroundColor: colors.bgRaised,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginRight: 14
|
||||
},
|
||||
resumeMain: { flex: 1, minWidth: 0 },
|
||||
resumeTitle: { fontSize: 13, fontWeight: '600', color: colors.textPrimary },
|
||||
resumeSub: { flexDirection: 'row', alignItems: 'center', gap: 6, marginTop: 3 },
|
||||
repoDot: { width: 7, height: 7, borderRadius: radii.pill },
|
||||
resumeSubText: { fontSize: 12, color: colors.textSecondary, flex: 1 }
|
||||
}))
|
||||
@@ -1,8 +1,7 @@
|
||||
import { useCallback, useState } from 'react'
|
||||
import { Alert } from 'react-native'
|
||||
import { Alert, View } from 'react-native'
|
||||
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context'
|
||||
import { useMobileTranslation } from '../i18n/use-mobile-translation'
|
||||
import { useOpenMobileAccounts } from '../accounts/use-open-mobile-accounts'
|
||||
import { getProvenCachedWorktrees } from '../cache/worktree-cache'
|
||||
import { ActionSheetModal } from '../components/ActionSheetModal'
|
||||
import { ConfirmModal } from '../components/ConfirmModal'
|
||||
@@ -26,11 +25,14 @@ import { removeHostAndCloseClient } from '../transport/host-removal-lifecycle'
|
||||
import { loadHostCatalog } from '../transport/host-store'
|
||||
import type { HostCatalogEntry, HostProfile } from '../transport/types'
|
||||
import { useOpenMobileHostEdit } from '../transport/use-open-mobile-host-edit'
|
||||
import type { HomeWorktreeSummary } from '../worktree/home-worktree-info'
|
||||
import { isResumeTargetConfirmedMissing, type HomeResumeCard } from '../worktree/home-resume-card'
|
||||
import { mobileConnectionPathLabel } from '../transport/mobile-connection-path-label'
|
||||
import type { HomeJumpBackInRow } from '../worktree/home-jump-back-in'
|
||||
import { isResumeTargetConfirmedMissing } from '../worktree/home-resume-card'
|
||||
import { MobileHomeAppUpdateCard } from './MobileHomeAppUpdateCard'
|
||||
import { MobileHomeBottomBar, MobileHomePairBar } from './MobileHomeBottomBar'
|
||||
import { MobileHomeEmptyState } from './MobileHomeEmptyState'
|
||||
import { MobileHomeHostList } from './MobileHomeHostList'
|
||||
import { MobileHomeListFooter } from './MobileHomeListFooter'
|
||||
import { MobileHomeJumpBackIn } from './MobileHomeJumpBackIn'
|
||||
import { MobileHomeTopBar } from './MobileHomeTopBar'
|
||||
import { useMobileHomeData } from './use-mobile-home-data'
|
||||
import { newWorkspaceOverlayHref } from '../new-workspace/new-workspace-overlay-route'
|
||||
@@ -44,28 +46,22 @@ export function MobileHomeScreen() {
|
||||
const openMobileHostEdit = useOpenMobileHostEdit()
|
||||
const openMobileTasks = useOpenMobileTasks()
|
||||
const openMobileSession = useOpenMobileSession()
|
||||
const openMobileAccounts = useOpenMobileAccounts()
|
||||
const disconnectHostClient = useDisconnectHostClient()
|
||||
const forgetHostClient = useForgetHostClient()
|
||||
const forceReconnectHost = useForceReconnect()
|
||||
const [actionTarget, setActionTarget] = useState<HostProfile | null>(null)
|
||||
const [confirmRemove, setConfirmRemove] = useState<{ id: string; name: string } | null>(null)
|
||||
|
||||
const openResume = useCallback(
|
||||
(card: HomeResumeCard) => {
|
||||
if (
|
||||
isResumeTargetConfirmedMissing(
|
||||
card,
|
||||
getProvenCachedWorktrees(card.hostId) as HomeWorktreeSummary[] | null
|
||||
)
|
||||
) {
|
||||
data.router.push(hostRouteWithNotice(card.hostId, 'worktree-missing'))
|
||||
const openWorktree = useCallback(
|
||||
(row: HomeJumpBackInRow) => {
|
||||
if (isResumeTargetConfirmedMissing(row, getProvenCachedWorktrees(row.hostId))) {
|
||||
data.router.push(hostRouteWithNotice(row.hostId, 'worktree-missing'))
|
||||
return
|
||||
}
|
||||
openMobileSession({
|
||||
hostId: card.hostId,
|
||||
worktreeId: card.worktree.worktreeId,
|
||||
name: card.worktree.displayName || card.worktree.repo
|
||||
hostId: row.hostId,
|
||||
worktreeId: row.worktree.worktreeId,
|
||||
name: row.worktree.displayName || row.worktree.repo
|
||||
})
|
||||
},
|
||||
[data.router, openMobileSession]
|
||||
@@ -114,58 +110,86 @@ export function MobileHomeScreen() {
|
||||
}
|
||||
}
|
||||
|
||||
const pairDesktop = () => data.router.push('/pair-scan')
|
||||
const actionTargetPath = actionTarget ? data.hostConnections[actionTarget.id]?.path : undefined
|
||||
const actionTargetConnected =
|
||||
actionTarget != null && data.hostStates[actionTarget.id] === 'connected'
|
||||
|
||||
return (
|
||||
<SafeAreaView style={styles.container} edges={['top']}>
|
||||
<MobileHomeTopBar onOpenSettings={() => data.router.push('/settings')} />
|
||||
{data.hostCatalog.length === 0 ? (
|
||||
<MobileHomeEmptyState
|
||||
bottomInset={insets.bottom}
|
||||
contentMaxWidth={contentMaxWidth}
|
||||
isWideLayout={isWideLayout}
|
||||
onPairDesktop={() => data.router.push('/pair-scan')}
|
||||
/>
|
||||
<MobileHomeTopBar
|
||||
showBrand={!data.hostCatalogLoaded || data.hostCatalog.length > 0}
|
||||
onOpenSettings={() => data.router.push('/settings')}
|
||||
/>
|
||||
{!data.hostCatalogLoaded ? (
|
||||
<View style={styles.fill} />
|
||||
) : data.hostCatalog.length === 0 ? (
|
||||
<>
|
||||
<MobileHomeEmptyState contentMaxWidth={contentMaxWidth} isWideLayout={isWideLayout} />
|
||||
<MobileHomePairBar
|
||||
bottomInset={insets.bottom}
|
||||
contentMaxWidth={contentMaxWidth}
|
||||
isWideLayout={isWideLayout}
|
||||
onPairDesktop={pairDesktop}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<MobileHomeHostList
|
||||
autoConnectHostIds={data.autoConnectHostIds}
|
||||
bottomInset={insets.bottom}
|
||||
contentMaxWidth={contentMaxWidth}
|
||||
footer={
|
||||
<MobileHomeListFooter
|
||||
accountsHosts={data.accountsHosts}
|
||||
connectedHosts={data.connectedHosts}
|
||||
resumeCard={data.resumeCard}
|
||||
taskProvidersByHost={data.taskProvidersByHost}
|
||||
onCreateWorkspace={() =>
|
||||
data.router.push(
|
||||
newWorkspaceOverlayHref(data.connectedHosts.map((host) => host.id))
|
||||
)
|
||||
}
|
||||
onOpenAccounts={openMobileAccounts}
|
||||
onOpenResume={openResume}
|
||||
onOpenTasks={openMobileTasks}
|
||||
onPairDesktop={() => data.router.push('/pair-scan')}
|
||||
/>
|
||||
}
|
||||
hostAttempts={data.hostAttempts}
|
||||
hostLastConnected={data.hostLastConnected}
|
||||
hostConnections={data.hostConnections}
|
||||
hosts={data.sortedHostCatalog}
|
||||
hostStates={data.hostStates}
|
||||
isWideLayout={isWideLayout}
|
||||
stats={data.stats}
|
||||
worktreeInfo={data.worktreeInfo}
|
||||
onOpen={openHost}
|
||||
onLongPress={(host) => {
|
||||
triggerMediumImpact()
|
||||
openHostActions(host)
|
||||
}}
|
||||
onOpenActions={openHostActions}
|
||||
/>
|
||||
<>
|
||||
<MobileHomeHostList
|
||||
autoConnectHostIds={data.autoConnectHostIds}
|
||||
bottomInset={0}
|
||||
contentMaxWidth={contentMaxWidth}
|
||||
header={
|
||||
data.jumpBackInRows.length > 0 ? (
|
||||
<MobileHomeJumpBackIn
|
||||
rows={data.jumpBackInRows}
|
||||
hosts={data.sortedHostCatalog}
|
||||
onOpen={openWorktree}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
footer={<MobileHomeAppUpdateCard />}
|
||||
hostAttempts={data.hostAttempts}
|
||||
hostLastConnected={data.hostLastConnected}
|
||||
hostConnections={data.hostConnections}
|
||||
hosts={data.sortedHostCatalog}
|
||||
hostStates={data.hostStates}
|
||||
isWideLayout={isWideLayout}
|
||||
worktreeInfo={data.worktreeInfo}
|
||||
onOpen={openHost}
|
||||
onOpenActions={(host) => {
|
||||
triggerMediumImpact()
|
||||
openHostActions(host)
|
||||
}}
|
||||
onPairDesktop={pairDesktop}
|
||||
/>
|
||||
<MobileHomeBottomBar
|
||||
bottomInset={insets.bottom}
|
||||
contentMaxWidth={contentMaxWidth}
|
||||
isWideLayout={isWideLayout}
|
||||
connectedHosts={data.connectedHosts}
|
||||
onOpenTasks={(hostId) => openMobileTasks(hostId)}
|
||||
onCreateWorkspace={() =>
|
||||
data.router.push(newWorkspaceOverlayHref(data.connectedHosts.map((host) => host.id)))
|
||||
}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<ActionSheetModal
|
||||
visible={actionTarget != null}
|
||||
title={actionTarget?.name}
|
||||
message={actionTarget ? hostEndpointLabel(actionTarget.endpoint) : undefined}
|
||||
message={
|
||||
actionTarget
|
||||
? [
|
||||
actionTargetConnected && actionTargetPath
|
||||
? mobileConnectionPathLabel(actionTargetPath)
|
||||
: null,
|
||||
hostEndpointLabel(actionTarget.endpoint)
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' · ')
|
||||
: undefined
|
||||
}
|
||||
actions={getHostListActionSheetActions({
|
||||
host: actionTarget,
|
||||
state: actionTarget
|
||||
@@ -204,5 +228,6 @@ export function MobileHomeScreen() {
|
||||
}
|
||||
|
||||
const styles = themedStyleSheet(() => ({
|
||||
container: { flex: 1, backgroundColor: colors.bgBase }
|
||||
container: { flex: 1, backgroundColor: colors.bgBase },
|
||||
fill: { flex: 1 }
|
||||
}))
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
import { ChevronRight, ListTodo } from 'lucide-react-native'
|
||||
import { Pressable, Text, View } from 'react-native'
|
||||
import { useMobileTranslation } from '../i18n/use-mobile-translation'
|
||||
import { TaskProviderLogo } from '../components/TaskProviderLogo'
|
||||
import type { TaskProvider } from '../../../src/shared/task-providers'
|
||||
import { colors, radii, spacing } from '../theme/mobile-theme'
|
||||
import { themedStyleSheet } from '../theme/themed-styles'
|
||||
import { useMobileColorScheme } from '../theme/use-mobile-color-scheme'
|
||||
|
||||
const TASK_PROVIDER_LABELS: Record<TaskProvider, string> = {
|
||||
github: 'GitHub',
|
||||
gitlab: 'GitLab',
|
||||
linear: 'Linear',
|
||||
jira: 'Jira'
|
||||
}
|
||||
|
||||
export function MobileHomeTasksCard(props: {
|
||||
enabled: boolean
|
||||
providers: TaskProvider[]
|
||||
onOpen: (provider?: TaskProvider) => void
|
||||
}) {
|
||||
const t = useMobileTranslation()
|
||||
useMobileColorScheme()
|
||||
return (
|
||||
<Pressable
|
||||
disabled={!props.enabled}
|
||||
style={({ pressed }) => [
|
||||
styles.card,
|
||||
!props.enabled && styles.cardDisabled,
|
||||
pressed && styles.cardPressed
|
||||
]}
|
||||
onPress={() => props.onOpen()}
|
||||
>
|
||||
<View style={styles.icon}>
|
||||
<ListTodo size={18} color={colors.textSecondary} />
|
||||
</View>
|
||||
<View style={styles.main}>
|
||||
<Text style={styles.title}>{t('home.tasksCard.title', 'Tasks')}</Text>
|
||||
<Text style={styles.subtitle} numberOfLines={1}>
|
||||
{props.providers.length > 0
|
||||
? props.providers.map((provider) => TASK_PROVIDER_LABELS[provider]).join(' · ')
|
||||
: t('home.tasksCard.noSources', 'No task sources connected')}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={styles.trailing}>
|
||||
<View
|
||||
style={styles.providerRow}
|
||||
accessibilityLabel={props.providers
|
||||
.map((provider) => TASK_PROVIDER_LABELS[provider])
|
||||
.join(', ')}
|
||||
>
|
||||
{props.providers.map((provider) => (
|
||||
<Pressable
|
||||
key={provider}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={t('home.tasksCard.openProviderTasks', 'Open {{provider}} tasks', {
|
||||
provider: TASK_PROVIDER_LABELS[provider]
|
||||
})}
|
||||
hitSlop={8}
|
||||
style={({ pressed }) => [
|
||||
styles.providerButton,
|
||||
pressed && styles.providerButtonPressed
|
||||
]}
|
||||
onPress={(event) => {
|
||||
event.stopPropagation()
|
||||
props.onOpen(provider)
|
||||
}}
|
||||
>
|
||||
<TaskProviderLogo provider={provider} size={22} color={colors.textSecondary} />
|
||||
</Pressable>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
<ChevronRight size={16} color={colors.textMuted} />
|
||||
</Pressable>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = themedStyleSheet(() => ({
|
||||
card: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: colors.bgPanel,
|
||||
borderWidth: 1,
|
||||
borderColor: colors.borderSubtle,
|
||||
borderRadius: radii.surface,
|
||||
minHeight: 72,
|
||||
paddingLeft: spacing.md,
|
||||
paddingRight: spacing.md,
|
||||
paddingVertical: 12
|
||||
},
|
||||
cardDisabled: { opacity: 0.45 },
|
||||
cardPressed: { backgroundColor: colors.bgRaised },
|
||||
icon: {
|
||||
width: 46,
|
||||
height: 46,
|
||||
borderRadius: radii.tile,
|
||||
backgroundColor: colors.bgRaised,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginRight: 14
|
||||
},
|
||||
main: { flex: 1, minWidth: 0 },
|
||||
title: { fontSize: 13, fontWeight: '600', color: colors.textPrimary },
|
||||
subtitle: { fontSize: 12, color: colors.textSecondary, marginTop: 3 },
|
||||
trailing: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
flexShrink: 0,
|
||||
marginLeft: spacing.sm
|
||||
},
|
||||
providerRow: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'flex-end',
|
||||
gap: 2
|
||||
},
|
||||
providerButton: {
|
||||
width: 34,
|
||||
height: 34,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
borderRadius: radii.control
|
||||
},
|
||||
providerButtonPressed: { backgroundColor: colors.bgRaised }
|
||||
}))
|
||||
@@ -6,25 +6,35 @@ import { colors, radii, spacing } from '../theme/mobile-theme'
|
||||
import { themedStyleSheet } from '../theme/themed-styles'
|
||||
import { useMobileColorScheme } from '../theme/use-mobile-color-scheme'
|
||||
|
||||
export function MobileHomeTopBar({ onOpenSettings }: { onOpenSettings: () => void }) {
|
||||
export function MobileHomeTopBar({
|
||||
onOpenSettings,
|
||||
showBrand = true
|
||||
}: {
|
||||
onOpenSettings: () => void
|
||||
/** First run draws the logo large below, so the bar keeps only Settings. */
|
||||
showBrand?: boolean
|
||||
}) {
|
||||
const t = useMobileTranslation()
|
||||
useMobileColorScheme()
|
||||
return (
|
||||
<View style={styles.topBar}>
|
||||
<View style={styles.brandLockup}>
|
||||
<View style={styles.logoMark}>
|
||||
<OrcaLogo size={18} />
|
||||
{showBrand ? (
|
||||
<View style={styles.brandLockup}>
|
||||
<View style={styles.logoMark}>
|
||||
<OrcaLogo size={19} />
|
||||
</View>
|
||||
<Text style={styles.brandName}>Orca</Text>
|
||||
</View>
|
||||
<Text style={styles.brandName}>Orca</Text>
|
||||
</View>
|
||||
) : (
|
||||
<View />
|
||||
)}
|
||||
<Pressable
|
||||
style={({ pressed }) => [styles.iconButton, pressed && styles.iconButtonPressed]}
|
||||
onPress={onOpenSettings}
|
||||
hitSlop={8}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={t('common.settings', 'Settings')}
|
||||
>
|
||||
<Settings size={18} color={colors.textSecondary} />
|
||||
<Settings size={22} color={colors.textSecondary} />
|
||||
</Pressable>
|
||||
</View>
|
||||
)
|
||||
@@ -35,16 +45,17 @@ const styles = themedStyleSheet(() => ({
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
paddingHorizontal: spacing.lg,
|
||||
paddingTop: spacing.sm,
|
||||
paddingBottom: spacing.md
|
||||
paddingLeft: spacing.lg + spacing.xs,
|
||||
paddingRight: spacing.sm,
|
||||
paddingTop: spacing.xs,
|
||||
paddingBottom: spacing.xs
|
||||
},
|
||||
brandLockup: { flexDirection: 'row', alignItems: 'center', minWidth: 0 },
|
||||
logoMark: { marginRight: spacing.sm },
|
||||
brandName: { color: colors.textPrimary, fontSize: 17, fontWeight: '700' },
|
||||
brandName: { color: colors.textPrimary, fontSize: 20, fontWeight: '700', letterSpacing: -0.4 },
|
||||
iconButton: {
|
||||
width: 36,
|
||||
height: 36,
|
||||
width: 44,
|
||||
height: 44,
|
||||
borderRadius: radii.control,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import type { z } from 'zod'
|
||||
import { totalHomeStats } from '../stats/home-stats-total'
|
||||
import { homeHostAccountsSchema, homeHostStatsSchema } from './home-host-reply-schema'
|
||||
|
||||
function reads<T>(schema: z.ZodType<T, unknown>, value: unknown): T {
|
||||
const parsed = schema.safeParse(value)
|
||||
if (!parsed.success) {
|
||||
throw new Error(`expected a readable reply: ${parsed.error.message}`)
|
||||
}
|
||||
return parsed.data
|
||||
}
|
||||
|
||||
function readsRow(value: unknown): NonNullable<z.output<typeof homeHostStatsSchema>> {
|
||||
const row = reads(homeHostStatsSchema, value)
|
||||
if (!row) {
|
||||
throw new Error('expected an object row')
|
||||
}
|
||||
return row
|
||||
}
|
||||
|
||||
function refuses(schema: z.ZodType<unknown, unknown>, value: unknown): boolean {
|
||||
return !schema.safeParse(value).success
|
||||
}
|
||||
|
||||
describe('a stats row is checked as an object and nothing more', () => {
|
||||
it('reads the row a current host sends', () => {
|
||||
const row = {
|
||||
totalAgentsSpawned: 3,
|
||||
totalPRsCreated: 1,
|
||||
totalAgentTimeMs: 90,
|
||||
firstEventAt: 1700000000000
|
||||
}
|
||||
expect(readsRow(row)).toMatchObject(row)
|
||||
})
|
||||
|
||||
it('reads a host that answers a shape totalHomeStats still sums', () => {
|
||||
expect(readsRow({ totalWorktrees: 3 })).toMatchObject({ totalWorktrees: 3 })
|
||||
expect(readsRow({}).totalAgentsSpawned).toBe(undefined)
|
||||
})
|
||||
|
||||
it('preserves an explicit null firstEventAt, which the total reads as no events yet', () => {
|
||||
expect(readsRow({ firstEventAt: null }).firstEventAt).toBe(null)
|
||||
expect(readsRow({ firstEventAt: 'never' }).firstEventAt).toBe(undefined)
|
||||
})
|
||||
|
||||
// Unreadable summaries must still occupy the host slot: totals skip them and render zero; refusal would omit the slot and remove the header row.
|
||||
it('seats a non-object summary in the slot rather than refusing the host out of the total', () => {
|
||||
for (const value of ['garbage', 7, true, []]) {
|
||||
expect(refuses(homeHostStatsSchema, value)).toBe(false)
|
||||
expect(reads(homeHostStatsSchema, value)).toBe(undefined)
|
||||
}
|
||||
expect(totalHomeStats({ 'host-1': reads(homeHostStatsSchema, 'garbage') }, ['host-1'])).toEqual(
|
||||
{
|
||||
totalAgentsSpawned: 0,
|
||||
totalPRsCreated: 0,
|
||||
totalAgentTimeMs: 0,
|
||||
firstEventAt: null
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it('seats a null or absent summary in the card slot, which the total skips', () => {
|
||||
expect(refuses(homeHostStatsSchema, null)).toBe(false)
|
||||
expect(refuses(homeHostStatsSchema, undefined)).toBe(false)
|
||||
expect(reads(homeHostStatsSchema, null)).toBe(null)
|
||||
expect(totalHomeStats({ 'host-1': reads(homeHostStatsSchema, null) }, ['host-1'])).toEqual({
|
||||
totalAgentsSpawned: 0,
|
||||
totalPRsCreated: 0,
|
||||
totalAgentTimeMs: 0,
|
||||
firstEventAt: null
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('the accounts snapshot stays opaque', () => {
|
||||
it('takes every shape decodeAccountsSnapshot judges', () => {
|
||||
expect(refuses(homeHostAccountsSchema, null)).toBe(false)
|
||||
expect(refuses(homeHostAccountsSchema, { claude: { accounts: [] } })).toBe(false)
|
||||
})
|
||||
})
|
||||
@@ -1,17 +0,0 @@
|
||||
import { z } from 'zod'
|
||||
import { salvagedOptional } from '../../../src/shared/zod-salvage'
|
||||
|
||||
// Decode any unreadable row as absent: totalHomeStats skips it, but seating the host slot preserves the zeroed header instead of removing it.
|
||||
// Keep firstEventAt:null as the host’s explicit no-events state when computing the minimum.
|
||||
export const homeHostStatsSchema = z
|
||||
.looseObject({
|
||||
totalAgentsSpawned: salvagedOptional('totalAgentsSpawned', z.number()),
|
||||
totalPRsCreated: salvagedOptional('totalPRsCreated', z.number()),
|
||||
totalAgentTimeMs: salvagedOptional('totalAgentTimeMs', z.number()),
|
||||
firstEventAt: salvagedOptional('firstEventAt', z.number().nullable())
|
||||
})
|
||||
.nullish()
|
||||
.catch(undefined)
|
||||
|
||||
// Forward accounts opaquely; decodeAccountsSnapshot already validates them at the call site and is shared with the account pane and reset sheet.
|
||||
export const homeHostAccountsSchema = z.unknown()
|
||||
@@ -0,0 +1,60 @@
|
||||
import { StyleSheet } from 'react-native'
|
||||
import { colors, radii, spacing } from '../theme/mobile-theme'
|
||||
import { themedStyleSheet } from '../theme/themed-styles'
|
||||
|
||||
// Text starts after the status column, so separators inset to it.
|
||||
const STATUS_COLUMN_WIDTH = 24
|
||||
const ROW_LEADING_PADDING = spacing.lg
|
||||
|
||||
/** Home's grouped lists: one inset group per section, a status column, a title and a wrapping detail line. */
|
||||
export const homeListStyles = themedStyleSheet(() => ({
|
||||
sectionHeading: {
|
||||
fontSize: 15,
|
||||
fontWeight: '600',
|
||||
color: colors.textPrimary,
|
||||
paddingHorizontal: spacing.xs,
|
||||
marginTop: 26,
|
||||
marginBottom: 9
|
||||
},
|
||||
firstSectionHeading: { marginTop: spacing.sm },
|
||||
group: { backgroundColor: colors.bgPanel, borderRadius: radii.surface, overflow: 'hidden' },
|
||||
row: { flexDirection: 'row', alignItems: 'flex-start', paddingLeft: ROW_LEADING_PADDING },
|
||||
rowPressed: { backgroundColor: colors.bgRaised },
|
||||
rowDisabled: { opacity: 0.45 },
|
||||
separator: {
|
||||
height: StyleSheet.hairlineWidth,
|
||||
backgroundColor: colors.borderSubtle,
|
||||
marginLeft: ROW_LEADING_PADDING + STATUS_COLUMN_WIDTH
|
||||
},
|
||||
fullSeparator: { marginLeft: ROW_LEADING_PADDING },
|
||||
status: {
|
||||
width: STATUS_COLUMN_WIDTH,
|
||||
height: 22,
|
||||
marginTop: 12,
|
||||
justifyContent: 'center'
|
||||
},
|
||||
// AgentSpinner's box, so connection dots and agent dots share one column.
|
||||
statusDotBox: { width: 12, alignItems: 'center' },
|
||||
text: { flex: 1, minWidth: 0, paddingVertical: 12 },
|
||||
title: { fontSize: 17, lineHeight: 22, color: colors.textPrimary },
|
||||
subtitle: { fontSize: 15, lineHeight: 20, color: colors.textSecondary, marginTop: 1 },
|
||||
detail: { fontSize: 15, lineHeight: 20, color: colors.textMuted, marginTop: 3 },
|
||||
trailing: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: 6,
|
||||
height: 22,
|
||||
marginTop: 12,
|
||||
paddingLeft: 10,
|
||||
paddingRight: 14
|
||||
},
|
||||
time: { fontSize: 15, color: colors.textMuted, fontVariant: ['tabular-nums'] },
|
||||
actionRow: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: 10,
|
||||
minHeight: 52,
|
||||
paddingHorizontal: ROW_LEADING_PADDING
|
||||
},
|
||||
actionLabel: { fontSize: 17, color: colors.accentBlue }
|
||||
}))
|
||||
@@ -1,25 +0,0 @@
|
||||
import { bindDeferredRpcOperation, defineRpcOperation } from '../transport/rpc-operation'
|
||||
import { rpcResultVariant } from '../transport/rpc-operation-result-reader'
|
||||
import { homeHostAccountsSchema, homeHostStatsSchema } from './home-host-reply-schema'
|
||||
|
||||
// Decorative counts skip refusals to retain the displayed snapshot; accepted unreadable rows decode as absent to preserve the host slot. Reuse Tasks operations for glab/Linear probes.
|
||||
export const homeHostStatsRead = bindDeferredRpcOperation(
|
||||
defineRpcOperation({
|
||||
name: 'stats.home-summary-or-skip',
|
||||
method: 'stats.summary',
|
||||
acceptance: 'success-result-or-skip',
|
||||
barrier: 'after-caller-barrier',
|
||||
read: rpcResultVariant('home-stats-summary', homeHostStatsSchema)
|
||||
})
|
||||
)
|
||||
|
||||
// Skip refused accounts to retain the snapshot; decodeAccountsSnapshot validates the forwarded payload at the call site.
|
||||
export const homeHostAccountsRead = bindDeferredRpcOperation(
|
||||
defineRpcOperation({
|
||||
name: 'accounts.home-snapshot-or-skip',
|
||||
method: 'accounts.list',
|
||||
acceptance: 'success-result-or-skip',
|
||||
barrier: 'after-caller-barrier',
|
||||
read: rpcResultVariant('home-accounts-snapshot', homeHostAccountsSchema)
|
||||
})
|
||||
)
|
||||
@@ -1,104 +0,0 @@
|
||||
import { settingsRead } from '../transport/settings-read-operations'
|
||||
import { decodeAccountsSnapshot, type AccountsSnapshot } from '../components/AccountUsage'
|
||||
import type { HomeStatsRow } from '../stats/home-stats-total'
|
||||
import { taskLinearStatusRead, taskPreflightRead } from '../tasks/mobile-task-runtime-operations'
|
||||
import {
|
||||
filterAvailableTaskProviders,
|
||||
normalizeVisibleTaskProviders,
|
||||
type TaskProvider
|
||||
} from '../../../src/shared/task-providers'
|
||||
import type { RpcClient } from '../transport/rpc-client'
|
||||
import { homeHostAccountsRead, homeHostStatsRead } from './mobile-home-host-operations'
|
||||
|
||||
type HomeTaskSettings = {
|
||||
visibleTaskProviders?: unknown
|
||||
}
|
||||
|
||||
export type HomeStatsSetter = (
|
||||
updater: (previous: Record<string, HomeStatsRow>) => Record<string, HomeStatsRow>
|
||||
) => void
|
||||
|
||||
export type HomeAccountsSetter = (
|
||||
updater: (previous: Record<string, AccountsSnapshot>) => Record<string, AccountsSnapshot>
|
||||
) => void
|
||||
|
||||
export type HomeTaskProvidersSetter = (
|
||||
updater: (previous: Record<string, TaskProvider[]>) => Record<string, TaskProvider[]>
|
||||
) => void
|
||||
|
||||
export function fetchMobileHomeStats(
|
||||
client: RpcClient,
|
||||
hostId: string,
|
||||
setStats: HomeStatsSetter,
|
||||
disposed: () => boolean
|
||||
): void {
|
||||
homeHostStatsRead
|
||||
.requestSingleFlight(client, hostId)
|
||||
.then((reply) => {
|
||||
const summary = homeHostStatsRead.interpret(reply)
|
||||
if (!disposed() && summary.accepted) {
|
||||
setStats((previous) => ({ ...previous, [hostId]: summary.value }))
|
||||
}
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
export function fetchMobileHomeAccounts(
|
||||
client: RpcClient,
|
||||
hostId: string,
|
||||
setSnapshots: HomeAccountsSetter,
|
||||
disposed: () => boolean
|
||||
): void {
|
||||
homeHostAccountsRead
|
||||
.requestSingleFlight(client, hostId)
|
||||
.then((reply) => {
|
||||
const accounts = homeHostAccountsRead.interpret(reply)
|
||||
if (!disposed() && accounts.accepted) {
|
||||
const snapshot = decodeAccountsSnapshot(accounts.value)
|
||||
setSnapshots((previous) => ({ ...previous, [hostId]: snapshot }))
|
||||
}
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
export function fetchMobileHomeTaskProviders(
|
||||
client: RpcClient,
|
||||
hostId: string,
|
||||
setProviders: HomeTaskProvidersSetter,
|
||||
disposed: () => boolean
|
||||
): void {
|
||||
Promise.all([
|
||||
settingsRead.requestSingleFlight(client, hostId),
|
||||
taskPreflightRead.requestSingleFlight(client, hostId),
|
||||
taskLinearStatusRead.requestSingleFlight(client, hostId)
|
||||
])
|
||||
.then(([settingsResponse, preflightResponse, linearResponse]) => {
|
||||
if (disposed()) {
|
||||
return
|
||||
}
|
||||
const settingsResult = settingsRead.interpret(settingsResponse)
|
||||
const settings = settingsResult.accepted
|
||||
? // oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: Preserve the established response shape at this boundary.
|
||||
((settingsResult.value ?? {}) as HomeTaskSettings)
|
||||
: {}
|
||||
const preflightResult = taskPreflightRead.interpret(preflightResponse)
|
||||
const preflight = preflightResult.accepted ? preflightResult.value : null
|
||||
const linearResult = taskLinearStatusRead.interpret(linearResponse)
|
||||
const linear = linearResult.accepted ? linearResult.value : null
|
||||
const providers = filterAvailableTaskProviders(
|
||||
normalizeVisibleTaskProviders(settings.visibleTaskProviders),
|
||||
{
|
||||
gitlabInstalled: preflight?.glab?.installed === true,
|
||||
linearConnected: linear?.connected === true
|
||||
}
|
||||
)
|
||||
setProviders((previous) => ({ ...previous, [hostId]: providers }))
|
||||
})
|
||||
.catch(() => {
|
||||
if (!disposed()) {
|
||||
setProviders((previous) =>
|
||||
previous[hostId] ? previous : { ...previous, [hostId]: ['github'] }
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,56 +1,53 @@
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage'
|
||||
import { useRouter, useFocusEffect } from 'expo-router'
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import type { AccountsSnapshot } from '../components/AccountUsage'
|
||||
import { hasRenderableUsage } from '../components/AccountUsage'
|
||||
import { loadHomeSnapshot, saveHomeSnapshot } from '../cache/home-snapshot-cache'
|
||||
import { getCachedWorktrees, setCachedWorktrees } from '../cache/worktree-cache'
|
||||
import {
|
||||
loadMobileOnboardingSteps,
|
||||
mobileOnboardingDestination
|
||||
} from '../onboarding/mobile-onboarding-plan'
|
||||
import { totalHomeStats, type HomeStatsRow } from '../stats/home-stats-total'
|
||||
import type { TaskProvider } from '../../../src/shared/task-providers'
|
||||
import {
|
||||
selectConnectableHostProfiles,
|
||||
sortHostsByLastConnected
|
||||
} from '../transport/host-catalog-selection'
|
||||
import { loadHostCatalog } from '../transport/host-store'
|
||||
import type { HostCatalogEntry, HostProfile } from '../transport/types'
|
||||
import type { HostCatalogEntry } from '../transport/types'
|
||||
import { fetchHomeHostWorktreeInfo } from '../worktree/home-host-worktree-fetch'
|
||||
import type { HomeWorktreeSummary, HostWorktreeInfo } from '../worktree/home-worktree-info'
|
||||
import {
|
||||
withoutHomeAgentStates,
|
||||
type HomeWorktreeSummary,
|
||||
type HostWorktreeInfo
|
||||
} from '../worktree/home-worktree-info'
|
||||
import {
|
||||
LAST_VISITED_WORKTREE_STORAGE_KEY,
|
||||
readLastVisitedWorktreeRecord
|
||||
} from '../worktree/last-visited-worktree-repo'
|
||||
import { selectHomeJumpBackInRows } from '../worktree/home-jump-back-in'
|
||||
import { selectHomeResumeCard } from '../worktree/home-resume-card'
|
||||
import {
|
||||
fetchMobileHomeAccounts,
|
||||
fetchMobileHomeStats,
|
||||
fetchMobileHomeTaskProviders
|
||||
} from './mobile-home-host-requests'
|
||||
import { projectHomeHostConnections } from './home-host-connection-projection'
|
||||
import { useMobileHomeHostConnections } from './use-mobile-home-host-connections'
|
||||
|
||||
export function useMobileHomeData() {
|
||||
const router = useRouter()
|
||||
const [hostCatalog, setHostCatalog] = useState<HostCatalogEntry[]>([])
|
||||
const [statsByHost, setStatsByHost] = useState<Record<string, HomeStatsRow>>({})
|
||||
// Until the catalog read lands, an empty list says nothing; a paired user must not see first-run.
|
||||
const [hostCatalogLoaded, setHostCatalogLoaded] = useState(false)
|
||||
const [worktreeInfo, setWorktreeInfo] = useState<Record<string, HostWorktreeInfo>>({})
|
||||
const [accountsByHost, setAccountsByHost] = useState<Record<string, AccountsSnapshot>>({})
|
||||
const [taskProvidersByHost, setTaskProvidersByHost] = useState<Record<string, TaskProvider[]>>({})
|
||||
const [lastVisited, setLastVisited] = useState<{ hostId: string; worktreeId: string } | null>(
|
||||
null
|
||||
)
|
||||
const onboardingCheckedRef = useRef(false)
|
||||
const hydratedRef = useRef(false)
|
||||
const focusedRef = useRef(false)
|
||||
const isHomeFocused = useCallback(() => focusedRef.current, [])
|
||||
const hosts = useMemo(() => selectConnectableHostProfiles(hostCatalog), [hostCatalog])
|
||||
const connections = useMobileHomeHostConnections(hosts, hostCatalog, {
|
||||
setStats: setStatsByHost,
|
||||
const connections = useMobileHomeHostConnections(
|
||||
hosts,
|
||||
hostCatalog,
|
||||
setWorktreeInfo,
|
||||
setAccounts: setAccountsByHost,
|
||||
setTaskProviders: setTaskProvidersByHost
|
||||
})
|
||||
isHomeFocused
|
||||
)
|
||||
const allClientsRef = useRef(connections.allClients)
|
||||
|
||||
useEffect(() => {
|
||||
@@ -64,10 +61,14 @@ export function useMobileHomeData() {
|
||||
return
|
||||
}
|
||||
setWorktreeInfo((previous) =>
|
||||
Object.keys(previous).length > 0 ? previous : snapshot.worktreeInfo
|
||||
)
|
||||
setAccountsByHost((previous) =>
|
||||
Object.keys(previous).length > 0 ? previous : snapshot.accountsByHost
|
||||
Object.keys(previous).length > 0
|
||||
? previous
|
||||
: Object.fromEntries(
|
||||
Object.entries(snapshot.worktreeInfo).map(([hostId, info]) => [
|
||||
hostId,
|
||||
withoutHomeAgentStates(info)
|
||||
])
|
||||
)
|
||||
)
|
||||
for (const [hostId, info] of Object.entries(snapshot.worktreeInfo)) {
|
||||
if (info.lastActiveWorktree) {
|
||||
@@ -81,10 +82,10 @@ export function useMobileHomeData() {
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (Object.keys(worktreeInfo).length > 0 || Object.keys(accountsByHost).length > 0) {
|
||||
saveHomeSnapshot({ worktreeInfo, accountsByHost, savedAt: Date.now() })
|
||||
if (Object.keys(worktreeInfo).length > 0) {
|
||||
saveHomeSnapshot({ worktreeInfo, savedAt: Date.now() })
|
||||
}
|
||||
}, [worktreeInfo, accountsByHost])
|
||||
}, [worktreeInfo])
|
||||
|
||||
useEffect(() => {
|
||||
allClientsRef.current = connections.allClients
|
||||
@@ -93,20 +94,28 @@ export function useMobileHomeData() {
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
let stale = false
|
||||
void loadHostCatalog().then(async (catalog) => {
|
||||
if (stale) {
|
||||
return
|
||||
}
|
||||
setHostCatalog(catalog)
|
||||
if (catalog.length === 0 || onboardingCheckedRef.current) {
|
||||
return
|
||||
}
|
||||
onboardingCheckedRef.current = true
|
||||
const steps = await loadMobileOnboardingSteps()
|
||||
if (!stale && steps.length > 0) {
|
||||
router.replace(mobileOnboardingDestination(steps))
|
||||
}
|
||||
})
|
||||
focusedRef.current = true
|
||||
void loadHostCatalog()
|
||||
// A failed read proves nothing about the desktops; keep the ones already shown.
|
||||
.catch((): HostCatalogEntry[] | null => null)
|
||||
.then(async (catalog) => {
|
||||
if (stale) {
|
||||
return
|
||||
}
|
||||
setHostCatalogLoaded(true)
|
||||
if (!catalog) {
|
||||
return
|
||||
}
|
||||
setHostCatalog(catalog)
|
||||
if (catalog.length === 0 || onboardingCheckedRef.current) {
|
||||
return
|
||||
}
|
||||
onboardingCheckedRef.current = true
|
||||
const steps = await loadMobileOnboardingSteps()
|
||||
if (!stale && steps.length > 0) {
|
||||
router.replace(mobileOnboardingDestination(steps))
|
||||
}
|
||||
})
|
||||
void AsyncStorage.getItem(LAST_VISITED_WORKTREE_STORAGE_KEY).then((raw) => {
|
||||
if (!stale) {
|
||||
setLastVisited(readLastVisitedWorktreeRecord(raw))
|
||||
@@ -114,27 +123,18 @@ export function useMobileHomeData() {
|
||||
})
|
||||
for (const entry of allClientsRef.current) {
|
||||
if (entry.client.getState() === 'connected') {
|
||||
fetchMobileHomeStats(entry.client, entry.hostId, setStatsByHost, () => stale)
|
||||
void fetchHomeHostWorktreeInfo(entry.client, entry.hostId, setWorktreeInfo, () => stale)
|
||||
fetchMobileHomeAccounts(entry.client, entry.hostId, setAccountsByHost, () => stale)
|
||||
fetchMobileHomeTaskProviders(
|
||||
entry.client,
|
||||
entry.hostId,
|
||||
setTaskProvidersByHost,
|
||||
() => stale
|
||||
)
|
||||
}
|
||||
}
|
||||
return () => {
|
||||
stale = true
|
||||
focusedRef.current = false
|
||||
}
|
||||
}, [router])
|
||||
)
|
||||
|
||||
const sortedHosts = useMemo(() => sortHostsByLastConnected(hosts), [hosts])
|
||||
const sortedHostCatalog = useMemo(() => sortHostsByLastConnected(hostCatalog), [hostCatalog])
|
||||
const hostIds = useMemo(() => hosts.map((host) => host.id), [hosts])
|
||||
const stats = useMemo(() => totalHomeStats(statsByHost, hostIds), [statsByHost, hostIds])
|
||||
const resumeCard = useMemo(
|
||||
() =>
|
||||
selectHomeResumeCard({
|
||||
@@ -146,20 +146,16 @@ export function useMobileHomeData() {
|
||||
}),
|
||||
[sortedHosts, connections.hostStates, worktreeInfo, lastVisited]
|
||||
)
|
||||
const accountsHosts = useMemo(() => {
|
||||
const items: { host: HostProfile; snapshot: AccountsSnapshot }[] = []
|
||||
for (const host of sortedHosts) {
|
||||
const snapshot = accountsByHost[host.id]
|
||||
if (
|
||||
connections.hostStates[host.id] === 'connected' &&
|
||||
snapshot &&
|
||||
(hasRenderableUsage(snapshot, 'claude') || hasRenderableUsage(snapshot, 'codex'))
|
||||
) {
|
||||
items.push({ host, snapshot })
|
||||
}
|
||||
}
|
||||
return items
|
||||
}, [sortedHosts, connections.hostStates, accountsByHost])
|
||||
const jumpBackInRows = useMemo(
|
||||
() =>
|
||||
selectHomeJumpBackInRows({
|
||||
hosts: sortedHosts,
|
||||
hostStates: connections.hostStates,
|
||||
worktreeInfo,
|
||||
resumeCard
|
||||
}),
|
||||
[sortedHosts, connections.hostStates, worktreeInfo, resumeCard]
|
||||
)
|
||||
const connectedHosts = useMemo(
|
||||
() => sortedHosts.filter((host) => connections.hostStates[host.id] === 'connected'),
|
||||
[sortedHosts, connections.hostStates]
|
||||
@@ -171,16 +167,14 @@ export function useMobileHomeData() {
|
||||
|
||||
return {
|
||||
...connections,
|
||||
accountsHosts,
|
||||
connectedHosts,
|
||||
hostCatalog,
|
||||
hostCatalogLoaded,
|
||||
hostConnections,
|
||||
resumeCard,
|
||||
jumpBackInRows,
|
||||
router,
|
||||
setHostCatalog,
|
||||
sortedHostCatalog,
|
||||
stats,
|
||||
taskProvidersByHost,
|
||||
worktreeInfo
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { decodeAccountsSnapshot } from '../components/AccountUsage'
|
||||
import { subscribeToDesktopNotifications } from '../notifications/mobile-notifications'
|
||||
import { usePrimeHosts } from '../transport/host-client-hooks'
|
||||
import { createHostConnectRefetchGate } from '../transport/host-connect-refetch-gate'
|
||||
@@ -11,87 +10,104 @@ import {
|
||||
fetchHomeHostWorktreeInfo,
|
||||
type HostWorktreeInfoSetter
|
||||
} from '../worktree/home-host-worktree-fetch'
|
||||
import { withoutHomeAgentStates } from '../worktree/home-worktree-info'
|
||||
import {
|
||||
readMobileHomeLastConnected,
|
||||
readMobileHomeReconnectAttempts,
|
||||
reconcileMobileHomeHostStates
|
||||
} from './mobile-home-connection-state'
|
||||
import {
|
||||
fetchMobileHomeStats,
|
||||
fetchMobileHomeTaskProviders,
|
||||
type HomeAccountsSetter,
|
||||
type HomeStatsSetter,
|
||||
type HomeTaskProvidersSetter
|
||||
} from './mobile-home-host-requests'
|
||||
|
||||
type Setters = {
|
||||
setStats: HomeStatsSetter
|
||||
setWorktreeInfo: HostWorktreeInfoSetter
|
||||
setAccounts: HomeAccountsSetter
|
||||
setTaskProviders: HomeTaskProvidersSetter
|
||||
}
|
||||
// Same pace as the workspace list's poll; agent status notices can arrive many times a second.
|
||||
const HOME_WORKTREE_REFRESH_MIN_INTERVAL_MS = 3000
|
||||
const WORKTREE_CHANGE_EVENTS: ReadonlySet<string> = new Set([
|
||||
'sidebarInputsChanged',
|
||||
'worktreesChanged',
|
||||
'reposChanged'
|
||||
])
|
||||
|
||||
function wireMobileHomeHostSubscriptions(
|
||||
entry: { hostId: string; client: RpcClient; state: ConnectionState },
|
||||
setters: Setters
|
||||
setWorktreeInfo: HostWorktreeInfoSetter,
|
||||
isHomeFocused: () => boolean
|
||||
): () => void {
|
||||
let unsubscribeNotifications: (() => void) | null = null
|
||||
let unsubscribeAccounts: (() => void) | null = null
|
||||
let unsubscribeClientEvents: (() => void) | null = null
|
||||
let lastRefreshAt = 0
|
||||
let trailingRefresh: ReturnType<typeof setTimeout> | null = null
|
||||
const refetchGate = createHostConnectRefetchGate()
|
||||
const refreshWorktrees = (): void => {
|
||||
lastRefreshAt = Date.now()
|
||||
void fetchHomeHostWorktreeInfo(entry.client, entry.hostId, setWorktreeInfo, () => false)
|
||||
}
|
||||
// Home stays mounted under other screens; focusing it again re-reads every desktop anyway.
|
||||
const refreshOnChange = (): void => {
|
||||
if (!isHomeFocused() || trailingRefresh) {
|
||||
return
|
||||
}
|
||||
const wait = lastRefreshAt + HOME_WORKTREE_REFRESH_MIN_INTERVAL_MS - Date.now()
|
||||
if (wait <= 0) {
|
||||
refreshWorktrees()
|
||||
return
|
||||
}
|
||||
trailingRefresh = setTimeout(() => {
|
||||
trailingRefresh = null
|
||||
if (isHomeFocused()) {
|
||||
refreshWorktrees()
|
||||
}
|
||||
}, wait)
|
||||
}
|
||||
const wireState = (state: ConnectionState): void => {
|
||||
const reconnected = refetchGate.observe(state)
|
||||
if (state === 'connected') {
|
||||
unsubscribeNotifications ??= subscribeToDesktopNotifications(entry.client, entry.hostId)
|
||||
unsubscribeAccounts ??= entry.client.subscribe('accounts.subscribe', null, (payload) => {
|
||||
if (!payload || typeof payload !== 'object') {
|
||||
return
|
||||
unsubscribeClientEvents ??= entry.client.subscribe(
|
||||
'runtime.clientEvents.subscribe',
|
||||
null,
|
||||
(payload) => {
|
||||
if (
|
||||
payload &&
|
||||
typeof payload === 'object' &&
|
||||
'type' in payload &&
|
||||
typeof payload.type === 'string' &&
|
||||
WORKTREE_CHANGE_EVENTS.has(payload.type)
|
||||
) {
|
||||
refreshOnChange()
|
||||
}
|
||||
}
|
||||
const event = payload as { type?: string; snapshot?: unknown }
|
||||
if (event.type !== 'ready' && event.type !== 'snapshot') {
|
||||
return
|
||||
}
|
||||
try {
|
||||
const snapshot = decodeAccountsSnapshot(event.snapshot)
|
||||
setters.setAccounts((previous) => ({ ...previous, [entry.hostId]: snapshot }))
|
||||
} catch {
|
||||
// Keep the last proven snapshot when a mixed-version host publishes malformed data.
|
||||
}
|
||||
})
|
||||
)
|
||||
if (reconnected) {
|
||||
fetchMobileHomeStats(entry.client, entry.hostId, setters.setStats, () => false)
|
||||
void fetchHomeHostWorktreeInfo(
|
||||
entry.client,
|
||||
entry.hostId,
|
||||
setters.setWorktreeInfo,
|
||||
() => false
|
||||
)
|
||||
fetchMobileHomeTaskProviders(
|
||||
entry.client,
|
||||
entry.hostId,
|
||||
setters.setTaskProviders,
|
||||
() => false
|
||||
)
|
||||
refreshWorktrees()
|
||||
}
|
||||
return
|
||||
}
|
||||
unsubscribeNotifications?.()
|
||||
unsubscribeNotifications = null
|
||||
unsubscribeAccounts?.()
|
||||
unsubscribeAccounts = null
|
||||
unsubscribeClientEvents?.()
|
||||
unsubscribeClientEvents = null
|
||||
// Agents keep moving while we're away; only the reconnect read may show their states again.
|
||||
setWorktreeInfo((previous) => {
|
||||
const current = previous[entry.hostId]
|
||||
const next = current && withoutHomeAgentStates(current)
|
||||
return next === current ? previous : { ...previous, [entry.hostId]: next }
|
||||
})
|
||||
}
|
||||
wireState(entry.state)
|
||||
const unsubscribeState = entry.client.onStateChange(wireState)
|
||||
return () => {
|
||||
unsubscribeState()
|
||||
unsubscribeNotifications?.()
|
||||
unsubscribeAccounts?.()
|
||||
unsubscribeClientEvents?.()
|
||||
if (trailingRefresh) {
|
||||
clearTimeout(trailingRefresh)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function useMobileHomeHostConnections(
|
||||
hosts: HostProfile[],
|
||||
hostCatalog: HostCatalogEntry[],
|
||||
setters: Setters
|
||||
setWorktreeInfo: HostWorktreeInfoSetter,
|
||||
isHomeFocused: () => boolean
|
||||
) {
|
||||
const [hostStates, setHostStates] = useState<Record<string, ConnectionState>>({})
|
||||
const [hostAttempts, setHostAttempts] = useState<Record<string, number>>({})
|
||||
@@ -104,7 +120,6 @@ export function useMobileHomeHostConnections(
|
||||
})
|
||||
const primeHosts = usePrimeHosts()
|
||||
const subscriptionsRef = useRef(new Map<string, { client: RpcClient; cleanup: () => void }>())
|
||||
const { setAccounts, setStats, setTaskProviders, setWorktreeInfo } = setters
|
||||
|
||||
useEffect(() => {
|
||||
if (hosts.length > 0) {
|
||||
@@ -126,16 +141,15 @@ export function useMobileHomeHostConnections(
|
||||
subscriptionsRef.current.delete(hostId)
|
||||
}
|
||||
}
|
||||
const activeSetters = { setAccounts, setStats, setTaskProviders, setWorktreeInfo }
|
||||
for (const entry of allClients) {
|
||||
if (!subscriptionsRef.current.has(entry.hostId)) {
|
||||
subscriptionsRef.current.set(entry.hostId, {
|
||||
client: entry.client,
|
||||
cleanup: wireMobileHomeHostSubscriptions(entry, activeSetters)
|
||||
cleanup: wireMobileHomeHostSubscriptions(entry, setWorktreeInfo, isHomeFocused)
|
||||
})
|
||||
}
|
||||
}
|
||||
}, [allClients, setAccounts, setStats, setTaskProviders, setWorktreeInfo])
|
||||
}, [allClients, isHomeFocused, setWorktreeInfo])
|
||||
|
||||
useEffect(() => {
|
||||
const subscriptions = subscriptionsRef.current
|
||||
|
||||
@@ -767,11 +767,6 @@
|
||||
"moveUp": "Nach oben verschieben",
|
||||
"reorderHint": "Mit den Aktionen „Nach oben verschieben“ und „Nach unten verschieben“ lässt sich die Reihenfolge ohne Ziehen ändern"
|
||||
},
|
||||
"homeQuickActions": {
|
||||
"newWorkspace": "Arbeitsbereich erstellen",
|
||||
"pairDesktop": "Desktop koppeln",
|
||||
"title": "Schnellaktionen"
|
||||
},
|
||||
"hostCard": {
|
||||
"hostActions": "Aktionen für {{title}}",
|
||||
"openHost": "{{title}} öffnen",
|
||||
@@ -1302,17 +1297,10 @@
|
||||
}
|
||||
},
|
||||
"home": {
|
||||
"accountUsageCards": {
|
||||
"sectionHeading": "Kontonutzung",
|
||||
"sessionWindow": "5 h",
|
||||
"systemDefault": "Systemstandard",
|
||||
"weeklyWindow": "7 Tage"
|
||||
},
|
||||
"appUpdateCard": {
|
||||
"dismissAccessibilityLabel": "Update-Hinweis für Orca {{version}} ausblenden",
|
||||
"title": "Orca {{version}} ist verfügbar",
|
||||
"updateAccessibilityLabel": "Orca {{version}} ist verfügbar, zum Aktualisieren antippen",
|
||||
"updateHint": "Zum Aktualisieren antippen"
|
||||
"updateAccessibilityLabel": "Orca {{version}} ist verfügbar, zum Aktualisieren antippen"
|
||||
},
|
||||
"emptyState": {
|
||||
"connectedDescription": "Der Desktop erscheint hier. Alles ist Ende-zu-Ende-verschlüsselt.",
|
||||
@@ -1320,26 +1308,14 @@
|
||||
"description": "Mit Orca auf dem Computer koppeln, um Agenten im Blick zu behalten, beliebige Terminals zu öffnen und die Arbeit vom Smartphone aus zu steuern.",
|
||||
"openDesktopDescription": "Einstellungen → Mobil öffnen und einen QR-Code zum Koppeln erstellen.",
|
||||
"openDesktopTitle": "Orca-Desktop-App öffnen",
|
||||
"pairDesktop": "Desktop koppeln",
|
||||
"scanCodeDescription": "Die Schaltfläche oben antippen, um den Scanner zu öffnen. Die Kamera auf den QR-Code auf dem Bildschirm richten.",
|
||||
"scanCodeTitle": "Code scannen",
|
||||
"stepsHeading": "So funktioniert’s",
|
||||
"title": "Desktop verbinden"
|
||||
},
|
||||
"listFooter": {
|
||||
"hostPickerTitle": "Aufgaben auf Host öffnen",
|
||||
"resumeHeading": "Fortsetzen",
|
||||
"tasksHeading": "Aufgaben"
|
||||
"hostPickerTitle": "Aufgaben auf Host öffnen"
|
||||
},
|
||||
"listHeader": {
|
||||
"agentTime": "Agentenzeit",
|
||||
"agentsSpawned": "Gestartete Agenten",
|
||||
"daysHours": "{{days}} T {{hours}} Std.",
|
||||
"desktopsHeading": "Desktop-Computer",
|
||||
"hoursMinutes": "{{hours}} Std. {{minutes}} Min.",
|
||||
"minutes": "{{minutes}} Min.",
|
||||
"prsCreated": "Erstellte PRs",
|
||||
"title": "Willkommen zurück"
|
||||
"desktopsHeading": "Desktop-Computer"
|
||||
},
|
||||
"screen": {
|
||||
"pairingCheckFailed": "Kopplung konnte nicht geprüft werden",
|
||||
@@ -1347,11 +1323,6 @@
|
||||
"removeHostMessage": "„{{name}}“ entfernen? Eine erneute Kopplung ist später möglich.",
|
||||
"removeHostTitle": "Host entfernen",
|
||||
"tryAgain": "Bitte erneut versuchen."
|
||||
},
|
||||
"tasksCard": {
|
||||
"noSources": "Keine Aufgabenquellen verbunden",
|
||||
"openProviderTasks": "{{provider}}-Aufgaben öffnen",
|
||||
"title": "Aufgaben"
|
||||
}
|
||||
},
|
||||
"hooks": {
|
||||
|
||||
@@ -478,40 +478,24 @@
|
||||
"appUpdateCard": {
|
||||
"title": "Orca {{version}} is available",
|
||||
"updateAccessibilityLabel": "Orca {{version}} is available, tap to update",
|
||||
"updateHint": "Tap to update",
|
||||
"dismissAccessibilityLabel": "Dismiss Orca {{version}} update"
|
||||
},
|
||||
"accountUsageCards": {
|
||||
"sectionHeading": "Account usage",
|
||||
"systemDefault": "System default",
|
||||
"sessionWindow": "5h",
|
||||
"weeklyWindow": "7d"
|
||||
"dismissAccessibilityLabel": "Dismiss Orca {{version}} update",
|
||||
"update": "Update"
|
||||
},
|
||||
"emptyState": {
|
||||
"openDesktopTitle": "Open Orca desktop",
|
||||
"openDesktopDescription": "Go to Settings → Mobile and generate a pairing QR code.",
|
||||
"scanCodeTitle": "Scan the code",
|
||||
"scanCodeDescription": "Tap the button above to open the scanner. Point at the QR code on your screen.",
|
||||
"connectedTitle": "You're connected",
|
||||
"connectedDescription": "Your desktop will appear here. Everything is encrypted end-to-end.",
|
||||
"title": "Connect your desktop",
|
||||
"description": "Pair with Orca on your computer to check on your agents, jump into any terminal, and drive work from your phone.",
|
||||
"pairDesktop": "Pair Desktop",
|
||||
"stepsHeading": "How it works"
|
||||
"pairDesktopButton": "Pair desktop",
|
||||
"scanCodeBelowDescription": "Tap Pair desktop below, then point your camera at the QR code on your screen."
|
||||
},
|
||||
"listFooter": {
|
||||
"resumeHeading": "Resume",
|
||||
"tasksHeading": "Tasks",
|
||||
"hostPickerTitle": "Open Tasks On"
|
||||
},
|
||||
"listHeader": {
|
||||
"daysHours": "{{days}}d {{hours}}h",
|
||||
"hoursMinutes": "{{hours}}h {{minutes}}m",
|
||||
"minutes": "{{minutes}}m",
|
||||
"title": "Welcome back",
|
||||
"agentsSpawned": "Agents spawned",
|
||||
"agentTime": "Agent time",
|
||||
"prsCreated": "PRs created",
|
||||
"desktopsHeading": "Desktops"
|
||||
},
|
||||
"screen": {
|
||||
@@ -521,10 +505,18 @@
|
||||
"removeHostTitle": "Remove Host",
|
||||
"removeHostMessage": "Remove \"{{name}}\"? You can re-pair later."
|
||||
},
|
||||
"tasksCard": {
|
||||
"title": "Tasks",
|
||||
"noSources": "No task sources connected",
|
||||
"openProviderTasks": "Open {{provider}} tasks"
|
||||
"bottomBar": {
|
||||
"connectFirst": "Connect a desktop to start new work.",
|
||||
"tasks": "Tasks",
|
||||
"newWorkspace": "New workspace"
|
||||
},
|
||||
"desktops": {
|
||||
"pairDesktop": "Pair a desktop"
|
||||
},
|
||||
"jumpBackIn": {
|
||||
"heading": "Jump back in",
|
||||
"lastOpened": "Last opened",
|
||||
"connectToOpen": "Connect {{desktop}} to open"
|
||||
}
|
||||
},
|
||||
"hooks": {
|
||||
@@ -1028,11 +1020,6 @@
|
||||
"addNoteOnLine": "Add note on line {{number}}",
|
||||
"editNoteOnLine": "Edit note on line {{number}}"
|
||||
},
|
||||
"homeQuickActions": {
|
||||
"title": "Quick Actions",
|
||||
"pairDesktop": "Pair Desktop",
|
||||
"newWorkspace": "New Workspace"
|
||||
},
|
||||
"hostCard": {
|
||||
"worktreeAccessibilitySummary": "{{summary}}, {{activeCount}} active",
|
||||
"pairingInvalid": "Pairing invalid",
|
||||
|
||||
@@ -809,11 +809,6 @@
|
||||
"moveUp": "Subir",
|
||||
"reorderHint": "Usa las acciones \"Subir\" y \"Bajar\" para reordenar sin arrastrar."
|
||||
},
|
||||
"homeQuickActions": {
|
||||
"newWorkspace": "Nuevo espacio de trabajo",
|
||||
"pairDesktop": "Emparejar escritorio",
|
||||
"title": "Acciones rápidas"
|
||||
},
|
||||
"hostCard": {
|
||||
"hostActions": "Acciones para {{title}}",
|
||||
"openHost": "Abrir {{title}}",
|
||||
@@ -1345,17 +1340,10 @@
|
||||
}
|
||||
},
|
||||
"home": {
|
||||
"accountUsageCards": {
|
||||
"sectionHeading": "Uso de la cuenta",
|
||||
"sessionWindow": "5h",
|
||||
"systemDefault": "Predeterminado del sistema",
|
||||
"weeklyWindow": "7d"
|
||||
},
|
||||
"appUpdateCard": {
|
||||
"dismissAccessibilityLabel": "Descartar la actualización de Orca {{version}}",
|
||||
"title": "Orca {{version}} está disponible",
|
||||
"updateAccessibilityLabel": "Orca {{version}} está disponible, toca para actualizar",
|
||||
"updateHint": "Toca para actualizar"
|
||||
"updateAccessibilityLabel": "Orca {{version}} está disponible, toca para actualizar"
|
||||
},
|
||||
"emptyState": {
|
||||
"connectedDescription": "Tu escritorio aparecerá aquí. Todo está cifrado de extremo a extremo.",
|
||||
@@ -1363,26 +1351,14 @@
|
||||
"description": "Empareja tu teléfono con Orca en tu ordenador para supervisar a tus agentes, acceder a cualquier terminal y gestionar el trabajo desde el móvil.",
|
||||
"openDesktopDescription": "Ve a Ajustes → Móvil y genera un código QR de emparejamiento.",
|
||||
"openDesktopTitle": "Abre Orca en el escritorio",
|
||||
"pairDesktop": "Emparejar escritorio",
|
||||
"scanCodeDescription": "Toca el botón de arriba para abrir el escáner. Apunta al código QR de tu pantalla.",
|
||||
"scanCodeTitle": "Escanea el código",
|
||||
"stepsHeading": "Cómo funciona",
|
||||
"title": "Conecta tu escritorio"
|
||||
},
|
||||
"listFooter": {
|
||||
"hostPickerTitle": "Abrir tareas en",
|
||||
"resumeHeading": "Reanudar",
|
||||
"tasksHeading": "Tareas"
|
||||
"hostPickerTitle": "Abrir tareas en"
|
||||
},
|
||||
"listHeader": {
|
||||
"agentTime": "Tiempo de agentes",
|
||||
"agentsSpawned": "Agentes iniciados",
|
||||
"daysHours": "{{days}} d {{hours}} h",
|
||||
"desktopsHeading": "Escritorios",
|
||||
"hoursMinutes": "{{hours}} h {{minutes}} min",
|
||||
"minutes": "{{minutes}} min",
|
||||
"prsCreated": "PRs creados",
|
||||
"title": "Bienvenido de nuevo"
|
||||
"desktopsHeading": "Escritorios"
|
||||
},
|
||||
"screen": {
|
||||
"pairingCheckFailed": "No se pudo comprobar el emparejamiento",
|
||||
@@ -1390,11 +1366,6 @@
|
||||
"removeHostMessage": "¿Eliminar \"{{name}}\"? Puedes volver a emparejarlo más tarde.",
|
||||
"removeHostTitle": "Eliminar host",
|
||||
"tryAgain": "Por favor, vuelve a intentarlo."
|
||||
},
|
||||
"tasksCard": {
|
||||
"noSources": "No hay fuentes de tareas conectadas",
|
||||
"openProviderTasks": "Abrir tareas de {{provider}}",
|
||||
"title": "Tareas"
|
||||
}
|
||||
},
|
||||
"hooks": {
|
||||
|
||||
@@ -809,11 +809,6 @@
|
||||
"moveUp": "Monter",
|
||||
"reorderHint": "Utilisez les actions « Monter » et « Descendre » pour réordonner sans faire glisser"
|
||||
},
|
||||
"homeQuickActions": {
|
||||
"newWorkspace": "Nouvel espace de travail",
|
||||
"pairDesktop": "Appairer l'ordinateur",
|
||||
"title": "Actions rapides"
|
||||
},
|
||||
"hostCard": {
|
||||
"hostActions": "Actions pour {{title}}",
|
||||
"openHost": "Ouvrir {{title}}",
|
||||
@@ -1345,17 +1340,10 @@
|
||||
}
|
||||
},
|
||||
"home": {
|
||||
"accountUsageCards": {
|
||||
"sectionHeading": "Utilisation du compte",
|
||||
"sessionWindow": "5 h",
|
||||
"systemDefault": "Valeur par défaut du système",
|
||||
"weeklyWindow": "7 j"
|
||||
},
|
||||
"appUpdateCard": {
|
||||
"dismissAccessibilityLabel": "Ignorer la mise à jour Orca {{version}}",
|
||||
"title": "Orca {{version}} est disponible",
|
||||
"updateAccessibilityLabel": "Orca {{version}} est disponible, touchez pour mettre à jour",
|
||||
"updateHint": "Touchez pour mettre à jour"
|
||||
"updateAccessibilityLabel": "Orca {{version}} est disponible, touchez pour mettre à jour"
|
||||
},
|
||||
"emptyState": {
|
||||
"connectedDescription": "Votre ordinateur apparaîtra ici. Tout est chiffré de bout en bout.",
|
||||
@@ -1363,26 +1351,14 @@
|
||||
"description": "Appairez votre téléphone à Orca sur votre ordinateur pour suivre vos agents, accéder à n'importe quel terminal et piloter votre travail depuis votre téléphone.",
|
||||
"openDesktopDescription": "Allez dans Paramètres → Mobile et générez un QR code d'appairage.",
|
||||
"openDesktopTitle": "Ouvrir Orca sur l'ordinateur",
|
||||
"pairDesktop": "Appairer l'ordinateur",
|
||||
"scanCodeDescription": "Touchez le bouton ci-dessus pour ouvrir le scanner. Visez le QR code affiché à l'écran.",
|
||||
"scanCodeTitle": "Scanner le code",
|
||||
"stepsHeading": "Fonctionnement",
|
||||
"title": "Connectez votre ordinateur"
|
||||
},
|
||||
"listFooter": {
|
||||
"hostPickerTitle": "Ouvrir les tâches sur",
|
||||
"resumeHeading": "Reprendre",
|
||||
"tasksHeading": "Tâches"
|
||||
"hostPickerTitle": "Ouvrir les tâches sur"
|
||||
},
|
||||
"listHeader": {
|
||||
"agentTime": "Temps agent",
|
||||
"agentsSpawned": "Agents lancés",
|
||||
"daysHours": "{{days}} j {{hours}} h",
|
||||
"desktopsHeading": "Ordinateurs",
|
||||
"hoursMinutes": "{{hours}} h {{minutes}} min",
|
||||
"minutes": "{{minutes}} min",
|
||||
"prsCreated": "PRs créées",
|
||||
"title": "Bon retour"
|
||||
"desktopsHeading": "Ordinateurs"
|
||||
},
|
||||
"screen": {
|
||||
"pairingCheckFailed": "Impossible de vérifier l'appairage",
|
||||
@@ -1390,11 +1366,6 @@
|
||||
"removeHostMessage": "Supprimer « {{name}} » ? Vous pourrez l'appairer à nouveau plus tard.",
|
||||
"removeHostTitle": "Supprimer l'hôte",
|
||||
"tryAgain": "Veuillez réessayer."
|
||||
},
|
||||
"tasksCard": {
|
||||
"noSources": "Aucune source de tâches connectée",
|
||||
"openProviderTasks": "Ouvrir les tâches de {{provider}}",
|
||||
"title": "Tâches"
|
||||
}
|
||||
},
|
||||
"hooks": {
|
||||
|
||||
@@ -763,11 +763,6 @@
|
||||
"moveUp": "上に移動",
|
||||
"reorderHint": "「上に移動」「下に移動」の操作で、ドラッグせずに並べ替えられます。"
|
||||
},
|
||||
"homeQuickActions": {
|
||||
"newWorkspace": "新規ワークスペース",
|
||||
"pairDesktop": "デスクトップのペアリング",
|
||||
"title": "クイック操作"
|
||||
},
|
||||
"hostCard": {
|
||||
"hostActions": "「{{title}}」の操作",
|
||||
"openHost": "「{{title}}」を開く",
|
||||
@@ -1298,17 +1293,10 @@
|
||||
}
|
||||
},
|
||||
"home": {
|
||||
"accountUsageCards": {
|
||||
"sectionHeading": "アカウントの使用状況",
|
||||
"sessionWindow": "5時間",
|
||||
"systemDefault": "システムのデフォルト",
|
||||
"weeklyWindow": "7日"
|
||||
},
|
||||
"appUpdateCard": {
|
||||
"dismissAccessibilityLabel": "Orca {{version}} の更新通知を閉じる",
|
||||
"title": "Orca {{version}} が利用可能です",
|
||||
"updateAccessibilityLabel": "Orca {{version}} が利用可能です。タップして更新してください。",
|
||||
"updateHint": "タップして更新"
|
||||
"updateAccessibilityLabel": "Orca {{version}} が利用可能です。タップして更新してください。"
|
||||
},
|
||||
"emptyState": {
|
||||
"connectedDescription": "デスクトップがここに表示されます。すべての通信はエンドツーエンドで暗号化されます。",
|
||||
@@ -1316,26 +1304,14 @@
|
||||
"description": "コンピュータ上の Orca とペアリングすると、スマートフォンから Agent の状況を確認したり、任意のターミナルを操作したりして、作業を進められます。",
|
||||
"openDesktopDescription": "「設定」→「モバイル」を開き、ペアリング用の QR コードを生成してください。",
|
||||
"openDesktopTitle": "デスクトップで Orca を開く",
|
||||
"pairDesktop": "デスクトップのペアリング",
|
||||
"scanCodeDescription": "上のボタンをタップしてスキャナーを開いてください。画面の QR コードにカメラを向けてください。",
|
||||
"scanCodeTitle": "コードをスキャン",
|
||||
"stepsHeading": "使い方",
|
||||
"title": "デスクトップに接続"
|
||||
},
|
||||
"listFooter": {
|
||||
"hostPickerTitle": "タスクを開くホスト",
|
||||
"resumeHeading": "再開",
|
||||
"tasksHeading": "タスク"
|
||||
"hostPickerTitle": "タスクを開くホスト"
|
||||
},
|
||||
"listHeader": {
|
||||
"agentTime": "Agent 時間",
|
||||
"agentsSpawned": "Agent が生成されました",
|
||||
"daysHours": "{{days}}日 {{hours}}時間",
|
||||
"desktopsHeading": "デスクトップ",
|
||||
"hoursMinutes": "{{hours}}時間 {{minutes}}分",
|
||||
"minutes": "{{minutes}}分",
|
||||
"prsCreated": "作成された PR",
|
||||
"title": "おかえり"
|
||||
"desktopsHeading": "デスクトップ"
|
||||
},
|
||||
"screen": {
|
||||
"pairingCheckFailed": "ペアリングを確認できませんでした",
|
||||
@@ -1343,11 +1319,6 @@
|
||||
"removeHostMessage": "「{{name}}」を削除しますか?後で再度ペアリングできます。",
|
||||
"removeHostTitle": "ホストを削除",
|
||||
"tryAgain": "再試行してください。"
|
||||
},
|
||||
"tasksCard": {
|
||||
"noSources": "タスクの連携先が接続されていません",
|
||||
"openProviderTasks": "{{provider}} のタスクを開く",
|
||||
"title": "タスク"
|
||||
}
|
||||
},
|
||||
"hooks": {
|
||||
|
||||
@@ -763,11 +763,6 @@
|
||||
"moveUp": "위로 이동",
|
||||
"reorderHint": "드래그하지 않고 순서를 바꾸려면 '위로 이동'과 '아래로 이동' 동작을 사용하세요."
|
||||
},
|
||||
"homeQuickActions": {
|
||||
"newWorkspace": "새 워크스페이스",
|
||||
"pairDesktop": "데스크탑 페어링",
|
||||
"title": "빠른 작업"
|
||||
},
|
||||
"hostCard": {
|
||||
"hostActions": "{{title}} 관련 작업",
|
||||
"openHost": "{{title}} 열기",
|
||||
@@ -1298,17 +1293,10 @@
|
||||
}
|
||||
},
|
||||
"home": {
|
||||
"accountUsageCards": {
|
||||
"sectionHeading": "계정 사용량",
|
||||
"sessionWindow": "5시간",
|
||||
"systemDefault": "시스템 기본값",
|
||||
"weeklyWindow": "7일"
|
||||
},
|
||||
"appUpdateCard": {
|
||||
"dismissAccessibilityLabel": "Orca {{version}} 업데이트 알림 닫기",
|
||||
"title": "Orca {{version}} 업데이트 가능",
|
||||
"updateAccessibilityLabel": "Orca {{version}} 업데이트가 있습니다. 탭하여 업데이트하세요.",
|
||||
"updateHint": "탭하여 업데이트하세요"
|
||||
"updateAccessibilityLabel": "Orca {{version}} 업데이트가 있습니다. 탭하여 업데이트하세요."
|
||||
},
|
||||
"emptyState": {
|
||||
"connectedDescription": "데스크톱이 여기에 표시됩니다. 모든 데이터는 종단 간 암호화로 보호됩니다.",
|
||||
@@ -1316,26 +1304,14 @@
|
||||
"description": "컴퓨터의 Orca와 페어링하여 에이전트 상태를 확인하고, 원하는 터미널에 접속하고, 휴대폰으로 작업을 진행하세요.",
|
||||
"openDesktopDescription": "설정 → 모바일에서 페어링 QR 코드를 생성하세요.",
|
||||
"openDesktopTitle": "Orca 데스크톱 앱 열기",
|
||||
"pairDesktop": "데스크탑 페어링",
|
||||
"scanCodeDescription": "위의 버튼을 탭해 스캐너를 여세요. 데스크톱 화면의 QR 코드에 카메라를 맞추세요.",
|
||||
"scanCodeTitle": "코드 스캔",
|
||||
"stepsHeading": "사용 방법",
|
||||
"title": "데스크톱을 연결하세요"
|
||||
},
|
||||
"listFooter": {
|
||||
"hostPickerTitle": "작업을 열 호스트",
|
||||
"resumeHeading": "재개",
|
||||
"tasksHeading": "작업"
|
||||
"hostPickerTitle": "작업을 열 호스트"
|
||||
},
|
||||
"listHeader": {
|
||||
"agentTime": "Agent 시간",
|
||||
"agentsSpawned": "시작된 Agents",
|
||||
"daysHours": "{{days}}일 {{hours}}시간",
|
||||
"desktopsHeading": "데스크탑",
|
||||
"hoursMinutes": "{{hours}}시간 {{minutes}}분",
|
||||
"minutes": "{{minutes}}분",
|
||||
"prsCreated": "생성된 PR",
|
||||
"title": "다시 오신 것을 환영합니다"
|
||||
"desktopsHeading": "데스크탑"
|
||||
},
|
||||
"screen": {
|
||||
"pairingCheckFailed": "페어링 확인 실패",
|
||||
@@ -1343,11 +1319,6 @@
|
||||
"removeHostMessage": "\"{{name}}\"을(를) 제거하시겠습니까? 나중에 다시 페어링할 수 있습니다.",
|
||||
"removeHostTitle": "호스트 제거",
|
||||
"tryAgain": "다시 시도하세요."
|
||||
},
|
||||
"tasksCard": {
|
||||
"noSources": "연결된 작업 소스 없음",
|
||||
"openProviderTasks": "{{provider}} 작업 열기",
|
||||
"title": "작업"
|
||||
}
|
||||
},
|
||||
"hooks": {
|
||||
|
||||
@@ -809,11 +809,6 @@
|
||||
"moveUp": "Mover para cima",
|
||||
"reorderHint": "Use as ações mover para cima e mover para baixo para reordenar sem arrastar"
|
||||
},
|
||||
"homeQuickActions": {
|
||||
"newWorkspace": "Novo Espaço de Trabalho",
|
||||
"pairDesktop": "Parear Desktop",
|
||||
"title": "Ações Rápidas"
|
||||
},
|
||||
"hostCard": {
|
||||
"hostActions": "Ações para {{title}}",
|
||||
"openHost": "Abrir {{title}}",
|
||||
@@ -1345,17 +1340,10 @@
|
||||
}
|
||||
},
|
||||
"home": {
|
||||
"accountUsageCards": {
|
||||
"sectionHeading": "Uso da conta",
|
||||
"sessionWindow": "5h",
|
||||
"systemDefault": "Padrão do sistema",
|
||||
"weeklyWindow": "7d"
|
||||
},
|
||||
"appUpdateCard": {
|
||||
"dismissAccessibilityLabel": "Descartar atualização do Orca {{version}}",
|
||||
"title": "Orca {{version}} está disponível",
|
||||
"updateAccessibilityLabel": "Orca {{version}} está disponível, toque para atualizar",
|
||||
"updateHint": "Toque para atualizar"
|
||||
"updateAccessibilityLabel": "Orca {{version}} está disponível, toque para atualizar"
|
||||
},
|
||||
"emptyState": {
|
||||
"connectedDescription": "Seu desktop aparecerá aqui. Tudo é criptografado de ponta a ponta.",
|
||||
@@ -1363,26 +1351,14 @@
|
||||
"description": "Pareie com o Orca no seu computador para acompanhar seus agentes, acessar qualquer terminal e gerenciar o trabalho pelo celular.",
|
||||
"openDesktopDescription": "Vá para Configurações → Mobile e gere um código QR de pareamento.",
|
||||
"openDesktopTitle": "Abra o Orca no desktop",
|
||||
"pairDesktop": "Parear Desktop",
|
||||
"scanCodeDescription": "Toque no botão acima para abrir o leitor. Aponte a câmera para o código QR na tela.",
|
||||
"scanCodeTitle": "Escaneie o código",
|
||||
"stepsHeading": "Como funciona",
|
||||
"title": "Conecte seu desktop"
|
||||
},
|
||||
"listFooter": {
|
||||
"hostPickerTitle": "Abrir tarefas em",
|
||||
"resumeHeading": "Retomar",
|
||||
"tasksHeading": "Tarefas"
|
||||
"hostPickerTitle": "Abrir tarefas em"
|
||||
},
|
||||
"listHeader": {
|
||||
"agentTime": "Tempo de agente",
|
||||
"agentsSpawned": "Agentes iniciados",
|
||||
"daysHours": "{{days}}d {{hours}}h",
|
||||
"desktopsHeading": "Desktops",
|
||||
"hoursMinutes": "{{hours}}h {{minutes}}min",
|
||||
"minutes": "{{minutes}}min",
|
||||
"prsCreated": "PRs criados",
|
||||
"title": "Bem-vindo de volta"
|
||||
"desktopsHeading": "Desktops"
|
||||
},
|
||||
"screen": {
|
||||
"pairingCheckFailed": "Não foi possível verificar o pareamento",
|
||||
@@ -1390,11 +1366,6 @@
|
||||
"removeHostMessage": "Remover \"{{name}}\"? Você pode parear novamente mais tarde.",
|
||||
"removeHostTitle": "Remover host",
|
||||
"tryAgain": "Tente novamente."
|
||||
},
|
||||
"tasksCard": {
|
||||
"noSources": "Nenhuma origem de tarefas conectada",
|
||||
"openProviderTasks": "Abrir tarefas do {{provider}}",
|
||||
"title": "Tarefas"
|
||||
}
|
||||
},
|
||||
"hooks": {
|
||||
|
||||
@@ -851,11 +851,6 @@
|
||||
"moveUp": "Переместить вверх",
|
||||
"reorderHint": "Чтобы изменить порядок без перетаскивания, используйте действия «Переместить вверх» и «Переместить вниз»"
|
||||
},
|
||||
"homeQuickActions": {
|
||||
"newWorkspace": "Новое рабочее пространство",
|
||||
"pairDesktop": "Подключить компьютер",
|
||||
"title": "Быстрые действия"
|
||||
},
|
||||
"hostCard": {
|
||||
"hostActions": "Действия для {{title}}",
|
||||
"openHost": "Открыть {{title}}",
|
||||
@@ -1388,17 +1383,10 @@
|
||||
}
|
||||
},
|
||||
"home": {
|
||||
"accountUsageCards": {
|
||||
"sectionHeading": "Использование учётной записи",
|
||||
"sessionWindow": "5 ч",
|
||||
"systemDefault": "Как в системе",
|
||||
"weeklyWindow": "7 дн."
|
||||
},
|
||||
"appUpdateCard": {
|
||||
"dismissAccessibilityLabel": "Скрыть уведомление об обновлении Orca {{version}}",
|
||||
"title": "Доступна Orca {{version}}",
|
||||
"updateAccessibilityLabel": "Доступна Orca {{version}}. Нажмите, чтобы обновить",
|
||||
"updateHint": "Нажмите, чтобы обновить"
|
||||
"updateAccessibilityLabel": "Доступна Orca {{version}}. Нажмите, чтобы обновить"
|
||||
},
|
||||
"emptyState": {
|
||||
"connectedDescription": "Здесь появится ваш компьютер. Все данные защищены сквозным шифрованием.",
|
||||
@@ -1406,26 +1394,14 @@
|
||||
"description": "Подключитесь к Orca на компьютере, чтобы следить за агентами, открывать любой терминал и управлять работой с телефона.",
|
||||
"openDesktopDescription": "Откройте «Настройки» → «Мобильный доступ» и создайте QR-код для сопряжения.",
|
||||
"openDesktopTitle": "Откройте Orca на компьютере",
|
||||
"pairDesktop": "Подключить компьютер",
|
||||
"scanCodeDescription": "Нажмите кнопку выше, чтобы открыть сканер. Наведите камеру на QR-код на экране компьютера.",
|
||||
"scanCodeTitle": "Отсканируйте код",
|
||||
"stepsHeading": "Как это работает",
|
||||
"title": "Подключите компьютер"
|
||||
},
|
||||
"listFooter": {
|
||||
"hostPickerTitle": "Открыть задачи на сервере",
|
||||
"resumeHeading": "Продолжить",
|
||||
"tasksHeading": "Задачи"
|
||||
"hostPickerTitle": "Открыть задачи на сервере"
|
||||
},
|
||||
"listHeader": {
|
||||
"agentTime": "Время агентов",
|
||||
"agentsSpawned": "Запущено агентов",
|
||||
"daysHours": "{{days}} дн. {{hours}} ч",
|
||||
"desktopsHeading": "Компьютеры",
|
||||
"hoursMinutes": "{{hours}} ч {{minutes}} мин",
|
||||
"minutes": "{{minutes}} мин",
|
||||
"prsCreated": "Создано PR",
|
||||
"title": "С возвращением"
|
||||
"desktopsHeading": "Компьютеры"
|
||||
},
|
||||
"screen": {
|
||||
"pairingCheckFailed": "Не удалось проверить сопряжение",
|
||||
@@ -1433,11 +1409,6 @@
|
||||
"removeHostMessage": "Удалить «{{name}}»? Позже его можно подключить снова.",
|
||||
"removeHostTitle": "Удалить сервер",
|
||||
"tryAgain": "Попробуйте ещё раз."
|
||||
},
|
||||
"tasksCard": {
|
||||
"noSources": "Источники задач не подключены",
|
||||
"openProviderTasks": "Открыть задачи {{provider}}",
|
||||
"title": "Задачи"
|
||||
}
|
||||
},
|
||||
"hooks": {
|
||||
|
||||
@@ -767,11 +767,6 @@
|
||||
"moveUp": "Yukarı taşı",
|
||||
"reorderHint": "Sürüklemeden yeniden sıralamak için yukarı taşı ve aşağı taşı eylemlerini kullanın"
|
||||
},
|
||||
"homeQuickActions": {
|
||||
"newWorkspace": "Yeni Çalışma Alanı",
|
||||
"pairDesktop": "Masaüstünü Eşleştir",
|
||||
"title": "Hızlı İşlemler"
|
||||
},
|
||||
"hostCard": {
|
||||
"hostActions": "{{title}} için işlemler",
|
||||
"openHost": "Aç: {{title}}",
|
||||
@@ -1302,17 +1297,10 @@
|
||||
}
|
||||
},
|
||||
"home": {
|
||||
"accountUsageCards": {
|
||||
"sectionHeading": "Hesap kullanımı",
|
||||
"sessionWindow": "5 sa",
|
||||
"systemDefault": "Sistem varsayılanı",
|
||||
"weeklyWindow": "7g"
|
||||
},
|
||||
"appUpdateCard": {
|
||||
"dismissAccessibilityLabel": "Orca {{version}} güncellemesini gizle",
|
||||
"title": "Orca {{version}} sürümü mevcut",
|
||||
"updateAccessibilityLabel": "Orca {{version}} sürümü mevcut, güncellemek için dokunun",
|
||||
"updateHint": "Güncellemek için dokunun"
|
||||
"updateAccessibilityLabel": "Orca {{version}} sürümü mevcut, güncellemek için dokunun"
|
||||
},
|
||||
"emptyState": {
|
||||
"connectedDescription": "Masaüstünüz burada görünecek. Her şey uçtan uca şifrelenir.",
|
||||
@@ -1320,26 +1308,14 @@
|
||||
"description": "Ajanlarınızı takip etmek, istediğiniz terminale erişmek ve işlerinizi telefonunuzdan yönetmek için bilgisayarınızdaki Orca ile eşleştirin.",
|
||||
"openDesktopDescription": "Ayarlar → Mobil bölümüne gidin ve eşleştirme için bir QR kodu oluşturun.",
|
||||
"openDesktopTitle": "Orca masaüstü uygulamasını açın",
|
||||
"pairDesktop": "Masaüstünü Eşleştir",
|
||||
"scanCodeDescription": "Tarayıcıyı açmak için yukarıdaki düğmeye dokunun. Kamerayı ekranınızdaki QR koduna doğrultun.",
|
||||
"scanCodeTitle": "Kodu tarayın",
|
||||
"stepsHeading": "Nasıl çalışır",
|
||||
"title": "Masaüstünüzü bağlayın"
|
||||
},
|
||||
"listFooter": {
|
||||
"hostPickerTitle": "Görevlerin Açılacağı Sunucu",
|
||||
"resumeHeading": "Devam et",
|
||||
"tasksHeading": "Görevler"
|
||||
"hostPickerTitle": "Görevlerin Açılacağı Sunucu"
|
||||
},
|
||||
"listHeader": {
|
||||
"agentTime": "Ajan süresi",
|
||||
"agentsSpawned": "Başlatılan ajanlar",
|
||||
"daysHours": "{{days}} g {{hours}} sa",
|
||||
"desktopsHeading": "Masaüstleri",
|
||||
"hoursMinutes": "{{hours}} sa {{minutes}} dk",
|
||||
"minutes": "{{minutes}} dk",
|
||||
"prsCreated": "Oluşturulan PR'lar",
|
||||
"title": "Tekrar hoş geldiniz"
|
||||
"desktopsHeading": "Masaüstleri"
|
||||
},
|
||||
"screen": {
|
||||
"pairingCheckFailed": "Eşleştirme kontrol edilemedi",
|
||||
@@ -1347,11 +1323,6 @@
|
||||
"removeHostMessage": "“{{name}}” kaldırılsın mı? Daha sonra yeniden eşleştirebilirsiniz.",
|
||||
"removeHostTitle": "Sunucuyu Kaldır",
|
||||
"tryAgain": "Lütfen tekrar deneyin."
|
||||
},
|
||||
"tasksCard": {
|
||||
"noSources": "Bağlı görev kaynağı yok",
|
||||
"openProviderTasks": "{{provider}} görevlerini aç",
|
||||
"title": "Görevler"
|
||||
}
|
||||
},
|
||||
"hooks": {
|
||||
|
||||
@@ -763,11 +763,6 @@
|
||||
"moveUp": "上移",
|
||||
"reorderHint": "使用「上移」和「下移」動作即可調整順序,無須拖曳"
|
||||
},
|
||||
"homeQuickActions": {
|
||||
"newWorkspace": "新工作區",
|
||||
"pairDesktop": "配對電腦",
|
||||
"title": "快速操作"
|
||||
},
|
||||
"hostCard": {
|
||||
"hostActions": "{{title}} 的操作",
|
||||
"openHost": "開啟 {{title}}",
|
||||
@@ -1298,17 +1293,10 @@
|
||||
}
|
||||
},
|
||||
"home": {
|
||||
"accountUsageCards": {
|
||||
"sectionHeading": "帳戶用量",
|
||||
"sessionWindow": "5 小時",
|
||||
"systemDefault": "系統預設",
|
||||
"weeklyWindow": "7 天"
|
||||
},
|
||||
"appUpdateCard": {
|
||||
"dismissAccessibilityLabel": "關閉 Orca {{version}} 更新提示",
|
||||
"title": "Orca {{version}} 已推出",
|
||||
"updateAccessibilityLabel": "Orca {{version}} 已推出,點一下即可更新",
|
||||
"updateHint": "點一下即可更新"
|
||||
"updateAccessibilityLabel": "Orca {{version}} 已推出,點一下即可更新"
|
||||
},
|
||||
"emptyState": {
|
||||
"connectedDescription": "您的電腦會顯示在這裡。所有資料皆採端對端加密。",
|
||||
@@ -1316,26 +1304,14 @@
|
||||
"description": "與電腦上的 Orca 配對,即可用手機查看代理程式狀態、開啟任何終端機,並推進工作。",
|
||||
"openDesktopDescription": "前往「設定」→「行動版」,產生配對用的 QR Code。",
|
||||
"openDesktopTitle": "開啟 Orca 桌面版",
|
||||
"pairDesktop": "配對電腦",
|
||||
"scanCodeDescription": "點一下上方按鈕開啟掃描器,對準電腦螢幕上的 QR Code。",
|
||||
"scanCodeTitle": "掃描 QR Code",
|
||||
"stepsHeading": "使用方式",
|
||||
"title": "連線至您的電腦"
|
||||
},
|
||||
"listFooter": {
|
||||
"hostPickerTitle": "選擇開啟任務的主機",
|
||||
"resumeHeading": "繼續",
|
||||
"tasksHeading": "任務"
|
||||
"hostPickerTitle": "選擇開啟任務的主機"
|
||||
},
|
||||
"listHeader": {
|
||||
"agentTime": "代理程式時間",
|
||||
"agentsSpawned": "已啟動代理程式",
|
||||
"daysHours": "{{days}} 天 {{hours}} 小時",
|
||||
"desktopsHeading": "電腦",
|
||||
"hoursMinutes": "{{hours}} 小時 {{minutes}} 分鐘",
|
||||
"minutes": "{{minutes}} 分鐘",
|
||||
"prsCreated": "已建立 PR",
|
||||
"title": "歡迎回來"
|
||||
"desktopsHeading": "電腦"
|
||||
},
|
||||
"screen": {
|
||||
"pairingCheckFailed": "無法檢查配對狀態",
|
||||
@@ -1343,11 +1319,6 @@
|
||||
"removeHostMessage": "要移除「{{name}}」嗎?之後可重新配對。",
|
||||
"removeHostTitle": "移除主機",
|
||||
"tryAgain": "請再試一次。"
|
||||
},
|
||||
"tasksCard": {
|
||||
"noSources": "尚未連結任務來源",
|
||||
"openProviderTasks": "開啟 {{provider}} 任務",
|
||||
"title": "任務"
|
||||
}
|
||||
},
|
||||
"hooks": {
|
||||
|
||||
@@ -763,11 +763,6 @@
|
||||
"moveUp": "上移",
|
||||
"reorderHint": "使用“上移”和“下移”操作调整顺序,无需拖动"
|
||||
},
|
||||
"homeQuickActions": {
|
||||
"newWorkspace": "新工作区",
|
||||
"pairDesktop": "配对桌面",
|
||||
"title": "快捷操作"
|
||||
},
|
||||
"hostCard": {
|
||||
"hostActions": "{{title}} 的操作",
|
||||
"openHost": "打开 {{title}}",
|
||||
@@ -1298,17 +1293,10 @@
|
||||
}
|
||||
},
|
||||
"home": {
|
||||
"accountUsageCards": {
|
||||
"sectionHeading": "账户使用情况",
|
||||
"sessionWindow": "5小时",
|
||||
"systemDefault": "系统默认",
|
||||
"weeklyWindow": "7天"
|
||||
},
|
||||
"appUpdateCard": {
|
||||
"dismissAccessibilityLabel": "关闭 Orca {{version}} 更新提示",
|
||||
"title": "Orca {{version}} 已可用",
|
||||
"updateAccessibilityLabel": "Orca {{version}} 已可用,点按更新",
|
||||
"updateHint": "点按更新"
|
||||
"updateAccessibilityLabel": "Orca {{version}} 已可用,点按更新"
|
||||
},
|
||||
"emptyState": {
|
||||
"connectedDescription": "桌面将显示在这里。所有数据均采用端到端加密。",
|
||||
@@ -1316,26 +1304,14 @@
|
||||
"description": "与电脑上的 Orca 配对,即可在手机上查看智能体、进入任意终端并推进工作。",
|
||||
"openDesktopDescription": "前往“设置” → “移动端”,生成配对二维码。",
|
||||
"openDesktopTitle": "打开 Orca 桌面应用",
|
||||
"pairDesktop": "配对桌面",
|
||||
"scanCodeDescription": "点按上方按钮打开扫码器,然后对准屏幕上的二维码。",
|
||||
"scanCodeTitle": "扫描二维码",
|
||||
"stepsHeading": "使用方法",
|
||||
"title": "连接桌面"
|
||||
},
|
||||
"listFooter": {
|
||||
"hostPickerTitle": "选择打开任务的主机",
|
||||
"resumeHeading": "继续",
|
||||
"tasksHeading": "任务"
|
||||
"hostPickerTitle": "选择打开任务的主机"
|
||||
},
|
||||
"listHeader": {
|
||||
"agentTime": "智能体时间",
|
||||
"agentsSpawned": "已启动智能体",
|
||||
"daysHours": "{{days}} 天 {{hours}} 小时",
|
||||
"desktopsHeading": "台式机",
|
||||
"hoursMinutes": "{{hours}} 小时 {{minutes}} 分钟",
|
||||
"minutes": "{{minutes}} 分钟",
|
||||
"prsCreated": "已创建 PR",
|
||||
"title": "欢迎回来"
|
||||
"desktopsHeading": "台式机"
|
||||
},
|
||||
"screen": {
|
||||
"pairingCheckFailed": "无法检查配对状态",
|
||||
@@ -1343,11 +1319,6 @@
|
||||
"removeHostMessage": "要移除“{{name}}”吗?之后可重新配对。",
|
||||
"removeHostTitle": "移除主机",
|
||||
"tryAgain": "请重试。"
|
||||
},
|
||||
"tasksCard": {
|
||||
"noSources": "未连接任务来源",
|
||||
"openProviderTasks": "打开 {{provider}} 任务",
|
||||
"title": "任务"
|
||||
}
|
||||
},
|
||||
"hooks": {
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { totalHomeStats, type HomeStatsSummary } from './home-stats-total'
|
||||
|
||||
function stats(overrides: Partial<HomeStatsSummary> = {}): HomeStatsSummary {
|
||||
return {
|
||||
totalAgentsSpawned: 10,
|
||||
totalPRsCreated: 2,
|
||||
totalAgentTimeMs: 60_000,
|
||||
firstEventAt: 1_700_000_000_000,
|
||||
...overrides
|
||||
}
|
||||
}
|
||||
|
||||
describe('totalHomeStats', () => {
|
||||
it('has nothing to show before any host answers', () => {
|
||||
expect(totalHomeStats({}, ['host-1'])).toBeNull()
|
||||
})
|
||||
|
||||
it('passes a single host through unchanged', () => {
|
||||
expect(totalHomeStats({ 'host-1': stats() }, ['host-1'])).toEqual(stats())
|
||||
})
|
||||
|
||||
it('sums every desktop instead of letting the last reply win', () => {
|
||||
const total = totalHomeStats(
|
||||
{
|
||||
'host-1': stats(),
|
||||
'host-2': stats({ totalAgentsSpawned: 5, totalPRsCreated: 1, totalAgentTimeMs: 30_000 })
|
||||
},
|
||||
['host-1', 'host-2']
|
||||
)
|
||||
|
||||
expect(total).toMatchObject({
|
||||
totalAgentsSpawned: 15,
|
||||
totalPRsCreated: 3,
|
||||
totalAgentTimeMs: 90_000
|
||||
})
|
||||
})
|
||||
|
||||
it('drops a removed desktop from the total', () => {
|
||||
const byHost = {
|
||||
'host-1': stats(),
|
||||
'host-2': stats({ totalAgentsSpawned: 5, totalPRsCreated: 1, totalAgentTimeMs: 30_000 })
|
||||
}
|
||||
|
||||
expect(totalHomeStats(byHost, ['host-1'])).toEqual(stats())
|
||||
expect(totalHomeStats(byHost, [])).toBeNull()
|
||||
})
|
||||
|
||||
it('keeps the earliest known first event', () => {
|
||||
const total = totalHomeStats(
|
||||
{
|
||||
'host-1': stats({ firstEventAt: 2_000 }),
|
||||
'host-2': stats({ firstEventAt: null }),
|
||||
'host-3': stats({ firstEventAt: 1_000 })
|
||||
},
|
||||
['host-1', 'host-2', 'host-3']
|
||||
)
|
||||
|
||||
expect(total?.firstEventAt).toBe(1_000)
|
||||
})
|
||||
|
||||
it('reports no first event when no host has one', () => {
|
||||
expect(
|
||||
totalHomeStats({ 'host-1': stats({ firstEventAt: null }) }, ['host-1'])?.firstEventAt
|
||||
).toBeNull()
|
||||
})
|
||||
|
||||
it('ignores a malformed reply instead of poisoning the header', () => {
|
||||
const total = totalHomeStats(
|
||||
{
|
||||
'host-1': stats(),
|
||||
'host-2': null as unknown as HomeStatsSummary,
|
||||
'host-3': { totalAgentsSpawned: 'lots' } as unknown as HomeStatsSummary
|
||||
},
|
||||
['host-1', 'host-2', 'host-3']
|
||||
)
|
||||
|
||||
expect(total).toEqual(stats())
|
||||
})
|
||||
})
|
||||
@@ -1,46 +0,0 @@
|
||||
export type HomeStatsSummary = {
|
||||
totalAgentsSpawned: number
|
||||
totalPRsCreated: number
|
||||
totalAgentTimeMs: number
|
||||
firstEventAt: number | null
|
||||
}
|
||||
|
||||
// Sum paired hosts only: process-lifetime cached replies outlive unpairing and a shared slot would show whichever host replied last.
|
||||
// Wire rows and fields may be absent or malformed; consumer guards supply all defaults.
|
||||
export type HomeStatsRow = Partial<HomeStatsSummary> | null | undefined
|
||||
|
||||
export function totalHomeStats(
|
||||
byHost: Record<string, HomeStatsRow>,
|
||||
hostIds: readonly string[]
|
||||
): HomeStatsSummary | null {
|
||||
const hosts = hostIds.filter((id) => id in byHost).map((id) => byHost[id])
|
||||
if (hosts.length === 0) {
|
||||
return null
|
||||
}
|
||||
const total: HomeStatsSummary = {
|
||||
totalAgentsSpawned: 0,
|
||||
totalPRsCreated: 0,
|
||||
totalAgentTimeMs: 0,
|
||||
firstEventAt: null
|
||||
}
|
||||
for (const host of hosts) {
|
||||
// One malformed host reply must not crash or NaN the combined usage header.
|
||||
if (!host || typeof host !== 'object') {
|
||||
continue
|
||||
}
|
||||
total.totalAgentsSpawned += finiteOrZero(host.totalAgentsSpawned)
|
||||
total.totalPRsCreated += finiteOrZero(host.totalPRsCreated)
|
||||
total.totalAgentTimeMs += finiteOrZero(host.totalAgentTimeMs)
|
||||
if (typeof host.firstEventAt === 'number' && Number.isFinite(host.firstEventAt)) {
|
||||
total.firstEventAt =
|
||||
total.firstEventAt === null
|
||||
? host.firstEventAt
|
||||
: Math.min(total.firstEventAt, host.firstEventAt)
|
||||
}
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
function finiteOrZero(value: number | undefined): number {
|
||||
return typeof value === 'number' && Number.isFinite(value) ? value : 0
|
||||
}
|
||||
@@ -198,7 +198,7 @@ describe('what the pin still admits', () => {
|
||||
expect(drift.join('\n')).toContain(
|
||||
`identical: pinned ${tally.identical}, ran ${moved.identical}`
|
||||
)
|
||||
expect(drift.join('\n')).toContain('result-absent-settlement: pinned 342, ran 341')
|
||||
expect(drift.join('\n')).toContain('result-absent-settlement: pinned 337, ran 336')
|
||||
})
|
||||
|
||||
it('goes red on a class that grew and on the corpus losing a golden', () => {
|
||||
|
||||
@@ -116,13 +116,13 @@ export const BRIDGED_PARITY_EXCLUSIONS: Readonly<Partial<Record<BridgedParityCla
|
||||
|
||||
// Pin strict History verdicts and unchanged legacy counts in both directions.
|
||||
export const BRIDGED_PARITY_BASELINE: Readonly<Record<BridgedParityClass | 'identical', number>> = {
|
||||
identical: 394,
|
||||
identical: 387,
|
||||
'reply-meta-required': 0,
|
||||
'result-absent-settlement': 342,
|
||||
'result-absent-settlement': 337,
|
||||
'result-absent-observation': 3,
|
||||
'result-absent-stream-release': 6,
|
||||
'params-undefined': 27,
|
||||
'write-ordinal': 13,
|
||||
'write-ordinal': 12,
|
||||
unclassified: 0
|
||||
}
|
||||
|
||||
@@ -175,8 +175,7 @@ export const BRIDGED_PARITY_MEMBERS: Readonly<
|
||||
'matrix-live-worktree-name-runtime.clientevents.subscribe-2-1',
|
||||
'matrix-live-worktree-name-worktree.show-1',
|
||||
'matrix-live-worktree-name-worktree.show-2',
|
||||
'matrix-live-worktree-name-worktree.show-3',
|
||||
'settings-home-coalesced'
|
||||
'matrix-live-worktree-name-worktree.show-3'
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -433,19 +433,19 @@ direct and relay transports.
|
||||
The frame plumbing is method-agnostic, so what stops a site is its consumer rather than the runner.
|
||||
Blur is unrecorded across all ten subscribing sites: `useFocusEffect` is substituted as `useEffect`,
|
||||
so a route's focus cleanup is recorded at unmount and an unsubscribe only a blur would reach is not
|
||||
— driving focus needs a substitute, and no recording reads one yet. Four of the nine probes pin
|
||||
— driving focus needs a substitute, and no recording reads one yet. Three of the eight probes pin
|
||||
behaviour with no demonstrated mutation — the two mixed reject/refusal new-tab orders
|
||||
and the home-providers and resume-metadata refresh refusals; they are frozen observations, not
|
||||
and the resume-metadata refresh refusal; they are frozen observations, not
|
||||
proven defect detectors. `settings.resume-metadata` projects `{}` as its state, so its probe
|
||||
observes only sender calls and settlements.
|
||||
|
||||
### Recorded finding: a refused refresh is not handled the same way twice
|
||||
|
||||
The five refuse-after-data probes record a `settings.get` read refusing a _refresh_ after a
|
||||
success: home providers and task hydration read through `settingsRead`, workspace context, resume
|
||||
metadata and repo metadata through `optionalSettingsRead`. Which one a site uses does not change
|
||||
The four refuse-after-data probes record a `settings.get` read refusing a _refresh_ after a
|
||||
success: task hydration reads through `settingsRead`, workspace context, resume metadata and repo
|
||||
metadata through `optionalSettingsRead`. Which one a site uses does not change
|
||||
what these probes record — the two share an acceptance and differ only in how they read a null
|
||||
result, and a refusal never reaches the reader. Four call sites retain what they had.
|
||||
result, and a refusal never reaches the reader. Three call sites retain what they had.
|
||||
`use-mobile-tasks-runtime-hydration.tsx` does not: it publishes `{}`, so a refused refresh wipes
|
||||
the runtime task settings. That divergence is recorded, not repaired —
|
||||
`settings-task-hydration-refuse-after-data.json` is the observation, and changing the behaviour is
|
||||
@@ -524,7 +524,7 @@ list is the count; a number in this paragraph would be one more thing that canno
|
||||
for a session that is already live. Closing it needs an adapter that can attach a terminal
|
||||
mid-scenario.
|
||||
|
||||
The original settings slice coverage maps nine host-RPC callers in
|
||||
The original settings slice coverage maps eight host-RPC callers in
|
||||
`settings-recording-coverage.json`; device-preference entries are excluded by coordinator
|
||||
instruction. Later manifest additions require new scenarios and remain uncovered until
|
||||
those recordings land. This runner does not certify native storage or transport skew.
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
import type { operationModuleLoader } from '../operation-module-loader'
|
||||
import type { MountAdapter } from '../recording-scenario'
|
||||
|
||||
const HOST = 'host-1'
|
||||
|
||||
// Load the decoder through AccountUsage so recordings verify the screen module works under the mount loader.
|
||||
export function homeAccountsMountAdapters(
|
||||
modules: ReturnType<typeof operationModuleLoader>
|
||||
): Record<string, MountAdapter> {
|
||||
return {
|
||||
'home.host-accounts': (context) => {
|
||||
const fetchAccounts = modules.load<typeof import('../../../home/mobile-home-host-requests')>(
|
||||
'mobile/src/home/mobile-home-host-requests.ts'
|
||||
).fetchMobileHomeAccounts
|
||||
let snapshots: Record<string, unknown> = {}
|
||||
let disposed = false
|
||||
return {
|
||||
action(name) {
|
||||
if (name === 'unmount') {
|
||||
disposed = true
|
||||
return
|
||||
}
|
||||
if (name !== 'load') {
|
||||
throw new Error(`Unknown home accounts action: ${name}`)
|
||||
}
|
||||
return fetchAccounts(
|
||||
context.client,
|
||||
HOST,
|
||||
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the recorder observes the published map as data, not as a decoded snapshot.
|
||||
((update: (value: Record<string, unknown>) => Record<string, unknown>) => {
|
||||
snapshots = update(snapshots)
|
||||
context.effect('accounts', snapshots)
|
||||
}) as Parameters<typeof fetchAccounts>[2],
|
||||
() => disposed
|
||||
)
|
||||
},
|
||||
state: () => ({ ...snapshots }),
|
||||
dispose: () => {
|
||||
disposed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,6 @@ import { hookMount, performHookAction } from '../hook-mount'
|
||||
import { observableModel, projectObservable } from '../observable-model'
|
||||
import type { operationModuleLoader } from '../operation-module-loader'
|
||||
|
||||
const HOST = 'host-1'
|
||||
|
||||
export function hostScreenMountAdapters(
|
||||
modules: ReturnType<typeof operationModuleLoader>
|
||||
): Record<string, MountAdapter> {
|
||||
@@ -47,34 +45,6 @@ export function hostScreenMountAdapters(
|
||||
state: () => projectObservable({ view: state.view }),
|
||||
dispose: hook.unmount
|
||||
}
|
||||
},
|
||||
'home.host-stats': (context) => {
|
||||
const fetchStats = modules.load(
|
||||
'mobile/src/home/mobile-home-host-requests.ts'
|
||||
).fetchMobileHomeStats
|
||||
let stats: Record<string, unknown> = {}
|
||||
let disposed = false
|
||||
return {
|
||||
action(name) {
|
||||
if (name === 'unmount') {
|
||||
disposed = true
|
||||
return
|
||||
}
|
||||
return fetchStats(
|
||||
context.client,
|
||||
HOST,
|
||||
(update: (value: Record<string, unknown>) => Record<string, unknown>) => {
|
||||
stats = update(stats)
|
||||
context.effect('stats', stats)
|
||||
},
|
||||
() => disposed
|
||||
)
|
||||
},
|
||||
state: () => ({ ...stats }),
|
||||
dispose: () => {
|
||||
disposed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import { fileInventoryMountAdapters } from './file-inventory-mount-adapters'
|
||||
import { fileTapOpenMountAdapters } from './file-tap-open-mount-adapters'
|
||||
import { fileRequestMountAdapters } from './file-request-mount-adapters'
|
||||
import { githubPrMountAdapters } from './github-pr-mount-adapters'
|
||||
import { homeAccountsMountAdapters } from './home-accounts-mount-adapters'
|
||||
import { hostScreenMountAdapters } from './host-screen-mount-adapters'
|
||||
import { hostWorktreeActionMountAdapters } from './host-worktree-action-mount-adapters'
|
||||
import { hostedReviewMountAdapters } from './hosted-review-mount-adapters'
|
||||
@@ -119,7 +118,6 @@ export const MOUNTED_OPERATION_MODULES: readonly MountedOperationModule[] = [
|
||||
{ source: 'file-tap-open-mount-adapters.ts', mounts: fileTapOpenMountAdapters },
|
||||
{ source: 'file-request-mount-adapters.ts', mounts: fileRequestMountAdapters },
|
||||
{ source: 'github-pr-mount-adapters.ts', mounts: githubPrMountAdapters },
|
||||
{ source: 'home-accounts-mount-adapters.ts', mounts: homeAccountsMountAdapters },
|
||||
{ source: 'host-screen-mount-adapters.ts', mounts: hostScreenMountAdapters },
|
||||
{
|
||||
source: 'host-worktree-action-mount-adapters.ts',
|
||||
|
||||
@@ -76,34 +76,6 @@ export function settingsMountAdapters(
|
||||
dispose: hook.unmount
|
||||
}
|
||||
},
|
||||
'settings.home-providers': (context) => {
|
||||
const load = modules.load(
|
||||
'mobile/src/home/mobile-home-host-requests.ts'
|
||||
).fetchMobileHomeTaskProviders
|
||||
let providers: unknown = {}
|
||||
let disposed = false
|
||||
return {
|
||||
action(name) {
|
||||
if (name === 'unmount') {
|
||||
disposed = true
|
||||
return
|
||||
}
|
||||
load(
|
||||
context.client,
|
||||
'host-1',
|
||||
(update: (value: unknown) => unknown) => {
|
||||
providers = update(providers)
|
||||
context.effect('providers', providers)
|
||||
},
|
||||
() => disposed
|
||||
)
|
||||
},
|
||||
state: () => providers,
|
||||
dispose: () => {
|
||||
disposed = true
|
||||
}
|
||||
}
|
||||
},
|
||||
'settings.resume-metadata': ({ client }) => {
|
||||
const load = modules.load<typeof ResumeMetadata>(
|
||||
'mobile/src/agent-history/mobile-agent-history-resume-metadata.ts'
|
||||
|
||||
@@ -25,7 +25,6 @@ export type DerivedGolden = {
|
||||
const SIBLING_SCHEDULE_BASES = [
|
||||
'b3',
|
||||
'settings-new-tab-ssh',
|
||||
'settings-home-providers-fulfilled',
|
||||
'settings-workspace-context-fulfilled',
|
||||
'settings-resume-metadata-fulfilled',
|
||||
'settings-task-hydration-fulfilled',
|
||||
|
||||
@@ -53,13 +53,6 @@ export const OPERATION_MUTATIONS = {
|
||||
return response
|
||||
}),`
|
||||
},
|
||||
// Decodes the reply envelope instead of the accepted snapshot, so the Home card publishes nothing
|
||||
// where a host answered.
|
||||
'home-accounts-envelope': {
|
||||
file: 'mobile-home-host-requests.ts',
|
||||
before: 'const snapshot = decodeAccountsSnapshot(accounts.value)',
|
||||
after: 'const snapshot = decodeAccountsSnapshot(reply)'
|
||||
},
|
||||
// Offset-zero chunk writes preserve length; digest and decoded-byte observations must detect corruption.
|
||||
'mobile-web-bundle-chunk-placement': {
|
||||
file: 'mobile-web-bundle-fetch.ts',
|
||||
@@ -110,12 +103,6 @@ export const OPERATION_MUTATIONS = {
|
||||
after:
|
||||
'(settingsRes.value.result as NewWorktreeRuntimeSettings & { visibleTaskProviders?: unknown })'
|
||||
},
|
||||
// Treats any successful linear.status reply as a connected Linear account.
|
||||
'home-providers-linear': {
|
||||
file: 'mobile-home-host-requests.ts',
|
||||
before: 'linearConnected: linear?.connected === true',
|
||||
after: 'linearConnected: linear !== null'
|
||||
},
|
||||
// Read the wrong host-platform field so rows display an unreported platform.
|
||||
'repo-metadata-platform': {
|
||||
file: 'host-screen-reply-schema.ts',
|
||||
|
||||
@@ -24,14 +24,12 @@ const mutants: Record<string, Mutation> = {
|
||||
b3: 'order',
|
||||
'settings-bot-overrides-fulfilled': 'bot-overrides-envelope',
|
||||
'settings-workspace-context-fulfilled': 'workspace-context-envelope',
|
||||
'settings-home-providers-fulfilled': 'home-providers-linear',
|
||||
'settings-repo-metadata-fulfilled': 'repo-metadata-platform',
|
||||
'settings-task-hydration-fulfilled': 'task-hydration-envelope',
|
||||
'settings-task-write': 'task-preferences-optimistic',
|
||||
'settings-workspace-submit-fulfilled': 'workspace-submit-envelope',
|
||||
'settings-task-workspace-fulfilled': 'task-workspace-envelope',
|
||||
'native-chat-write-delivery-unknown': 'native-chat-send-delivery-unknown',
|
||||
'home-host-accounts': 'home-accounts-envelope',
|
||||
'notifications-display-test-accepted': 'push-test-envelope',
|
||||
'tasks-route-repo-list': 'task-screen-repo-envelope',
|
||||
'linear-select-workspace': 'linear-workspace-context-reload',
|
||||
|
||||
@@ -103,6 +103,7 @@ describe('fetchHomeHostWorktreeInfo', () => {
|
||||
totalWorktrees: 12,
|
||||
activeCount: 2,
|
||||
lastActiveWorktree: expect.objectContaining({ worktreeId: 'wt-0' }),
|
||||
attention: expect.any(Array),
|
||||
catalogUnavailable: true,
|
||||
staleCounts: true,
|
||||
countsProvenAt: expect.any(Number)
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
type HomeWorktreeSummary,
|
||||
type HostWorktreeInfo
|
||||
} from './home-worktree-info'
|
||||
import { selectHomeAttentionWorktrees } from './home-jump-back-in'
|
||||
import { pickResumeWorktree } from './resume-worktree'
|
||||
import { isWorktreeActive } from './workspace-list-sections'
|
||||
import { worktreeCatalogRead } from './worktree-catalog-operations'
|
||||
@@ -44,7 +45,7 @@ export function fetchHomeHostWorktreeInfo(
|
||||
markUnavailable()
|
||||
return
|
||||
}
|
||||
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: `worktrees` is a salvaged member, so the `?? []` is what makes it an array; the rows stay opaque because three screens project a row differently. This card reads `status` and the resume pick, both through their own guards, and the `worktree-home-catalog` golden records the row it is given as `{worktreeId, displayName, repo, status}`.
|
||||
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: `worktrees` is a salvaged member, so the `?? []` is what makes it an array; the rows stay opaque because three screens project a row differently. Home reads `status`, `sidebarStatus` and the resume pick, both through their own guards, and the `worktree-home-catalog` golden records the row it is given as `{worktreeId, displayName, repo, status}`.
|
||||
const worktrees = (catalog.value.worktrees ?? []) as HomeWorktreeSummary[]
|
||||
setCachedWorktrees(hostId, worktrees, { proven: true })
|
||||
// Count what the list shows: archived rows are hidden, and active is the sleeping filter's rule.
|
||||
@@ -58,6 +59,7 @@ export function fetchHomeHostWorktreeInfo(
|
||||
totalWorktrees: listed.length,
|
||||
activeCount: listed.filter(isWorktreeActive).length,
|
||||
lastActiveWorktree: lastActive,
|
||||
attention: selectHomeAttentionWorktrees(listed),
|
||||
countsProvenAt: Date.now()
|
||||
}
|
||||
}))
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { selectHomeAttentionWorktrees, selectHomeJumpBackInRows } from './home-jump-back-in'
|
||||
import type { HomeWorktreeSummary, HostWorktreeInfo } from './home-worktree-info'
|
||||
|
||||
const NOW = 1_000_000_000
|
||||
|
||||
function row(id: string, fields: Partial<HomeWorktreeSummary>): HomeWorktreeSummary {
|
||||
return {
|
||||
worktreeId: id,
|
||||
repo: 'orca',
|
||||
branch: id,
|
||||
displayName: id,
|
||||
liveTerminalCount: 1,
|
||||
...fields
|
||||
}
|
||||
}
|
||||
|
||||
function info(hostId: string, rows: HomeWorktreeSummary[]): HostWorktreeInfo {
|
||||
return {
|
||||
hostId,
|
||||
totalWorktrees: rows.length,
|
||||
activeCount: 0,
|
||||
lastActiveWorktree: null,
|
||||
attention: selectHomeAttentionWorktrees(rows),
|
||||
countsProvenAt: NOW
|
||||
}
|
||||
}
|
||||
|
||||
describe('home jump back in', () => {
|
||||
it('puts agents waiting on the user first, then unseen finished work, then running work', () => {
|
||||
const rows = selectHomeAttentionWorktrees([
|
||||
row('running', { sidebarStatus: 'working', lastOutputAt: NOW }),
|
||||
row('finished-seen', { sidebarStatus: 'done', unread: false }),
|
||||
row('finished-unseen', { sidebarStatus: 'done', unread: true, lastOutputAt: NOW - 5 }),
|
||||
row('blocked', { sidebarStatus: 'permission', lastOutputAt: NOW - 10 }),
|
||||
row('archived', { sidebarStatus: 'permission', isArchived: true }),
|
||||
row('idle', { status: 'inactive', hasHostSidebarActivity: false })
|
||||
])
|
||||
expect(rows.map((worktree) => worktree.worktreeId)).toEqual([
|
||||
'blocked',
|
||||
'finished-unseen',
|
||||
'running'
|
||||
])
|
||||
})
|
||||
|
||||
it('claims no agent state for a desktop that is offline or whose last read failed', () => {
|
||||
const blocked = [row('blocked', { sidebarStatus: 'permission' })]
|
||||
const rows = selectHomeJumpBackInRows({
|
||||
hosts: [{ id: 'live' }, { id: 'offline' }, { id: 'failed' }],
|
||||
hostStates: { live: 'connected', offline: 'disconnected', failed: 'connected' },
|
||||
worktreeInfo: {
|
||||
live: info('live', blocked),
|
||||
offline: info('offline', blocked),
|
||||
failed: { ...info('failed', blocked), catalogUnavailable: true }
|
||||
},
|
||||
resumeCard: null
|
||||
})
|
||||
expect(rows.map((entry) => entry.hostId)).toEqual(['live'])
|
||||
})
|
||||
|
||||
it('adds the last-opened workspace after the agents, unless it is already listed', () => {
|
||||
const resumeWorktree = row('resume', {})
|
||||
const base = {
|
||||
hosts: [{ id: 'host' }],
|
||||
hostStates: { host: 'connected' as const }
|
||||
}
|
||||
const appended = selectHomeJumpBackInRows({
|
||||
...base,
|
||||
worktreeInfo: { host: info('host', [row('blocked', { sidebarStatus: 'permission' })]) },
|
||||
resumeCard: { hostId: 'host', worktree: resumeWorktree, actionable: true }
|
||||
})
|
||||
expect(appended.map((entry) => [entry.worktree.worktreeId, entry.lastOpened])).toEqual([
|
||||
['blocked', false],
|
||||
['resume', true]
|
||||
])
|
||||
|
||||
const deduped = selectHomeJumpBackInRows({
|
||||
...base,
|
||||
worktreeInfo: { host: info('host', [row('resume', { sidebarStatus: 'working' })]) },
|
||||
resumeCard: { hostId: 'host', worktree: resumeWorktree, actionable: true }
|
||||
})
|
||||
expect(deduped.map((entry) => [entry.worktree.worktreeId, entry.lastOpened])).toEqual([
|
||||
['resume', false]
|
||||
])
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,125 @@
|
||||
import type { WorktreeStatus } from '../../../src/shared/worktree-status-rollup'
|
||||
import type { ConnectionState } from '../transport/types'
|
||||
import type { HomeResumeCard } from './home-resume-card'
|
||||
import type {
|
||||
HomeAttentionWorktree,
|
||||
HomeWorktreeSummary,
|
||||
HostWorktreeInfo
|
||||
} from './home-worktree-info'
|
||||
import { getWorktreeDotStatus } from './workspace-list-ordering'
|
||||
|
||||
const ATTENTION_ROWS_PER_HOST = 6
|
||||
const ATTENTION_ROWS_SHOWN = 3
|
||||
|
||||
// Lower comes first: agents blocked on the user, then finished work they haven't seen, then running work.
|
||||
function attentionRank(dotStatus: WorktreeStatus, unread: boolean): number | null {
|
||||
if (dotStatus === 'permission' || dotStatus === 'failed') {
|
||||
return 0
|
||||
}
|
||||
if (dotStatus === 'done' && unread) {
|
||||
return 1
|
||||
}
|
||||
if (dotStatus === 'working' || dotStatus === 'monitoring') {
|
||||
return 2
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function byRankThenRecent(
|
||||
a: { rank: number; lastOutputAt?: number },
|
||||
b: { rank: number; lastOutputAt?: number }
|
||||
): number {
|
||||
return a.rank - b.rank || (b.lastOutputAt ?? 0) - (a.lastOutputAt ?? 0)
|
||||
}
|
||||
|
||||
/** The rows of one desktop's list that "Jump back in" may show, using the workspace list's dot status. */
|
||||
export function selectHomeAttentionWorktrees(
|
||||
rows: readonly HomeWorktreeSummary[]
|
||||
): HomeAttentionWorktree[] {
|
||||
const ranked: (HomeAttentionWorktree & { rank: number })[] = []
|
||||
for (const row of rows) {
|
||||
if (row.isArchived === true) {
|
||||
continue
|
||||
}
|
||||
const dotStatus = getWorktreeDotStatus(row)
|
||||
const rank = attentionRank(dotStatus, row.unread === true)
|
||||
if (rank === null) {
|
||||
continue
|
||||
}
|
||||
ranked.push({
|
||||
rank,
|
||||
dotStatus,
|
||||
worktreeId: row.worktreeId,
|
||||
repo: row.repo,
|
||||
branch: row.branch,
|
||||
displayName: row.displayName,
|
||||
lastOutputAt: typeof row.lastOutputAt === 'number' ? row.lastOutputAt : undefined,
|
||||
workingMode: row.workingMode
|
||||
})
|
||||
}
|
||||
return ranked
|
||||
.sort(byRankThenRecent)
|
||||
.slice(0, ATTENTION_ROWS_PER_HOST)
|
||||
.map(({ rank: _rank, ...worktree }) => worktree)
|
||||
}
|
||||
|
||||
export type HomeJumpBackInRow = Readonly<{
|
||||
hostId: string
|
||||
worktree: HomeAttentionWorktree
|
||||
/** The resume target rather than an agent that needs the user. */
|
||||
lastOpened: boolean
|
||||
actionable: boolean
|
||||
}>
|
||||
|
||||
export function selectHomeJumpBackInRows(input: {
|
||||
hosts: readonly { id: string }[]
|
||||
hostStates: Readonly<Record<string, ConnectionState>>
|
||||
worktreeInfo: Readonly<Record<string, HostWorktreeInfo>>
|
||||
resumeCard: HomeResumeCard | null
|
||||
}): HomeJumpBackInRow[] {
|
||||
const candidates: (HomeJumpBackInRow & { rank: number; lastOutputAt?: number })[] = []
|
||||
for (const host of input.hosts) {
|
||||
const info = input.worktreeInfo[host.id]
|
||||
// Agent states are shown only from a connected desktop's list read this session.
|
||||
if (input.hostStates[host.id] !== 'connected' || !info?.attention || info.catalogUnavailable) {
|
||||
continue
|
||||
}
|
||||
for (const worktree of info.attention) {
|
||||
candidates.push({
|
||||
hostId: host.id,
|
||||
worktree,
|
||||
lastOpened: false,
|
||||
actionable: true,
|
||||
rank: attentionRank(worktree.dotStatus, true) ?? 2,
|
||||
lastOutputAt: worktree.lastOutputAt
|
||||
})
|
||||
}
|
||||
}
|
||||
const rows: HomeJumpBackInRow[] = candidates
|
||||
.sort(byRankThenRecent)
|
||||
.slice(0, ATTENTION_ROWS_SHOWN)
|
||||
.map(({ rank: _rank, lastOutputAt: _lastOutputAt, ...row }) => row)
|
||||
const resume = input.resumeCard
|
||||
if (
|
||||
resume &&
|
||||
!rows.some(
|
||||
(row) =>
|
||||
row.hostId === resume.hostId && row.worktree.worktreeId === resume.worktree.worktreeId
|
||||
)
|
||||
) {
|
||||
rows.push({
|
||||
hostId: resume.hostId,
|
||||
worktree: {
|
||||
worktreeId: resume.worktree.worktreeId,
|
||||
repo: resume.worktree.repo,
|
||||
branch: resume.worktree.branch,
|
||||
displayName: resume.worktree.displayName,
|
||||
lastOutputAt: resume.worktree.lastOutputAt,
|
||||
dotStatus: 'inactive'
|
||||
},
|
||||
lastOpened: true,
|
||||
actionable: resume.actionable
|
||||
})
|
||||
}
|
||||
return rows
|
||||
}
|
||||
@@ -59,13 +59,19 @@ function hostHistoryCard(
|
||||
|
||||
// Only a proven catalog can establish deletion; cold/loading snapshots leave navigation to the session’s later check.
|
||||
export function isResumeTargetConfirmedMissing(
|
||||
card: HomeResumeCard,
|
||||
provenWorktrees: readonly { worktreeId: string }[] | null
|
||||
card: Readonly<{ worktree: Readonly<{ worktreeId: string }> }>,
|
||||
provenWorktrees: readonly unknown[] | null
|
||||
): boolean {
|
||||
if (!provenWorktrees || isSyntheticWorkspaceRoute(card.worktree.worktreeId)) {
|
||||
return false
|
||||
}
|
||||
return !provenWorktrees.some((worktree) => worktree.worktreeId === card.worktree.worktreeId)
|
||||
return !provenWorktrees.some(
|
||||
(worktree) =>
|
||||
typeof worktree === 'object' &&
|
||||
worktree !== null &&
|
||||
'worktreeId' in worktree &&
|
||||
worktree.worktreeId === card.worktree.worktreeId
|
||||
)
|
||||
}
|
||||
|
||||
export function selectHomeResumeCard(input: HomeResumeCardInput): HomeResumeCard | null {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import type { AgentWorkingMode } from '../../../src/shared/agent-status-types'
|
||||
import type { WorktreeStatus } from '../../../src/shared/worktree-status-rollup'
|
||||
|
||||
export type HomeWorktreeSummary = {
|
||||
worktreeId: string
|
||||
repo: string
|
||||
@@ -10,13 +13,22 @@ export type HomeWorktreeSummary = {
|
||||
isArchived?: boolean
|
||||
isActive?: boolean
|
||||
lastOutputAt?: number
|
||||
sidebarStatus?: string
|
||||
workingMode?: AgentWorkingMode
|
||||
}
|
||||
|
||||
/** A workspace whose agents Home surfaces under "Jump back in", with the list's dot status. */
|
||||
export type HomeAttentionWorktree = Pick<
|
||||
HomeWorktreeSummary,
|
||||
'worktreeId' | 'repo' | 'branch' | 'displayName' | 'lastOutputAt' | 'workingMode'
|
||||
> & { dotStatus: WorktreeStatus }
|
||||
|
||||
export type HostWorktreeInfo = {
|
||||
hostId: string
|
||||
totalWorktrees: number
|
||||
activeCount: number
|
||||
lastActiveWorktree: HomeWorktreeSummary | null
|
||||
attention?: HomeAttentionWorktree[]
|
||||
catalogUnavailable?: boolean
|
||||
// The counts are the last proven ones, kept across a failed refresh.
|
||||
staleCounts?: boolean
|
||||
@@ -27,6 +39,15 @@ export type HostWorktreeInfo = {
|
||||
// Persisted counts may be days old; bound their live label without discarding history.
|
||||
export const HOME_WORKTREE_COUNTS_LIVE_MAX_AGE_MS = 10 * 60_000
|
||||
|
||||
/** Agent states are live facts; a saved copy or a dropped connection can't vouch for them. */
|
||||
export function withoutHomeAgentStates(info: HostWorktreeInfo): HostWorktreeInfo {
|
||||
if (!info.attention) {
|
||||
return info
|
||||
}
|
||||
const { attention: _attention, ...rest } = info
|
||||
return rest
|
||||
}
|
||||
|
||||
export function markHomeWorktreeCatalogUnavailable(
|
||||
current: HostWorktreeInfo | undefined,
|
||||
hostId: string
|
||||
|
||||
@@ -90,8 +90,13 @@ export function sortWorktrees(
|
||||
)
|
||||
}
|
||||
|
||||
type WorktreeStatusInputs = Pick<
|
||||
Worktree,
|
||||
'hasHostSidebarActivity' | 'status' | 'liveTerminalCount' | 'sidebarStatus'
|
||||
>
|
||||
|
||||
export function getWorktreeStatus(
|
||||
w: Worktree
|
||||
w: WorktreeStatusInputs
|
||||
): 'working' | 'active' | 'permission' | 'done' | 'inactive' {
|
||||
// Match desktop sidebar activity; retained/background PTYs alone must not imply an active workspace.
|
||||
if (w.hasHostSidebarActivity === false) {
|
||||
@@ -127,6 +132,6 @@ function isHostVerdictDotStatus(value: string | undefined): value is WorktreeSta
|
||||
return value !== undefined && HOST_VERDICT_DOT_STATUSES.has(value)
|
||||
}
|
||||
|
||||
export function getWorktreeDotStatus(w: Worktree): WorktreeStatus {
|
||||
export function getWorktreeDotStatus(w: WorktreeStatusInputs): WorktreeStatus {
|
||||
return isHostVerdictDotStatus(w.sidebarStatus) ? w.sidebarStatus : getWorktreeStatus(w)
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
--m-status-green: #22c55e;
|
||||
--m-status-amber: #f59e0b;
|
||||
--m-status-red: #ef4444;
|
||||
--m-agent-done: #10b981;
|
||||
--m-mono: 'Menlo', 'SF Mono', monospace;
|
||||
|
||||
position: relative;
|
||||
@@ -1084,6 +1085,7 @@
|
||||
/* Paint containment prevents transformed slides leaking past Chromium’s rounded overflow clipping. */
|
||||
contain: paint;
|
||||
isolation: isolate;
|
||||
container: mp-phone-screen / inline-size;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-screen-slide {
|
||||
@@ -1191,115 +1193,122 @@
|
||||
padding: 0 16px 16px;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-greeting {
|
||||
padding: 6px 4px 10px;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-greeting-title {
|
||||
.mobile-page-root .mp-home-heading {
|
||||
margin: 20px 0 7px;
|
||||
padding: 0 4px;
|
||||
color: var(--m-text-primary);
|
||||
font-size: 22px;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-stat-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 8px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-stat-card {
|
||||
border: 1px solid var(--m-border);
|
||||
border-radius: 10px;
|
||||
background: rgba(26, 26, 26, 0.6);
|
||||
padding: 8px 10px;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-stat-value {
|
||||
color: var(--m-text-primary);
|
||||
font-size: 17px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-stat-label {
|
||||
margin-top: 1px;
|
||||
color: var(--m-text-muted);
|
||||
font-size: 10px;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-section-label {
|
||||
margin: 0 0 6px;
|
||||
padding-left: 4px;
|
||||
color: var(--m-text-muted);
|
||||
font-size: 10px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.06em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-host-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: 60px;
|
||||
padding: 10px 12px;
|
||||
gap: 12px;
|
||||
border: 1px solid var(--m-border);
|
||||
border-radius: 14px;
|
||||
.mobile-page-root .mp-home-heading.is-first {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-home-group {
|
||||
overflow: hidden;
|
||||
border-radius: 16px;
|
||||
background: var(--m-bg-panel);
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-host-card + .mp-host-card {
|
||||
margin-top: 6px;
|
||||
.mobile-page-root .mp-home-row {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
padding-left: 14px;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-host-icon {
|
||||
display: grid;
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
place-items: center;
|
||||
border-radius: 11px;
|
||||
background: var(--m-bg-raised);
|
||||
color: var(--m-text-primary);
|
||||
.mobile-page-root .mp-home-row + .mp-home-row::before,
|
||||
.mobile-page-root .mp-home-row + .mp-home-action-row::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 34px;
|
||||
border-top: 1px solid var(--m-border);
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-host-icon svg {
|
||||
width: 18px;
|
||||
.mobile-page-root .mp-home-row + .mp-home-action-row::before {
|
||||
left: 14px;
|
||||
}
|
||||
|
||||
/* Status column; separators inset to the text after it. */
|
||||
.mobile-page-root .mp-home-status {
|
||||
display: flex;
|
||||
width: 20px;
|
||||
height: 18px;
|
||||
stroke-width: 1.75;
|
||||
margin-top: 10px;
|
||||
align-items: center;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-host-icon.is-dim {
|
||||
color: var(--m-text-secondary);
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-host-main {
|
||||
.mobile-page-root .mp-home-text {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-host-name {
|
||||
.mobile-page-root .mp-home-title,
|
||||
.mobile-page-root .mp-home-subtitle {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-home-title {
|
||||
color: var(--m-text-primary);
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-host-name.is-dim {
|
||||
.mobile-page-root .mp-home-subtitle {
|
||||
margin-top: 1px;
|
||||
color: var(--m-text-secondary);
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-host-meta {
|
||||
.mobile-page-root .mp-home-subtitle .is-waiting {
|
||||
color: var(--m-status-red);
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-home-subtitle .is-done {
|
||||
color: var(--m-agent-done);
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-home-trailing {
|
||||
display: flex;
|
||||
height: 18px;
|
||||
margin-top: 10px;
|
||||
padding: 0 10px 0 8px;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
margin-top: 2px;
|
||||
color: var(--m-text-secondary);
|
||||
font-size: 11px;
|
||||
white-space: nowrap;
|
||||
gap: 4px;
|
||||
color: var(--m-text-muted);
|
||||
font-size: 12px;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-home-trailing svg {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
stroke-width: 2;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-home-action-row {
|
||||
position: relative;
|
||||
display: flex;
|
||||
min-height: 42px;
|
||||
padding: 0 14px;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: var(--m-accent-blue);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-home-action-row svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
stroke-width: 2.2;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-status-dot {
|
||||
@@ -1321,69 +1330,6 @@
|
||||
background: var(--m-text-muted);
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-chevron-right {
|
||||
color: var(--m-text-muted);
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-chevron-right svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
stroke-width: 1.75;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-resume-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px 12px;
|
||||
gap: 12px;
|
||||
border: 1px solid var(--m-border);
|
||||
border-radius: 14px;
|
||||
background: var(--m-bg-panel);
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-resume-icon {
|
||||
display: grid;
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
place-items: center;
|
||||
border-radius: 11px;
|
||||
background: var(--m-bg-raised);
|
||||
color: var(--m-text-secondary);
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-resume-icon svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
stroke-width: 1.75;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-resume-title {
|
||||
color: var(--m-text-primary);
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-resume-sub {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
margin-top: 2px;
|
||||
color: var(--m-text-secondary);
|
||||
font-size: 11px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/* Why: keep long repo/branch labels inside the phone frame (matches RN numberOfLines={1}). */
|
||||
.mobile-page-root .mp-resume-sub > span:not(.mp-repo-dot) {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-repo-dot {
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
@@ -1391,183 +1337,61 @@
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-quick-actions {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-quick-action {
|
||||
/* Pinned like the phone's bottom bar; the bright fill marks the primary action. */
|
||||
.mobile-page-root .mp-home-bottom-bar {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
padding: 8px 12px 22px;
|
||||
border-top: 1px solid var(--m-border);
|
||||
background: var(--m-bg-base);
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-home-bar-button {
|
||||
display: flex;
|
||||
min-height: 40px;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid var(--m-border);
|
||||
border-radius: 14px;
|
||||
background: var(--m-bg-panel);
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-quick-action-icon {
|
||||
display: grid;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
place-items: center;
|
||||
border-radius: 9px;
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
color: var(--m-text-secondary);
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-quick-action-icon svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
stroke-width: 1.75;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-quick-action-label {
|
||||
color: var(--m-text-secondary);
|
||||
font-size: 12px;
|
||||
justify-content: center;
|
||||
min-width: 0;
|
||||
padding: 0 8px;
|
||||
gap: 5px;
|
||||
border-radius: 12px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-task-home-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px 12px;
|
||||
gap: 12px;
|
||||
border: 1px solid var(--m-border);
|
||||
border-radius: 14px;
|
||||
background: var(--m-bg-panel);
|
||||
.mobile-page-root .mp-home-bar-button svg {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
stroke-width: 2.2;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-task-home-icon {
|
||||
display: grid;
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
place-items: center;
|
||||
border-radius: 11px;
|
||||
background: var(--m-bg-raised);
|
||||
color: var(--m-text-secondary);
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-task-home-icon svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
stroke-width: 1.75;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-task-home-title {
|
||||
color: var(--m-text-primary);
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-task-home-subtitle {
|
||||
margin-top: 2px;
|
||||
color: var(--m-text-secondary);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-task-home-providers {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-task-home-provider-button {
|
||||
display: grid;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
place-items: center;
|
||||
border-radius: 6px;
|
||||
color: var(--m-text-secondary);
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-task-home-provider-button svg {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
stroke-width: 1.75;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-accounts-card {
|
||||
background: var(--m-bg-panel);
|
||||
border: 1px solid var(--m-border);
|
||||
border-radius: 14px;
|
||||
padding: 10px 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-accounts-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-accounts-icon {
|
||||
display: grid;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
place-items: center;
|
||||
border-radius: 9px;
|
||||
background: var(--m-bg-raised);
|
||||
color: var(--m-text-primary);
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-accounts-icon svg {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-accounts-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-accounts-email {
|
||||
color: var(--m-text-primary);
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-accounts-bars {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-usage-bar {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-usage-bar-label {
|
||||
color: var(--m-text-muted);
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
min-width: 16px;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-usage-bar-track {
|
||||
flex: 1;
|
||||
height: 4px;
|
||||
border-radius: 999px;
|
||||
background: var(--m-bg-raised);
|
||||
.mobile-page-root .mp-home-bar-button > span {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-usage-bar-fill {
|
||||
height: 100%;
|
||||
background: var(--m-text-secondary);
|
||||
border-radius: inherit;
|
||||
/* The narrow-layout frame can't fit icon and label in half its width. */
|
||||
@container mp-phone-screen (max-width: 320px) {
|
||||
.mobile-page-root .mp-home-bar-button {
|
||||
padding: 0 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-home-bar-button svg {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-home-bar-button.is-secondary {
|
||||
background: var(--m-bg-raised);
|
||||
color: var(--m-text-primary);
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-home-bar-button.is-primary {
|
||||
background: var(--m-text-primary);
|
||||
color: var(--m-bg-base);
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-wl-chrome {
|
||||
@@ -1730,7 +1554,7 @@
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-wl-dot.is-green {
|
||||
background: #10b981;
|
||||
background: var(--m-agent-done);
|
||||
}
|
||||
|
||||
.mobile-page-root .mp-wl-dot.is-muted {
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { defineFixture } from '../../../component-fixtures/component-fixture'
|
||||
import { HomeSlide } from './HomeSlide'
|
||||
|
||||
function PhoneFrame({ width }: { width: number }): React.JSX.Element {
|
||||
return (
|
||||
<div className="mobile-page-root" style={{ height: 'auto', overflow: 'visible' }}>
|
||||
<div className="mp-phone-frame" style={{ width }}>
|
||||
<div className="mp-phone-screen">
|
||||
<HomeSlide tapping={false} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// The carousel's largest frame and the narrow-layout frame.
|
||||
export const large = defineFixture({ width: 440, render: () => <PhoneFrame width={420} /> })
|
||||
|
||||
export const narrow = defineFixture({ width: 310, render: () => <PhoneFrame width={290} /> })
|
||||
@@ -1,8 +1,30 @@
|
||||
import { ClaudeIcon, OpenAIIcon } from '../../status-bar/icons'
|
||||
import { cn } from '../../../lib/utils'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
|
||||
type AgentIndicator = 'waiting' | 'done' | 'working' | 'idle'
|
||||
|
||||
type JumpBackInRowProps = {
|
||||
indicator: AgentIndicator
|
||||
name: string
|
||||
status: string
|
||||
desktop: string
|
||||
time: string
|
||||
}
|
||||
|
||||
const AGENT_DOT: Record<Exclude<AgentIndicator, 'working'>, string> = {
|
||||
waiting: 'is-red',
|
||||
done: 'is-green',
|
||||
idle: 'is-muted'
|
||||
}
|
||||
|
||||
// Waiting and done ask something of the user, so only they tint the status word (mobile parity).
|
||||
const STATUS_TONE: Partial<Record<AgentIndicator, string>> = {
|
||||
waiting: 'is-waiting',
|
||||
done: 'is-done'
|
||||
}
|
||||
|
||||
export function HomeSlide({ tapping }: { tapping: boolean }): React.JSX.Element {
|
||||
const macbookPro = translate('auto.components.mobile.slides.HomeSlide.19c212e25e', 'MacBook Pro')
|
||||
return (
|
||||
<div className="mp-device-screen">
|
||||
<div className="mp-app-topbar">
|
||||
@@ -22,226 +44,129 @@ export function HomeSlide({ tapping }: { tapping: boolean }): React.JSX.Element
|
||||
</div>
|
||||
|
||||
<div className="mp-scroll-region">
|
||||
<div className="mp-greeting">
|
||||
<div className="mp-greeting-title">
|
||||
{translate('auto.components.mobile.slides.HomeSlide.c0e2e9dcd9', 'Welcome back')}
|
||||
</div>
|
||||
<div className="mp-home-heading is-first">
|
||||
{translate('auto.components.mobile.slides.HomeSlide.dc1f3f5535', 'Jump back in')}
|
||||
</div>
|
||||
|
||||
<div className="mp-stat-row">
|
||||
<Stat
|
||||
value="1,284"
|
||||
label={translate(
|
||||
'auto.components.mobile.slides.HomeSlide.00a6903322',
|
||||
'Agents spawned'
|
||||
<div className="mp-home-group">
|
||||
<JumpBackInRow
|
||||
indicator="waiting"
|
||||
name="fix-auth-redirect"
|
||||
status={translate(
|
||||
'auto.components.mobile.slides.HomeSlide.c2a0215163',
|
||||
'Waiting for input'
|
||||
)}
|
||||
desktop={macbookPro}
|
||||
time={translate('auto.components.mobile.slides.HomeSlide.0c14dac908', '2m')}
|
||||
/>
|
||||
<Stat
|
||||
value="142h"
|
||||
label={translate('auto.components.mobile.slides.HomeSlide.4a40af029b', 'Agent time')}
|
||||
<JumpBackInRow
|
||||
indicator="done"
|
||||
name="tasks-sheet-polish"
|
||||
status={translate('auto.components.mobile.slides.HomeSlide.77c0dae863', 'Done')}
|
||||
desktop={translate('auto.components.mobile.slides.HomeSlide.b5308d5e2c', 'M1 Mini')}
|
||||
time={translate('auto.components.mobile.slides.HomeSlide.3166f865af', '14m')}
|
||||
/>
|
||||
<Stat
|
||||
value="96"
|
||||
label={translate('auto.components.mobile.slides.HomeSlide.156db8a68a', 'PRs created')}
|
||||
<JumpBackInRow
|
||||
indicator="working"
|
||||
name="relay-reconnect-backoff"
|
||||
status={translate('auto.components.mobile.slides.HomeSlide.609517273b', 'Working')}
|
||||
desktop={macbookPro}
|
||||
time={translate('auto.components.mobile.slides.HomeSlide.005afbad3d', 'now')}
|
||||
/>
|
||||
<JumpBackInRow
|
||||
indicator="idle"
|
||||
name="feat/mobile-page"
|
||||
status={translate('auto.components.mobile.slides.HomeSlide.e986b282af', 'Last opened')}
|
||||
desktop={macbookPro}
|
||||
time={translate('auto.components.mobile.slides.HomeSlide.8a3ebdbdac', '1h')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mp-section-label">
|
||||
<div className="mp-home-heading">
|
||||
{translate('auto.components.mobile.slides.HomeSlide.2f1a1d10c4', 'Desktops')}
|
||||
</div>
|
||||
<div className={cn('mp-host-card', tapping && 'is-tapping')}>
|
||||
<div className="mp-host-icon">
|
||||
<DesktopIcon />
|
||||
</div>
|
||||
<div className="mp-host-main">
|
||||
<div className="mp-host-name">
|
||||
{translate('auto.components.mobile.slides.HomeSlide.19c212e25e', 'MacBook Pro')}
|
||||
</div>
|
||||
<div className="mp-host-meta">
|
||||
<div className="mp-home-group">
|
||||
{/* The carousel's next slide is this desktop's worktree list, so the tap lands here. */}
|
||||
<div className={cn('mp-home-row', tapping && 'is-tapping')}>
|
||||
<div className="mp-home-status">
|
||||
<span className="mp-status-dot is-green" />
|
||||
<span>
|
||||
</div>
|
||||
<div className="mp-home-text">
|
||||
<div className="mp-home-title">{macbookPro}</div>
|
||||
<div className="mp-home-subtitle">
|
||||
{translate(
|
||||
'auto.components.mobile.slides.HomeSlide.0bc1881bc4',
|
||||
'Connected · 40 worktrees · 5 active'
|
||||
'auto.components.mobile.slides.HomeSlide.04a94cadee',
|
||||
'40 worktrees · 5 active'
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mp-home-trailing">
|
||||
<ChevronIcon />
|
||||
</div>
|
||||
</div>
|
||||
<div className="mp-chevron-right">
|
||||
<ChevronIcon />
|
||||
</div>
|
||||
</div>
|
||||
<div className="mp-host-card">
|
||||
<div className="mp-host-icon is-dim">
|
||||
<DesktopIcon />
|
||||
</div>
|
||||
<div className="mp-host-main">
|
||||
<div className="mp-host-name is-dim">
|
||||
{translate('auto.components.mobile.slides.HomeSlide.091355da3d', 'M1 Mini · home')}
|
||||
</div>
|
||||
<div className="mp-host-meta">
|
||||
<div className="mp-home-row">
|
||||
<div className="mp-home-status">
|
||||
<span className="mp-status-dot is-muted" />
|
||||
<span>
|
||||
</div>
|
||||
<div className="mp-home-text">
|
||||
<div className="mp-home-title">
|
||||
{translate('auto.components.mobile.slides.HomeSlide.b5308d5e2c', 'M1 Mini')}
|
||||
</div>
|
||||
<div className="mp-home-subtitle">
|
||||
{translate('auto.components.mobile.slides.HomeSlide.cf3f98fa3f', 'Disconnected')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mp-home-trailing">
|
||||
<ChevronIcon />
|
||||
</div>
|
||||
</div>
|
||||
<div className="mp-chevron-right">
|
||||
<ChevronIcon />
|
||||
<div className="mp-home-action-row">
|
||||
<PlusIcon />
|
||||
{translate('auto.components.mobile.slides.HomeSlide.aefd110ea5', 'Pair a desktop')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mp-section-label" style={{ marginTop: 14 }}>
|
||||
{translate('auto.components.mobile.slides.HomeSlide.c791677f2f', 'Resume')}
|
||||
<div className="mp-home-bottom-bar">
|
||||
<div className="mp-home-bar-button is-secondary">
|
||||
<ListTodoIcon />
|
||||
<span>{translate('auto.components.mobile.slides.HomeSlide.a4c3f7b7aa', 'Tasks')}</span>
|
||||
</div>
|
||||
<div className="mp-resume-card">
|
||||
<div className="mp-resume-icon">
|
||||
<ResumeIcon />
|
||||
</div>
|
||||
<div className="mp-host-main">
|
||||
<div className="mp-resume-title">
|
||||
{translate('auto.components.mobile.slides.HomeSlide.25d6e8a491', 'feat/mobile-page')}
|
||||
</div>
|
||||
<div className="mp-resume-sub">
|
||||
<span className="mp-repo-dot" style={{ background: '#3b82f6' }} />
|
||||
<span>
|
||||
{translate(
|
||||
'auto.components.mobile.slides.HomeSlide.d33d7a9c29',
|
||||
// Why: plain spaces (not ) — React text nodes render HTML entities literally.
|
||||
'orca · feat/mobile-page'
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mp-chevron-right">
|
||||
<ChevronIcon />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mp-section-label" style={{ marginTop: 10 }}>
|
||||
{translate('auto.components.mobile.slides.HomeSlide.a4c3f7b7aa', 'Tasks')}
|
||||
</div>
|
||||
<div className="mp-task-home-card">
|
||||
<div className="mp-task-home-icon">
|
||||
<ListTodoIcon />
|
||||
</div>
|
||||
<div className="mp-host-main">
|
||||
<div className="mp-task-home-title">
|
||||
{translate('auto.components.mobile.slides.HomeSlide.a4c3f7b7aa', 'Tasks')}
|
||||
</div>
|
||||
<div className="mp-task-home-subtitle">
|
||||
{translate('auto.components.mobile.slides.HomeSlide.d047197480', 'GitHub · Linear')}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="mp-task-home-providers"
|
||||
aria-label={translate(
|
||||
'auto.components.mobile.slides.HomeSlide.0bad5b07c8',
|
||||
'GitHub and Linear'
|
||||
)}
|
||||
>
|
||||
<div className="mp-task-home-provider-button">
|
||||
<GithubIcon />
|
||||
</div>
|
||||
<div className="mp-task-home-provider-button">
|
||||
<LinearIcon />
|
||||
</div>
|
||||
</div>
|
||||
<div className="mp-chevron-right">
|
||||
<ChevronIcon />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mp-section-label" style={{ marginTop: 14 }}>
|
||||
{translate('auto.components.mobile.slides.HomeSlide.0b00c98506', 'Quick Actions')}
|
||||
</div>
|
||||
<div className="mp-quick-actions">
|
||||
<div className="mp-quick-action">
|
||||
<div className="mp-quick-action-icon">
|
||||
<QrSmallIcon />
|
||||
</div>
|
||||
<div className="mp-quick-action-label">
|
||||
{translate('auto.components.mobile.slides.HomeSlide.4405f3c440', 'Pair Desktop')}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mp-quick-action">
|
||||
<div className="mp-quick-action-icon">
|
||||
<PlusIcon />
|
||||
</div>
|
||||
<div className="mp-quick-action-label">
|
||||
{translate('auto.components.mobile.slides.HomeSlide.e27fdaee51', 'New Workspace')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mp-section-label" style={{ marginTop: 14 }}>
|
||||
{translate('auto.components.mobile.slides.HomeSlide.8a350a4784', 'Account usage')}
|
||||
</div>
|
||||
<div className="mp-accounts-card">
|
||||
<AccountRow
|
||||
icon={<ClaudeIcon size={18} />}
|
||||
email="claude@stably.ai"
|
||||
sessionPct={42}
|
||||
weekPct={18}
|
||||
/>
|
||||
<AccountRow
|
||||
icon={<OpenAIIcon size={18} />}
|
||||
email="codex@stably.ai"
|
||||
sessionPct={67}
|
||||
weekPct={31}
|
||||
/>
|
||||
<div className="mp-home-bar-button is-primary">
|
||||
<PlusIcon />
|
||||
<span>
|
||||
{translate('auto.components.mobile.slides.HomeSlide.d08ee9c466', 'New workspace')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function Stat({ value, label }: { value: string; label: string }): React.JSX.Element {
|
||||
function JumpBackInRow({
|
||||
indicator,
|
||||
name,
|
||||
status,
|
||||
desktop,
|
||||
time
|
||||
}: JumpBackInRowProps): React.JSX.Element {
|
||||
return (
|
||||
<div className="mp-stat-card">
|
||||
<div className="mp-stat-value">{value}</div>
|
||||
<div className="mp-stat-label">{label}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function AccountRow({
|
||||
icon,
|
||||
email,
|
||||
sessionPct,
|
||||
weekPct
|
||||
}: {
|
||||
icon: React.ReactNode
|
||||
email: string
|
||||
sessionPct: number
|
||||
weekPct: number
|
||||
}): React.JSX.Element {
|
||||
return (
|
||||
<div className="mp-accounts-row">
|
||||
<div className="mp-accounts-icon">{icon}</div>
|
||||
<div className="mp-accounts-info">
|
||||
<div className="mp-accounts-email">{email}</div>
|
||||
<div className="mp-accounts-bars">
|
||||
<UsageBar
|
||||
label={translate('auto.components.mobile.slides.HomeSlide.a3d5476811', '5h')}
|
||||
pct={sessionPct}
|
||||
/>
|
||||
<UsageBar
|
||||
label={translate('auto.components.mobile.slides.HomeSlide.a7d9e2c44d', '7d')}
|
||||
pct={weekPct}
|
||||
/>
|
||||
<div className="mp-home-row">
|
||||
<div className="mp-home-status">
|
||||
{indicator === 'working' ? (
|
||||
<div className="mp-wl-spinner" />
|
||||
) : (
|
||||
<div className={cn('mp-wl-dot', AGENT_DOT[indicator])} />
|
||||
)}
|
||||
</div>
|
||||
<div className="mp-home-text">
|
||||
<div className="mp-home-title">{name}</div>
|
||||
<div className="mp-home-subtitle">
|
||||
<span className={STATUS_TONE[indicator]}>{status}</span> · {desktop}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function UsageBar({ label, pct }: { label: string; pct: number }): React.JSX.Element {
|
||||
return (
|
||||
<div className="mp-usage-bar">
|
||||
<div className="mp-usage-bar-label">{label}</div>
|
||||
<div className="mp-usage-bar-track">
|
||||
<div className="mp-usage-bar-fill" style={{ width: `${pct}%` }} />
|
||||
<div className="mp-home-trailing">
|
||||
{time}
|
||||
<ChevronIcon />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
@@ -266,16 +191,6 @@ function SettingsIcon(): React.JSX.Element {
|
||||
)
|
||||
}
|
||||
|
||||
function DesktopIcon(): React.JSX.Element {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
||||
<rect x="2" y="3" width="20" height="14" rx="2" />
|
||||
<path d="M8 21h8" />
|
||||
<path d="M12 17v4" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
function ChevronIcon(): React.JSX.Element {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
||||
@@ -284,15 +199,6 @@ function ChevronIcon(): React.JSX.Element {
|
||||
)
|
||||
}
|
||||
|
||||
function ResumeIcon(): React.JSX.Element {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
||||
<path d="m4 17 6-6-6-6" />
|
||||
<path d="M12 19h8" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
function ListTodoIcon(): React.JSX.Element {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
||||
@@ -305,37 +211,6 @@ function ListTodoIcon(): React.JSX.Element {
|
||||
)
|
||||
}
|
||||
|
||||
function GithubIcon(): React.JSX.Element {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
||||
<path d="M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 2 5 2 5 2c-.3 1.15-.3 2.35 0 3.5A5.4 5.4 0 0 0 4 9c0 3.5 3 5.5 6 5.5-.39.49-.68 1.05-.85 1.65-.17.6-.22 1.23-.15 1.85v4" />
|
||||
<path d="M9 18c-4.51 2-5-2-7-2" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
function LinearIcon(): React.JSX.Element {
|
||||
return (
|
||||
<svg viewBox="0 0 100 100" fill="currentColor" aria-hidden>
|
||||
<path d="M1.225 61.523c-.187-.738.708-1.235 1.246-.697l36.703 36.703c.538.538.041 1.433-.697 1.246C20.6 94.16 5.84 79.4 1.225 61.523ZM.002 46.811a.997.997 0 0 0 .291.749l52.147 52.147a.998.998 0 0 0 .749.291 50.328 50.328 0 0 0 9.235-1.119c.667-.149.904-.972.422-1.454L1.575 37.154c-.482-.482-1.305-.245-1.454.422A50.328 50.328 0 0 0 .002 46.81Zm4.528-18.34a.998.998 0 0 0 .195 1.144l64.66 64.66a.998.998 0 0 0 1.144.195 50.45 50.45 0 0 0 5.913-3.46.999.999 0 0 0 .14-1.518L9.51 22.418a.999.999 0 0 0-1.518.14 50.45 50.45 0 0 0-3.46 5.913Zm10.435-13.075a.999.999 0 0 0 .002 1.41l68.226 68.226a.999.999 0 0 0 1.41.002c19.292-19.477 19.234-50.97-.176-70.378-19.410-19.410-50.901-19.468-70.378-.176-1.061 1.044.916 1.916.916 1.916Z" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
function QrSmallIcon(): React.JSX.Element {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
||||
<rect x="3" y="3" width="7" height="7" rx="1" />
|
||||
<rect x="14" y="3" width="7" height="7" rx="1" />
|
||||
<rect x="3" y="14" width="7" height="7" rx="1" />
|
||||
<rect x="14" y="14" width="3" height="3" />
|
||||
<rect x="18" y="14" width="3" height="3" />
|
||||
<rect x="14" y="18" width="3" height="3" />
|
||||
<rect x="18" y="18" width="3" height="3" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
function PlusIcon(): React.JSX.Element {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
||||
|
||||
@@ -15093,27 +15093,10 @@
|
||||
},
|
||||
"slides": {
|
||||
"HomeSlide": {
|
||||
"a7d9e2c44d": "7 Tage",
|
||||
"a3d5476811": "5 h",
|
||||
"8a350a4784": "Kontonutzung",
|
||||
"e27fdaee51": "Arbeitsbereich erstellen",
|
||||
"4405f3c440": "Desktop koppeln",
|
||||
"0b00c98506": "Schnellaktionen",
|
||||
"0bad5b07c8": "GitHub und Linear",
|
||||
"d047197480": "GitHub · Linear",
|
||||
"a4c3f7b7aa": "Aufgaben",
|
||||
"d33d7a9c29": "orca · feat/mobile-page",
|
||||
"25d6e8a491": "feat/mobile-page",
|
||||
"c791677f2f": "Fortsetzen",
|
||||
"cf3f98fa3f": "Getrennt",
|
||||
"091355da3d": "M1 Mini · zu Hause",
|
||||
"0bc1881bc4": "Verbunden · 40 Worktrees · 5 aktiv",
|
||||
"19c212e25e": "MacBook Pro",
|
||||
"2f1a1d10c4": "Desktop-Computer",
|
||||
"156db8a68a": "Erstellte PRs",
|
||||
"4a40af029b": "Agentenzeit",
|
||||
"00a6903322": "Gestartete Agenten",
|
||||
"c0e2e9dcd9": "Willkommen zurück",
|
||||
"af761a0c0d": "Einstellungen",
|
||||
"5d94e8ddcc": "Orca"
|
||||
},
|
||||
|
||||
@@ -15337,29 +15337,25 @@
|
||||
},
|
||||
"slides": {
|
||||
"HomeSlide": {
|
||||
"a7d9e2c44d": "7d",
|
||||
"a3d5476811": "5h",
|
||||
"8a350a4784": "Account usage",
|
||||
"e27fdaee51": "New Workspace",
|
||||
"4405f3c440": "Pair Desktop",
|
||||
"0b00c98506": "Quick Actions",
|
||||
"0bad5b07c8": "GitHub and Linear",
|
||||
"d047197480": "GitHub · Linear",
|
||||
"a4c3f7b7aa": "Tasks",
|
||||
"d33d7a9c29": "orca · feat/mobile-page",
|
||||
"25d6e8a491": "feat/mobile-page",
|
||||
"c791677f2f": "Resume",
|
||||
"cf3f98fa3f": "Disconnected",
|
||||
"091355da3d": "M1 Mini · home",
|
||||
"0bc1881bc4": "Connected · 40 worktrees · 5 active",
|
||||
"19c212e25e": "MacBook Pro",
|
||||
"2f1a1d10c4": "Desktops",
|
||||
"156db8a68a": "PRs created",
|
||||
"4a40af029b": "Agent time",
|
||||
"00a6903322": "Agents spawned",
|
||||
"c0e2e9dcd9": "Welcome back",
|
||||
"af761a0c0d": "Settings",
|
||||
"5d94e8ddcc": "Orca"
|
||||
"5d94e8ddcc": "Orca",
|
||||
"dc1f3f5535": "Jump back in",
|
||||
"c2a0215163": "Waiting for input",
|
||||
"0c14dac908": "2m",
|
||||
"77c0dae863": "Done",
|
||||
"3166f865af": "14m",
|
||||
"609517273b": "Working",
|
||||
"005afbad3d": "now",
|
||||
"e986b282af": "Last opened",
|
||||
"8a3ebdbdac": "1h",
|
||||
"04a94cadee": "40 worktrees · 5 active",
|
||||
"b5308d5e2c": "M1 Mini",
|
||||
"aefd110ea5": "Pair a desktop",
|
||||
"d08ee9c466": "New workspace"
|
||||
},
|
||||
"TerminalSlide": {
|
||||
"0bb39f8fe6": "Send",
|
||||
|
||||
@@ -14951,27 +14951,10 @@
|
||||
},
|
||||
"slides": {
|
||||
"HomeSlide": {
|
||||
"a7d9e2c44d": "7d",
|
||||
"a3d5476811": "5h",
|
||||
"8a350a4784": "Uso de la cuenta",
|
||||
"e27fdaee51": "Nuevo espacio de trabajo",
|
||||
"4405f3c440": "Emparejar escritorio",
|
||||
"0b00c98506": "Acciones rápidas",
|
||||
"0bad5b07c8": "GitHub y Linear",
|
||||
"d047197480": "GitHub · Linear",
|
||||
"a4c3f7b7aa": "Tareas",
|
||||
"d33d7a9c29": "orca · feat/mobile-page",
|
||||
"25d6e8a491": "feat/mobile-page",
|
||||
"c791677f2f": "Reanudar",
|
||||
"cf3f98fa3f": "Desconectado",
|
||||
"091355da3d": "M1 Mini · inicio",
|
||||
"0bc1881bc4": "Conectado · 40 worktrees · 5 activos",
|
||||
"19c212e25e": "MacBook Pro",
|
||||
"2f1a1d10c4": "Escritorios",
|
||||
"156db8a68a": "PRs creados",
|
||||
"4a40af029b": "Tiempo de agentes",
|
||||
"00a6903322": "Agentes iniciados",
|
||||
"c0e2e9dcd9": "Bienvenido de nuevo",
|
||||
"af761a0c0d": "Ajustes",
|
||||
"5d94e8ddcc": "Orca"
|
||||
},
|
||||
|
||||
@@ -15175,27 +15175,10 @@
|
||||
},
|
||||
"slides": {
|
||||
"HomeSlide": {
|
||||
"a7d9e2c44d": "7 j",
|
||||
"a3d5476811": "5 h",
|
||||
"8a350a4784": "Utilisation du compte",
|
||||
"e27fdaee51": "Nouvel espace de travail",
|
||||
"4405f3c440": "Appairer l'ordinateur",
|
||||
"0b00c98506": "Actions rapides",
|
||||
"0bad5b07c8": "GitHub et Linear",
|
||||
"d047197480": "GitHub · Linear",
|
||||
"a4c3f7b7aa": "Tâches",
|
||||
"d33d7a9c29": "orca · feat/mobile-page",
|
||||
"25d6e8a491": "feat/mobile-page",
|
||||
"c791677f2f": "Reprendre",
|
||||
"cf3f98fa3f": "Déconnecté",
|
||||
"091355da3d": "M1 Mini · accueil",
|
||||
"0bc1881bc4": "Connecté · 40 worktrees · 5 actifs",
|
||||
"19c212e25e": "MacBook Pro",
|
||||
"2f1a1d10c4": "Ordinateurs",
|
||||
"156db8a68a": "PRs créées",
|
||||
"4a40af029b": "Temps agent",
|
||||
"00a6903322": "Agents lancés",
|
||||
"c0e2e9dcd9": "Bon retour",
|
||||
"af761a0c0d": "Paramètres",
|
||||
"5d94e8ddcc": "Orca"
|
||||
},
|
||||
|
||||
@@ -14576,27 +14576,10 @@
|
||||
},
|
||||
"slides": {
|
||||
"HomeSlide": {
|
||||
"a7d9e2c44d": "7日",
|
||||
"a3d5476811": "5時間",
|
||||
"8a350a4784": "アカウントの使用状況",
|
||||
"e27fdaee51": "新規ワークスペース",
|
||||
"4405f3c440": "デスクトップのペアリング",
|
||||
"0b00c98506": "クイック操作",
|
||||
"0bad5b07c8": "GitHub と Linear",
|
||||
"d047197480": "GitHub · Linear",
|
||||
"a4c3f7b7aa": "タスク",
|
||||
"d33d7a9c29": "orca · feat/mobile-page",
|
||||
"25d6e8a491": "feat/mobile-page",
|
||||
"c791677f2f": "再開",
|
||||
"cf3f98fa3f": "切断済み",
|
||||
"091355da3d": "M1ミニ・ホーム",
|
||||
"0bc1881bc4": "接続済み · 40 ワークツリー · 5 アクティブ",
|
||||
"19c212e25e": "MacBook Pro",
|
||||
"2f1a1d10c4": "デスクトップ",
|
||||
"156db8a68a": "作成された PR",
|
||||
"4a40af029b": "Agent 時間",
|
||||
"00a6903322": "Agent が生成されました",
|
||||
"c0e2e9dcd9": "おかえり",
|
||||
"af761a0c0d": "設定",
|
||||
"5d94e8ddcc": "Orca"
|
||||
},
|
||||
|
||||
@@ -14576,27 +14576,10 @@
|
||||
},
|
||||
"slides": {
|
||||
"HomeSlide": {
|
||||
"a7d9e2c44d": "7일",
|
||||
"a3d5476811": "5시간",
|
||||
"8a350a4784": "계정 사용량",
|
||||
"e27fdaee51": "새 워크스페이스",
|
||||
"4405f3c440": "데스크탑 페어링",
|
||||
"0b00c98506": "빠른 작업",
|
||||
"0bad5b07c8": "GitHub 및 Linear",
|
||||
"d047197480": "GitHub · Linear",
|
||||
"a4c3f7b7aa": "작업",
|
||||
"d33d7a9c29": "orca · feat/mobile-page",
|
||||
"25d6e8a491": "feat/mobile-page",
|
||||
"c791677f2f": "재개",
|
||||
"cf3f98fa3f": "연결이 끊김",
|
||||
"091355da3d": "M1 미니 · 집",
|
||||
"0bc1881bc4": "연결됨 · 워크트리 40개 · 활성 5개",
|
||||
"19c212e25e": "맥북 프로",
|
||||
"2f1a1d10c4": "데스크탑",
|
||||
"156db8a68a": "생성된 PR",
|
||||
"4a40af029b": "Agent 시간",
|
||||
"00a6903322": "시작된 Agents",
|
||||
"c0e2e9dcd9": "다시 오신 것을 환영합니다",
|
||||
"af761a0c0d": "설정",
|
||||
"5d94e8ddcc": "Orca"
|
||||
},
|
||||
|
||||
@@ -16419,27 +16419,10 @@
|
||||
"mobile": {
|
||||
"slides": {
|
||||
"HomeSlide": {
|
||||
"a7d9e2c44d": "7d",
|
||||
"a3d5476811": "5h",
|
||||
"8a350a4784": "Uso da conta",
|
||||
"e27fdaee51": "Novo Espaço de Trabalho",
|
||||
"4405f3c440": "Parear Desktop",
|
||||
"0b00c98506": "Ações Rápidas",
|
||||
"0bad5b07c8": "GitHub e Linear",
|
||||
"d047197480": "GitHub · Linear",
|
||||
"a4c3f7b7aa": "Tarefas",
|
||||
"d33d7a9c29": "orca · feat/mobile-page",
|
||||
"25d6e8a491": "feat/mobile-page",
|
||||
"c791677f2f": "Retomar",
|
||||
"cf3f98fa3f": "Desconectado",
|
||||
"091355da3d": "M1 Mini · casa",
|
||||
"0bc1881bc4": "Conectado · 40 worktrees · 5 ativas",
|
||||
"19c212e25e": "MacBook Pro",
|
||||
"2f1a1d10c4": "Desktops",
|
||||
"156db8a68a": "PRs criados",
|
||||
"4a40af029b": "Tempo de agente",
|
||||
"00a6903322": "Agentes iniciados",
|
||||
"c0e2e9dcd9": "Bem-vindo de volta",
|
||||
"af761a0c0d": "Configurações",
|
||||
"5d94e8ddcc": "Orca"
|
||||
},
|
||||
|
||||
@@ -15563,27 +15563,10 @@
|
||||
},
|
||||
"slides": {
|
||||
"HomeSlide": {
|
||||
"a7d9e2c44d": "7 дн.",
|
||||
"a3d5476811": "5 ч",
|
||||
"8a350a4784": "Использование учётной записи",
|
||||
"e27fdaee51": "Новое рабочее пространство",
|
||||
"4405f3c440": "Подключить компьютер",
|
||||
"0b00c98506": "Быстрые действия",
|
||||
"0bad5b07c8": "GitHub и Linear",
|
||||
"d047197480": "GitHub · Linear",
|
||||
"a4c3f7b7aa": "Задачи",
|
||||
"d33d7a9c29": "orca · feat/mobile-page",
|
||||
"25d6e8a491": "feat/mobile-page",
|
||||
"c791677f2f": "Продолжить",
|
||||
"cf3f98fa3f": "Отключено",
|
||||
"091355da3d": "M1 Mini · home",
|
||||
"0bc1881bc4": "Подключено · рабочих деревьев: 40 · активных: 5",
|
||||
"19c212e25e": "MacBook Pro",
|
||||
"2f1a1d10c4": "Компьютеры",
|
||||
"156db8a68a": "Создано PR",
|
||||
"4a40af029b": "Время агентов",
|
||||
"00a6903322": "Запущено агентов",
|
||||
"c0e2e9dcd9": "С возвращением",
|
||||
"af761a0c0d": "Настройки",
|
||||
"5d94e8ddcc": "Orca"
|
||||
},
|
||||
|
||||
@@ -15079,27 +15079,10 @@
|
||||
},
|
||||
"slides": {
|
||||
"HomeSlide": {
|
||||
"a7d9e2c44d": "7g",
|
||||
"a3d5476811": "5 sa",
|
||||
"8a350a4784": "Hesap kullanımı",
|
||||
"e27fdaee51": "Yeni Çalışma Alanı",
|
||||
"4405f3c440": "Masaüstünü Eşleştir",
|
||||
"0b00c98506": "Hızlı İşlemler",
|
||||
"0bad5b07c8": "GitHub ve Linear",
|
||||
"d047197480": "GitHub · Linear",
|
||||
"a4c3f7b7aa": "Görevler",
|
||||
"d33d7a9c29": "orca · feat/mobile-page",
|
||||
"25d6e8a491": "feat/mobile-page",
|
||||
"c791677f2f": "Devam Et",
|
||||
"cf3f98fa3f": "Bağlantı Kesildi",
|
||||
"091355da3d": "M1 Mini · ev",
|
||||
"0bc1881bc4": "Bağlandı · 40 worktree · 5 etkin",
|
||||
"19c212e25e": "MacBook Pro",
|
||||
"2f1a1d10c4": "Masaüstleri",
|
||||
"156db8a68a": "Oluşturulan PR'lar",
|
||||
"4a40af029b": "Ajan süresi",
|
||||
"00a6903322": "Oluşturulan ajanlar",
|
||||
"c0e2e9dcd9": "Tekrar hoş geldiniz",
|
||||
"af761a0c0d": "Ayarlar",
|
||||
"5d94e8ddcc": "Orca"
|
||||
},
|
||||
|
||||
@@ -14881,27 +14881,10 @@
|
||||
},
|
||||
"slides": {
|
||||
"HomeSlide": {
|
||||
"a7d9e2c44d": "7 天",
|
||||
"a3d5476811": "5 小時",
|
||||
"8a350a4784": "帳戶用量",
|
||||
"e27fdaee51": "新工作區",
|
||||
"4405f3c440": "配對電腦",
|
||||
"0b00c98506": "快速操作",
|
||||
"0bad5b07c8": "GitHub 和 Linear",
|
||||
"d047197480": "GitHub · Linear",
|
||||
"a4c3f7b7aa": "任務",
|
||||
"d33d7a9c29": "orca · feat/mobile-page",
|
||||
"25d6e8a491": "feat/mobile-page",
|
||||
"c791677f2f": "恢復",
|
||||
"cf3f98fa3f": "已斷開連線",
|
||||
"091355da3d": "M1 Mini · 家",
|
||||
"0bc1881bc4": "已連線 · 40 個工作樹 · 5 個使用中",
|
||||
"19c212e25e": "MacBook Pro",
|
||||
"2f1a1d10c4": "電腦",
|
||||
"156db8a68a": "已建立 PR",
|
||||
"4a40af029b": "代理程式時間",
|
||||
"00a6903322": "已建立的代理程式",
|
||||
"c0e2e9dcd9": "歡迎回來",
|
||||
"af761a0c0d": "設定",
|
||||
"5d94e8ddcc": "Orca"
|
||||
},
|
||||
|
||||
@@ -14576,27 +14576,10 @@
|
||||
},
|
||||
"slides": {
|
||||
"HomeSlide": {
|
||||
"a7d9e2c44d": "7天",
|
||||
"a3d5476811": "5小时",
|
||||
"8a350a4784": "账户使用情况",
|
||||
"e27fdaee51": "新工作区",
|
||||
"4405f3c440": "配对桌面",
|
||||
"0b00c98506": "快捷操作",
|
||||
"0bad5b07c8": "GitHub 和 Linear",
|
||||
"d047197480": "GitHub · Linear",
|
||||
"a4c3f7b7aa": "任务",
|
||||
"d33d7a9c29": "Orca / mobile页面",
|
||||
"25d6e8a491": "mobile页面",
|
||||
"c791677f2f": "恢复",
|
||||
"cf3f98fa3f": "已断开连接",
|
||||
"091355da3d": "M1迷你·家",
|
||||
"0bc1881bc4": "已连接 · 40 个工作树 · 5 个活动树",
|
||||
"19c212e25e": "MacBook Pro",
|
||||
"2f1a1d10c4": "台式机",
|
||||
"156db8a68a": "已创建 PR",
|
||||
"4a40af029b": "智能体时间",
|
||||
"00a6903322": "已启动智能体",
|
||||
"c0e2e9dcd9": "欢迎回来",
|
||||
"af761a0c0d": "设置",
|
||||
"5d94e8ddcc": "Orca"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user