@nextclaw/ui 0.12.7 → 0.12.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +50 -0
- package/dist/assets/{ChannelsList-D8p4OlM6.js → ChannelsList-KIQIxluX.js} +1 -1
- package/dist/assets/{DocBrowser-Cse_F8Nn.js → DocBrowser-BMxf9CIK.js} +1 -1
- package/dist/assets/DocBrowser-CyDgAtO9.js +1 -0
- package/dist/assets/{DocBrowserContext-Bai1WU2H.js → DocBrowserContext-Ce28gRXt.js} +1 -1
- package/dist/assets/{LogoBadge-BdxMPc9v.js → LogoBadge-o92MOA2L.js} +1 -1
- package/dist/assets/{MarketplacePage-BbpAkllU.js → MarketplacePage-BySqkYDh.js} +1 -1
- package/dist/assets/MarketplacePage-C0olZaek.js +1 -0
- package/dist/assets/{McpMarketplacePage-CxPFOgxv.js → McpMarketplacePage-DqKaiXO9.js} +1 -1
- package/dist/assets/{ModelConfig-3GLqQ5GY.js → ModelConfig-IrmzoslW.js} +1 -1
- package/dist/assets/{ProviderScopedModelInput-BYNouw-i.js → ProviderScopedModelInput-CmTIzgI7.js} +1 -1
- package/dist/assets/{ProvidersList-BR1gJ4Dm.js → ProvidersList-8_Kalfwl.js} +1 -1
- package/dist/assets/{RemoteAccessPage-DyYVWsyK.js → RemoteAccessPage-CyQlSjPf.js} +1 -1
- package/dist/assets/RuntimeConfig-Bk0uYBhf.js +1 -0
- package/dist/assets/{SearchConfig-DTeJvp8m.js → SearchConfig-DNBR-UbE.js} +1 -1
- package/dist/assets/{SecretsConfig-CCYO6NcV.js → SecretsConfig-Ba1RPJaG.js} +1 -1
- package/dist/assets/{SessionsConfig-Du39vDgt.js → SessionsConfig-Doqp5ghH.js} +1 -1
- package/dist/assets/{app-query-client-Dr5d-K8d.js → app-query-client-DniXoIN5.js} +1 -1
- package/dist/assets/{book-open-Da4OEPqB.js → book-open-DocgeQtR.js} +1 -1
- package/dist/assets/chat-page-Bph8M5zo.js +58 -0
- package/dist/assets/chat-session-display-CoN3Wmn-.js +1 -0
- package/dist/assets/{chunk-JZWAC4HX-CoFVxHXV.js → chunk-JZWAC4HX-BvKvh1R8.js} +1 -1
- package/dist/assets/{client-CSk58DcF.js → client-CVqPF5ie.js} +1 -1
- package/dist/assets/{config-D8KzikVB.js → config-Bop2oB18.js} +1 -1
- package/dist/assets/{createLucideIcon-83gaZMtv.js → createLucideIcon-DVv8taGY.js} +1 -1
- package/dist/assets/desktop-update-config-1KBrqLBC.js +1 -0
- package/dist/assets/{dist-toEYs-MZ.js → dist-Da5Gm_pO.js} +1 -1
- package/dist/assets/{dist-aTmhMDVh.js → dist-DmAlInRu.js} +1 -1
- package/dist/assets/{external-link-QQ0TC6X4.js → external-link-DFjw3x1B.js} +1 -1
- package/dist/assets/{hash-DaFBEkmi.js → hash-DJtaCejM.js} +1 -1
- package/dist/assets/i18n-CwHZ-9vt.js +1 -0
- package/dist/assets/{index-CE4N7ItL.css → index-DafCdM4F.css} +1 -1
- package/dist/assets/{index-riX7Sg0_.js → index-DdksE6U3.js} +3 -3
- package/dist/assets/{infiniteQueryBehavior-BmHX_ayZ.js → infiniteQueryBehavior-DHSEQ3OH.js} +1 -1
- package/dist/assets/loader-circle-PsSP0H9n.js +1 -0
- package/dist/assets/{logos-Dzlz30M3.js → logos-DEFUIR12.js} +1 -1
- package/dist/assets/{page-layout-D2eRufRQ.js → page-layout-Da3i3r6G.js} +1 -1
- package/dist/assets/play-DBQbBxTA.js +1 -0
- package/dist/assets/plus-DUOVbsyQ.js +1 -0
- package/dist/assets/{popover-BSXxm5bj.js → popover-C_mWOFzI.js} +1 -1
- package/dist/assets/{refresh-ccw-B3zMtN-_.js → refresh-ccw-D6HkNtfz.js} +1 -1
- package/dist/assets/{refresh-cw-DlZkIHnJ.js → refresh-cw-DRcvRrnc.js} +1 -1
- package/dist/assets/rotate-cw-BmDKfXtH.js +1 -0
- package/dist/assets/{save-Us9fg4Sj.js → save-DHGmi2e9.js} +1 -1
- package/dist/assets/search-MChQRYR1.js +1 -0
- package/dist/assets/{security-config-BGWYwxNr.js → security-config-CbXfPZzr.js} +1 -1
- package/dist/assets/{select-DLYqySQK.js → select-Caud8QvU.js} +1 -1
- package/dist/assets/skeleton-B-4vRq_Z.js +1 -0
- package/dist/assets/{status-dot-DGayudyB.js → status-dot-DurKKSwA.js} +1 -1
- package/dist/assets/{switch-Dz2ScsKx.js → switch-0rmPBRKI.js} +1 -1
- package/dist/assets/{tabs-custom-CdKyjiGk.js → tabs-custom-5JLVL6v8.js} +1 -1
- package/dist/assets/{trash-2-Db-mZOZs.js → trash-2-C6caKPoz.js} +1 -1
- package/dist/assets/{use-infinite-scroll-loader-DBJX5hj0.js → use-infinite-scroll-loader-dwnaa_qi.js} +1 -1
- package/dist/assets/{useConfirmDialog-DL0a-oGC.js → useConfirmDialog-mMeWD_yo.js} +1 -1
- package/dist/assets/{useMutation-BdZm-9PL.js → useMutation-BmxxvCNf.js} +1 -1
- package/dist/assets/x-DuMhMATD.js +1 -0
- package/dist/index.html +20 -20
- package/package.json +6 -6
- package/src/api/runtime-control.ts +34 -0
- package/src/api/runtime-control.types.ts +58 -0
- package/src/api/types.ts +13 -0
- package/src/{App.test.tsx → app.test.tsx} +1 -1
- package/src/{App.tsx → app.tsx} +1 -1
- package/src/components/chat/ChatConversationPanel.test.tsx +78 -16
- package/src/components/chat/ChatSidebar.test.tsx +36 -7
- package/src/components/chat/ChatSidebar.tsx +19 -26
- package/src/components/chat/chat-child-session-panel.tsx +16 -8
- package/src/components/chat/chat-page-runtime.test.ts +1 -1
- package/src/components/chat/{ChatPage.tsx → chat-page.tsx} +1 -1
- package/src/components/chat/managers/chat-session-list.manager.test.ts +82 -31
- package/src/components/chat/managers/chat-session-list.manager.ts +79 -14
- package/src/components/chat/managers/chat-ui.manager.ts +2 -0
- package/src/components/chat/ncp/README.md +1 -1
- package/src/components/chat/ncp/ncp-chat-input.manager.ts +7 -1
- package/src/components/chat/ncp/ncp-chat-page-data.test.ts +1 -1
- package/src/components/chat/ncp/ncp-session-adapter.test.ts +5 -1
- package/src/components/chat/ncp/ncp-session-adapter.ts +12 -0
- package/src/components/chat/ncp/session-conversation/use-ncp-child-session-tabs-view.ts +4 -0
- package/src/components/chat/ncp/tests/ncp-chat-input.manager.test.ts +99 -0
- package/src/components/chat/stores/chat-session-list.store.ts +25 -54
- package/src/components/common/ProviderScopedModelInput.tsx +12 -2
- package/src/components/config/ModelConfig.test.tsx +108 -2
- package/src/components/config/RuntimeConfig.tsx +14 -6
- package/src/components/config/desktop-update-config.test.tsx +85 -0
- package/src/components/config/desktop-update-config.tsx +44 -3
- package/src/components/config/runtime-control-card.test.tsx +255 -0
- package/src/components/config/runtime-control-card.tsx +301 -0
- package/src/components/config/runtime-presence-card.test.tsx +154 -0
- package/src/components/config/runtime-presence-card.tsx +163 -0
- package/src/desktop/desktop-update.types.ts +25 -0
- package/src/desktop/managers/desktop-presence.manager.ts +91 -0
- package/src/desktop/managers/desktop-update.manager.ts +37 -1
- package/src/desktop/stores/desktop-presence.store.ts +18 -0
- package/src/desktop/stores/desktop-update.store.ts +7 -1
- package/src/hooks/use-runtime-control.ts +24 -0
- package/src/lib/desktop-update-labels.utils.ts +28 -2
- package/src/lib/i18n.runtime-control.ts +120 -0
- package/src/lib/i18n.ts +2 -4
- package/src/main.tsx +1 -1
- package/src/runtime-control/runtime-control.manager.ts +118 -0
- package/dist/assets/ChatPage-A45t1Rmf.js +0 -58
- package/dist/assets/DocBrowser-B2MpsnU9.js +0 -1
- package/dist/assets/MarketplacePage-BNZ3Jx5d.js +0 -1
- package/dist/assets/RuntimeConfig-ChdfK4Y_.js +0 -1
- package/dist/assets/chat-session-display-CAlPrnlV.js +0 -1
- package/dist/assets/desktop-update-config-CfoVwf-w.js +0 -1
- package/dist/assets/i18n-C3jb83S6.js +0 -1
- package/dist/assets/loader-circle-BjMg63eu.js +0 -1
- package/dist/assets/plus-CIXME2pD.js +0 -1
- package/dist/assets/search-B_Qr0f6C.js +0 -1
- package/dist/assets/skeleton-CYQJazv6.js +0 -1
- package/dist/assets/x-B8Tho_xC.js +0 -1
- /package/dist/assets/{config-hints-GSUMvmSo.js → config-hints-BZoDjXye.js} +0 -0
- /package/dist/assets/{config-layout-CgBMG7OL.js → config-layout-DmlGaay2.js} +0 -0
- /package/src/components/chat/ncp/{NcpChatPage.tsx → ncp-chat-page.tsx} +0 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
export const RUNTIME_CONTROL_LABELS: Record<string, { zh: string; en: string }> = {
|
|
2
|
+
runtimePageTitle: { zh: '路由与运行时', en: 'Routing & Runtime' },
|
|
3
|
+
runtimePageDescription: {
|
|
4
|
+
zh: '对齐 OpenClaw 的多 Agent 路由:绑定规则、Agent 池、私聊范围。',
|
|
5
|
+
en: 'Align multi-agent routing with OpenClaw: bindings, agent pool, and DM scope.'
|
|
6
|
+
},
|
|
7
|
+
runtimeLoading: { zh: '加载运行时配置中...', en: 'Loading runtime settings...' },
|
|
8
|
+
runtimeControlTitle: { zh: '服务管理', en: 'Service Management' },
|
|
9
|
+
runtimeControlDescription: {
|
|
10
|
+
zh: '明确当前服务状态,并在宿主允许的范围内执行启动、重启、停止或桌面应用重启。',
|
|
11
|
+
en: 'Make the current service state explicit and manage start, restart, stop, or full desktop app restart when the host allows it.'
|
|
12
|
+
},
|
|
13
|
+
runtimePresenceTitle: { zh: '运行形态与常驻', en: 'Presence & Lifecycle' },
|
|
14
|
+
runtimePresenceDescription: {
|
|
15
|
+
zh: '明确当前环境里“关闭窗口/页面”会发生什么,以及哪些后台能力由宿主层负责。',
|
|
16
|
+
en: 'Make the current environment explicit: what happens when the window or page closes, and which background behaviors belong to the host.'
|
|
17
|
+
},
|
|
18
|
+
runtimePresenceLoading: { zh: '正在读取常驻状态...', en: 'Loading presence settings...' },
|
|
19
|
+
runtimePresenceLoadFailed: { zh: '读取桌面端常驻状态失败', en: 'Failed to load desktop presence settings' },
|
|
20
|
+
runtimePresenceBehaviorLabel: { zh: '当前关闭行为', en: 'Current Close Behavior' },
|
|
21
|
+
runtimePresenceBehaviorBackground: { zh: '关闭窗口时隐藏到后台', en: 'Closing the window hides it to the background' },
|
|
22
|
+
runtimePresenceBehaviorQuit: { zh: '关闭窗口时退出应用', en: 'Closing the window quits the app' },
|
|
23
|
+
runtimePresenceCloseToBackground: { zh: '关闭窗口时继续在后台运行', en: 'Keep running in background when closing the window' },
|
|
24
|
+
runtimePresenceCloseToBackgroundHelp: {
|
|
25
|
+
zh: '开启后,关闭桌面窗口只会隐藏到托盘,不会停止本地 NextClaw 服务。',
|
|
26
|
+
en: 'When enabled, closing the desktop window hides it to the tray instead of stopping the local NextClaw service.'
|
|
27
|
+
},
|
|
28
|
+
runtimePresenceLaunchAtLogin: { zh: '登录系统时自动启动 NextClaw', en: 'Launch NextClaw at login' },
|
|
29
|
+
runtimePresenceLaunchAtLoginHelp: {
|
|
30
|
+
zh: '开启后,桌面端会在登录系统后自动启动,并默认保持后台运行。',
|
|
31
|
+
en: 'When enabled, the desktop app starts automatically after login and stays in the background by default.'
|
|
32
|
+
},
|
|
33
|
+
runtimePresenceLaunchAtLoginUnavailable: {
|
|
34
|
+
zh: '当前环境暂不支持在这里配置开机自启。',
|
|
35
|
+
en: 'Launch-at-login is not configurable in this environment yet.'
|
|
36
|
+
},
|
|
37
|
+
runtimePresenceSaved: { zh: '桌面端常驻设置已保存', en: 'Desktop presence settings saved' },
|
|
38
|
+
runtimePresenceSaveFailed: { zh: '保存桌面端常驻设置失败', en: 'Failed to save desktop presence settings' },
|
|
39
|
+
runtimePresenceManagedLocalTitle: { zh: '浏览器只是本地服务控制面', en: 'The browser is only a control surface for the local service' },
|
|
40
|
+
runtimePresenceManagedLocalDescription: {
|
|
41
|
+
zh: '关闭浏览器标签页不会停止本地 NextClaw 服务。服务生命周期由本地受管服务负责,而不是由页面生命周期决定。',
|
|
42
|
+
en: 'Closing the browser tab does not stop the local NextClaw service. The local managed service owns the lifecycle, not the page.'
|
|
43
|
+
},
|
|
44
|
+
runtimePresenceSelfHostedTitle: { zh: '页面不拥有自托管服务生命周期', en: 'The page does not own the self-hosted service lifecycle' },
|
|
45
|
+
runtimePresenceSelfHostedDescription: {
|
|
46
|
+
zh: '关闭浏览器页面不会影响自托管实例。服务启停和开机常驻应由宿主环境或部署层管理。',
|
|
47
|
+
en: 'Closing the browser page does not affect the self-hosted instance. Service start, stop, and auto-start belong to the host environment or deployment layer.'
|
|
48
|
+
},
|
|
49
|
+
runtimePresenceSharedTitle: { zh: '共享网页端只暴露状态,不暴露进程控制', en: 'Shared web exposes service state, not process ownership' },
|
|
50
|
+
runtimePresenceSharedDescription: {
|
|
51
|
+
zh: '关闭页面只会结束当前连接或会话,不会影响共享服务本体。服务生命周期由平台托管层负责。',
|
|
52
|
+
en: 'Closing the page only ends the current connection or session, not the shared service itself. The platform owns the lifecycle.'
|
|
53
|
+
},
|
|
54
|
+
runtimeControlLoading: { zh: '读取运行时控制能力中...', en: 'Loading runtime control capabilities...' },
|
|
55
|
+
runtimeControlLoadFailed: { zh: '读取运行时控制状态失败', en: 'Failed to load runtime control state' },
|
|
56
|
+
runtimeControlServiceRunning: { zh: '服务运行中', en: 'Service running' },
|
|
57
|
+
runtimeControlServiceStopped: { zh: '服务已停止', en: 'Service stopped' },
|
|
58
|
+
runtimeControlServiceStarting: { zh: '正在启动服务', en: 'Starting service' },
|
|
59
|
+
runtimeControlServiceStopping: { zh: '正在停止服务', en: 'Stopping service' },
|
|
60
|
+
runtimeControlServiceRestarting: { zh: '正在重启服务', en: 'Restarting service' },
|
|
61
|
+
runtimeControlServiceUnknown: { zh: '服务状态未知', en: 'Service state unknown' },
|
|
62
|
+
runtimeControlHealthy: { zh: '运行时正常', en: 'Runtime healthy' },
|
|
63
|
+
runtimeControlStartingService: { zh: '正在启动服务', en: 'Starting service' },
|
|
64
|
+
runtimeControlRestartingService: { zh: '正在重启服务', en: 'Restarting service' },
|
|
65
|
+
runtimeControlStoppingService: { zh: '正在停止服务', en: 'Stopping service' },
|
|
66
|
+
runtimeControlRestartingApp: { zh: '正在重启应用', en: 'Restarting app' },
|
|
67
|
+
runtimeControlRecovering: { zh: '正在恢复连接', en: 'Recovering connection' },
|
|
68
|
+
runtimeControlFailed: { zh: '重启失败', en: 'Restart failed' },
|
|
69
|
+
runtimeControlUnavailable: { zh: '当前不可用', en: 'Currently unavailable' },
|
|
70
|
+
runtimeControlEnvironmentDesktop: { zh: '桌面端内嵌运行时', en: 'Desktop embedded runtime' },
|
|
71
|
+
runtimeControlEnvironmentManagedService: { zh: '本地托管服务', en: 'Managed local service' },
|
|
72
|
+
runtimeControlEnvironmentSelfHosted: { zh: '自托管网页端', en: 'Self-hosted web' },
|
|
73
|
+
runtimeControlEnvironmentSharedWeb: { zh: '共享网页端', en: 'Shared web' },
|
|
74
|
+
runtimeControlStartService: { zh: '启动服务', en: 'Start Service' },
|
|
75
|
+
runtimeControlRestartService: { zh: '重启服务', en: 'Restart Service' },
|
|
76
|
+
runtimeControlStopService: { zh: '停止服务', en: 'Stop Service' },
|
|
77
|
+
runtimeControlRestartApp: { zh: '重启应用', en: 'Restart App' },
|
|
78
|
+
runtimeControlStartingServiceHelp: {
|
|
79
|
+
zh: '正在启动 NextClaw 服务,页面可能会在服务恢复后重新连接。',
|
|
80
|
+
en: 'Starting the NextClaw service. The page may reconnect after the service becomes available.'
|
|
81
|
+
},
|
|
82
|
+
runtimeControlRestartingServiceHelp: {
|
|
83
|
+
zh: '正在重启 NextClaw 服务,页面可能会短暂断开。',
|
|
84
|
+
en: 'Restarting the NextClaw service. The page may disconnect briefly.'
|
|
85
|
+
},
|
|
86
|
+
runtimeControlStoppingServiceHelp: {
|
|
87
|
+
zh: '正在停止 NextClaw 服务,当前页面会很快断开。',
|
|
88
|
+
en: 'Stopping the NextClaw service. This page will disconnect shortly.'
|
|
89
|
+
},
|
|
90
|
+
runtimeControlRestartingAppHelp: {
|
|
91
|
+
zh: '正在重新启动桌面应用,当前窗口会短暂关闭并立即拉起。',
|
|
92
|
+
en: 'Restarting the desktop app. The current window will close briefly and relaunch.'
|
|
93
|
+
},
|
|
94
|
+
runtimeControlRecoveringHelp: {
|
|
95
|
+
zh: '正在等待服务恢复连接...',
|
|
96
|
+
en: 'Waiting for the service to come back...'
|
|
97
|
+
},
|
|
98
|
+
runtimeControlRecovered: { zh: '运行时已恢复连接', en: 'Runtime connection restored' },
|
|
99
|
+
runtimeControlActionFailed: { zh: '服务管理动作失败', en: 'Service management action failed' },
|
|
100
|
+
runtimeControlRestartAppConfirm: {
|
|
101
|
+
zh: '这会重启整个 NextClaw 桌面应用,并中断当前窗口。确定继续吗?',
|
|
102
|
+
en: 'This restarts the entire NextClaw desktop app and interrupts the current window. Continue?'
|
|
103
|
+
},
|
|
104
|
+
runtimeControlStopServiceConfirm: {
|
|
105
|
+
zh: '这会停止当前 NextClaw 服务,当前页面会立即断开。确定继续吗?',
|
|
106
|
+
en: 'This stops the current NextClaw service and disconnects the page immediately. Continue?'
|
|
107
|
+
},
|
|
108
|
+
runtimeRestartAppUnavailable: {
|
|
109
|
+
zh: '当前环境不支持从前端重启整个应用。',
|
|
110
|
+
en: 'This environment does not support restarting the entire app from the UI.'
|
|
111
|
+
},
|
|
112
|
+
runtimeControlDesktopServiceHint: {
|
|
113
|
+
zh: '桌面端由 Launcher 持有应用生命周期;普通用户无需单独停止内嵌服务。',
|
|
114
|
+
en: 'The desktop launcher owns the app lifecycle, so end users do not stop the embedded service separately.'
|
|
115
|
+
},
|
|
116
|
+
runtimeRecoveryTimedOut: {
|
|
117
|
+
zh: '等待运行时恢复超时,请稍后重试或查看日志。',
|
|
118
|
+
en: 'Timed out waiting for the runtime to recover. Try again or inspect the logs.'
|
|
119
|
+
}
|
|
120
|
+
};
|
package/src/lib/i18n.ts
CHANGED
|
@@ -16,6 +16,7 @@ import { DESKTOP_UPDATE_LABELS } from './desktop-update-labels.utils';
|
|
|
16
16
|
import { MARKETPLACE_LABELS } from './i18n.marketplace';
|
|
17
17
|
import { PATH_PICKER_LABELS } from './i18n-runtime/i18n.path-picker';
|
|
18
18
|
import { REMOTE_LABELS } from './i18n.remote';
|
|
19
|
+
import { RUNTIME_CONTROL_LABELS } from './i18n.runtime-control';
|
|
19
20
|
import { SEARCH_LABELS } from './i18n.search';
|
|
20
21
|
export type { I18nLanguage };
|
|
21
22
|
export { getLanguage, getLocale, initializeI18n, LANGUAGE_OPTIONS, resolveInitialLanguage, setLanguage, subscribeLanguageChange };
|
|
@@ -301,10 +302,7 @@ export const LABELS: Record<string, { zh: string; en: string }> = {
|
|
|
301
302
|
authRetryStatus: { zh: '重试', en: 'Retry' },
|
|
302
303
|
authStatusLoadFailed: { zh: '无法获取认证状态,请检查 UI 服务是否正常。', en: 'Failed to load authentication status. Check whether the UI server is healthy.' },
|
|
303
304
|
|
|
304
|
-
|
|
305
|
-
runtimePageTitle: { zh: '路由与运行时', en: 'Routing & Runtime' },
|
|
306
|
-
runtimePageDescription: { zh: '对齐 OpenClaw 的多 Agent 路由:绑定规则、Agent 池、私聊范围。', en: 'Align multi-agent routing with OpenClaw: bindings, agent pool, and DM scope.' },
|
|
307
|
-
runtimeLoading: { zh: '加载运行时配置中...', en: 'Loading runtime settings...' },
|
|
305
|
+
...RUNTIME_CONTROL_LABELS,
|
|
308
306
|
authSecurityTitle: { zh: 'Security', en: 'Security' },
|
|
309
307
|
authSecurityDescription: {
|
|
310
308
|
zh: '保持本机控制台默认简单;只有在你需要远程暴露时,再给 UI 加一层登录门。',
|
package/src/main.tsx
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { StrictMode } from 'react';
|
|
2
2
|
import { createRoot } from 'react-dom/client';
|
|
3
3
|
import { BrowserRouter } from 'react-router-dom';
|
|
4
|
-
import App from './
|
|
4
|
+
import App from './app';
|
|
5
5
|
import { I18nProvider } from '@/components/providers/I18nProvider';
|
|
6
6
|
import { ThemeProvider } from '@/components/providers/ThemeProvider';
|
|
7
7
|
import './index.css';
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { fetchRuntimeControl, restartRuntimeService, startRuntimeService, stopRuntimeService } from '@/api/runtime-control';
|
|
2
|
+
import type {
|
|
3
|
+
RuntimeControlAction,
|
|
4
|
+
RuntimeControlActionResult,
|
|
5
|
+
RuntimeControlView
|
|
6
|
+
} from '@/api/runtime-control.types';
|
|
7
|
+
import type { NextClawDesktopBridge } from '@/desktop/desktop-update.types';
|
|
8
|
+
import { t } from '@/lib/i18n';
|
|
9
|
+
|
|
10
|
+
type RecoveryWaitOptions = {
|
|
11
|
+
timeoutMs?: number;
|
|
12
|
+
pollIntervalMs?: number;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export class RuntimeControlManager {
|
|
16
|
+
getControl = async (): Promise<RuntimeControlView> => {
|
|
17
|
+
return this.decorateForCurrentEnvironment(await fetchRuntimeControl());
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
controlService = async (action: RuntimeControlAction): Promise<RuntimeControlActionResult> => {
|
|
21
|
+
const desktopBridge = this.getDesktopBridge();
|
|
22
|
+
if (action === 'restart-service' && desktopBridge && typeof desktopBridge.restartService === 'function') {
|
|
23
|
+
const result = await desktopBridge.restartService();
|
|
24
|
+
return {
|
|
25
|
+
accepted: result.accepted,
|
|
26
|
+
action: 'restart-service',
|
|
27
|
+
lifecycle: result.lifecycle,
|
|
28
|
+
message: result.message
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
if (action === 'start-service') {
|
|
32
|
+
return await startRuntimeService();
|
|
33
|
+
}
|
|
34
|
+
if (action === 'stop-service') {
|
|
35
|
+
return await stopRuntimeService();
|
|
36
|
+
}
|
|
37
|
+
return await restartRuntimeService();
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
restartApp = async (): Promise<RuntimeControlActionResult> => {
|
|
41
|
+
const desktopBridge = this.getDesktopBridge();
|
|
42
|
+
if (!desktopBridge || typeof desktopBridge.restartApp !== 'function') {
|
|
43
|
+
throw new Error(t('runtimeRestartAppUnavailable'));
|
|
44
|
+
}
|
|
45
|
+
const result = await desktopBridge.restartApp();
|
|
46
|
+
return {
|
|
47
|
+
accepted: result.accepted,
|
|
48
|
+
action: 'restart-app',
|
|
49
|
+
lifecycle: result.lifecycle,
|
|
50
|
+
message: result.message
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
waitForRecovery = async (options: RecoveryWaitOptions = {}): Promise<RuntimeControlView> => {
|
|
55
|
+
const timeoutMs = options.timeoutMs ?? 25_000;
|
|
56
|
+
const pollIntervalMs = options.pollIntervalMs ?? 1_500;
|
|
57
|
+
const deadline = Date.now() + timeoutMs;
|
|
58
|
+
|
|
59
|
+
let lastError: unknown = null;
|
|
60
|
+
while (Date.now() < deadline) {
|
|
61
|
+
try {
|
|
62
|
+
return await this.getControl();
|
|
63
|
+
} catch (error) {
|
|
64
|
+
lastError = error;
|
|
65
|
+
await this.sleep(pollIntervalMs);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
throw lastError instanceof Error
|
|
70
|
+
? lastError
|
|
71
|
+
: new Error(t('runtimeRecoveryTimedOut'));
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
decorateForCurrentEnvironment = (view: RuntimeControlView): RuntimeControlView => {
|
|
75
|
+
const desktopBridge = this.getDesktopBridge();
|
|
76
|
+
if (!desktopBridge || typeof desktopBridge.restartApp !== 'function') {
|
|
77
|
+
return view;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
...view,
|
|
82
|
+
environment: 'desktop-embedded',
|
|
83
|
+
serviceState: 'running',
|
|
84
|
+
canStartService: {
|
|
85
|
+
available: false,
|
|
86
|
+
requiresConfirmation: false,
|
|
87
|
+
impact: 'none'
|
|
88
|
+
},
|
|
89
|
+
canStopService: {
|
|
90
|
+
available: false,
|
|
91
|
+
requiresConfirmation: true,
|
|
92
|
+
impact: 'brief-ui-disconnect'
|
|
93
|
+
},
|
|
94
|
+
canRestartApp: {
|
|
95
|
+
available: true,
|
|
96
|
+
requiresConfirmation: true,
|
|
97
|
+
impact: 'full-app-relaunch'
|
|
98
|
+
},
|
|
99
|
+
ownerLabel: t('runtimeControlEnvironmentDesktop'),
|
|
100
|
+
managementHint: t('runtimeControlDesktopServiceHint')
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
private getDesktopBridge = (): NextClawDesktopBridge | null => {
|
|
105
|
+
if (typeof window === 'undefined') {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
return window.nextclawDesktop ?? null;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
private sleep = async (ms: number): Promise<void> => {
|
|
112
|
+
await new Promise<void>((resolve) => {
|
|
113
|
+
window.setTimeout(resolve, ms);
|
|
114
|
+
});
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export const runtimeControlManager = new RuntimeControlManager();
|