Repository navigation
feat(wework): guide device version upgrades - #1319
Conversation
Task: o8GuHjOIZ7 引导用户升级支持wework版本
📝 WalkthroughWalkthroughThis PR adds device executor version compatibility checking and a complete device upgrade feature. It introduces version gating for WeWork, type contracts for device events, a new DeviceStatusPrompt UI component, upgrade state orchestration in WorkbenchProvider, and integrates the upgrade flow through layouts and project creation dialogs with internationalization support. ChangesDevice Upgrade Feature
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
wework/src/components/chat/ChatInput.test.tsx (1)
70-82:⚠️ Potential issue | 🟠 Major | ⚡ Quick winRemove the duplicate
deviceskey in the returned object.
devicesis declared twice in the same object literal, which triggers Biomelint/suspicious/noDuplicateObjectKeysand can fail lint in CI.💡 Suggested patch
return { projects: [], - devices, currentProjectId: undefined, currentStandaloneDeviceId: null, executionMode: 'current_workspace', executionModeLocked: false, onSelectProject: vi.fn(), onSelectStandaloneDevice: vi.fn(), onExecutionModeChange: vi.fn(), ...overrides, devices, }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@wework/src/components/chat/ChatInput.test.tsx` around lines 70 - 82, The returned object in the test factory contains a duplicate devices key which triggers the lint rule; remove the second occurrence of "devices" from the object literal in ChatInput.test.tsx (the object returned in the test setup/factory), leaving a single "devices" entry and keeping the ...overrides spread as-is so overrides still apply correctly.Source: Linters/SAST tools
🧹 Nitpick comments (11)
wework/src/features/workbench/WorkbenchProvider.tsx (4)
778-781: ⚡ Quick winInternationalize error message.
The error message is hardcoded in Chinese. Like the status messages, this should use the i18n system for consistency with the rest of the upgrade UI (per PR objectives).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@wework/src/features/workbench/WorkbenchProvider.tsx` around lines 778 - 781, Replace the hardcoded Chinese strings assigned to the local variable message (in the device.status !== 'online' ? ... : ... conditional) with i18n translation keys using the same translation helper used elsewhere (e.g., the t function from useTranslation or i18n.t), import/use that helper in WorkbenchProvider.tsx if not already present, and update the translation resource files with appropriate keys and Chinese/other locales; ensure the conditional becomes something like t('workbench.device_offline') and t('workbench.device_busy') so it matches existing status message patterns.
1644-1666: ⚡ Quick winInternationalize version-check error messages.
Lines 1651 and 1662 contain hardcoded Chinese error messages for version gating. These should use the i18n system added in this PR for consistency with other device upgrade UI.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@wework/src/features/workbench/WorkbenchProvider.tsx` around lines 1644 - 1666, Replace the two hardcoded Chinese error messages passed to dispatch({type: 'error_set', error: ...}) with i18n lookups that include placeholders for dynamic values; for the device-specific message use the display name from getWorkbenchDeviceDisplayName and WEWORK_MIN_EXECUTOR_VERSION as params (e.g. i18n.t('workbench.deviceVersionLow', { deviceName, version })) and for the no-online-device case use a key like i18n.t('workbench.noCompatibleDeviceOnline', { version: WEWORK_MIN_EXECUTOR_VERSION }); update the imports/use of the translator in WorkbenchProvider (where state.currentProject/state.currentTask/dispatch and helpers like isWeWorkCompatibleDevice are used) so the dispatch error strings come from the i18n system rather than hardcoded text.
104-111: ⚡ Quick winConsider internationalizing hardcoded status messages.
The function returns hardcoded Chinese strings for user-facing upgrade status messages. While i18n files are added in this PR (per objectives), these specific strings bypass the i18n system. For consistency and maintainability, consider using the i18n system for all user-facing text.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@wework/src/features/workbench/WorkbenchProvider.tsx` around lines 104 - 111, getUpgradeStatusMessage currently returns hardcoded Chinese user-facing strings; update it to use the project's i18n translation function instead (e.g., i18n.t or the existing translate wrapper) so all messages go through the localization system. Keep the existing behavior for payload.message and payload.error (using error as fallback), but replace literals like '升级完成,正在检查设备版本', '升级失败', '设备正在执行任务,空闲后再升级', '设备已是最新版本', and '设备升级中' with calls to the appropriate translation keys (create/choose sensible keys like workbench.upgrade.success, workbench.upgrade.error, workbench.upgrade.busy, workbench.upgrade.skipped, workbench.upgrade.in_progress) and import/initialize the i18n helper in WorkbenchProvider.tsx so getUpgradeStatusMessage(DeviceUpgradeStatusPayload) returns translated strings.
803-803: 💤 Low valueConsider logging errors from background device refresh.
The
.catch(() => undefined)pattern silently suppresses errors from the device list refresh. While surfacing these to users may not be appropriate, logging them would aid debugging without affecting UX.💡 Optional improvement
- void refreshDevices().catch(() => undefined) + void refreshDevices().catch((err) => console.error('Device refresh failed after upgrade:', err))🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@wework/src/features/workbench/WorkbenchProvider.tsx` at line 803, The background call to refreshDevices() currently swallows errors with .catch(() => undefined); update this to log the error and still swallow it so UX is unchanged. Replace .catch(() => undefined) with something like .catch(err => { /* use existing logger if available, e.g. logger.error('refreshDevices failed', err) */ console.error('refreshDevices failed', err); }); and reference the refreshDevices call inside WorkbenchProvider (where void refreshDevices() is invoked).wework/src/components/layout/DeviceStatusPrompt.tsx (1)
49-341: 🏗️ Heavy liftBreak
DeviceStatusPromptinto smaller render helpers/hooks.The component has grown far beyond the preferred function-length threshold, making this branching UI harder to test and evolve safely.
As per coding guidelines,
**/*.{ts,tsx,js,jsx}: Function length: Max 50 lines per function (preferred).🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@wework/src/components/layout/DeviceStatusPrompt.tsx` around lines 49 - 341, DeviceStatusPrompt is too large—extract the large branching render blocks and side-effect logic into smaller helpers/hooks to keep the main function under ~50 lines; specifically move runUpgrades into a useDeviceUpgrade hook (or utility) and create small components or render helpers named e.g. NoDevicesPrompt, ActiveUpgradePrompt (uses activeUpgradeDevice and upgradingDevices), OutdatedActivePrompt (uses activeDevice, canRequestDeviceUpgrade, getBlockedReason), ActiveOfflinePrompt (uses isDeviceRunningTask), OutdatedDevicesPrompt (uses lowVersionUpgradeCandidates/lowVersionBlockedDevices), RequiresOnlinePrompt, and UpdateAvailablePrompt (uses updateCandidates and canRequestDeviceUpgrade); wire these helpers from DeviceStatusPrompt so the top-level function only computes claudeCodeDevices/compatibleDevices/updateCandidates/activeUpgradeDevice and returns the appropriate small component. Ensure each helper receives only the props it needs (device lists, handlers like onUpgradeDevice/onOpenCloudDeviceSettings, manualPending state from useDeviceUpgrade) and keep naming exact (DeviceStatusPrompt, runUpgrades, activeUpgradeDevice, updateCandidates, lowVersionUpgradeCandidates) to locate code easily.Source: Coding guidelines
wework/src/components/chat/ChatInput.test.tsx (1)
1-2812: 🏗️ Heavy liftSplit this test file into smaller modules.
This file is well above the repository size limit and is becoming hard to maintain as behaviors keep expanding.
As per coding guidelines,
**/*.{ts,tsx,js,jsx}: File size limit: If a file exceeds 1000 lines, split it into multiple sub-modules.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@wework/src/components/chat/ChatInput.test.tsx` around lines 1 - 2812, The test file ChatInput.test.tsx is over the repository size limit (>1000 lines); split it into focused test modules by feature (e.g., local-skill.tests.tsx, model-menu.tests.tsx, project-work.tests.tsx, attachments.tests.tsx, compact-desktop-ui.tests.tsx). Extract and reuse shared helpers and fixtures (ControlledChatInput, projectChatControls, projectWorkControls, shared imports like fireEvent/render/waitFor, and shared afterEach cleanup logic including URL.createObjectURL restoration and vi.restoreAllMocks) into a new test utility module (e.g., chatInputTestUtils) and import them from each smaller test file; move global stubs (vi.stubGlobal('fetch')) and any DOM getBoundingClientRect mocks into per-test setup in the new modules or shared setup functions to preserve behavior; keep the top-level describe('ChatInput', ...) groups split so each new file contains a focused describe and tests, retaining test IDs used in assertions (e.g., 'chat-message-input', 'local-skill-autocomplete', 'model-selector-button', 'project-work-button', 'attachment-badge') so tests continue to target the same component behavior.Source: Coding guidelines
wework/src/components/projects/ProjectCreateDialog.tsx (1)
1-1186: 🏗️ Heavy liftSplit
ProjectCreateDialog.tsxinto sub-modules.The file is over the repository’s size limit and now contains multiple domains (device gating, directory browsing, git flow, dialog rendering) in one module.
As per coding guidelines,
**/*.{ts,tsx,js,jsx}: File size limit: If a file exceeds 1000 lines, split it into multiple sub-modules.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@wework/src/components/projects/ProjectCreateDialog.tsx` around lines 1 - 1186, This file exceeds the size guideline — split it into focused sub-modules: 1) move path utilities (sanitizePathSegment, normalizePath, joinPath, basename, getParentPath, getPathSearchParts, directoryMatchesQuery) into a new path-utils module; 2) move device-related helpers (sortDevicesForProjectCreation, getProjectCreationDevices, getProjectDeviceOptions, canSelectProjectDevice, getDefaultDeviceId, isProjectDeviceUpgradeActive, canRequestDeviceUpgrade/isDeviceBelowWeWorkVersion checks wrappers, and getDeviceOptionLabel/ directoryDeviceUnavailableMessage logic) into a device-utils module; 3) move git helpers (getGitProjectName, uniqueRepositories, getGitErrorMessage) into a git-utils module; 4) keep the React components (ProjectCreateDialog and ProjectCreateDialogContent) in the component file and import the helpers from the new modules; ensure all moved functions are exported/imported with the same names and update any references (e.g., ProjectCreateDialogContent uses getDefaultDeviceId, getProjectDeviceOptions, onGetProjectWorkspaceRoot, onListGitRepositories, onListGitBranches, handleCreateDirectory, handleBrowsePath, handleConfirmPathInput) so behavior is unchanged and tests/imports still work.Source: Coding guidelines
wework/src/lib/device-capabilities.ts (1)
17-37: 💤 Low valueConsider adding a documentation comment for the version comparison algorithm.
The
isVersionAtLeastfunction implements non-trivial semantic version comparison (handles prefixes, suffixes, variable-length segments). A brief comment explaining the algorithm would improve maintainability:/** * Compares semantic versions segment-by-segment. * - Strips leading 'v' (case-insensitive) * - Ignores prerelease suffixes after '-' * - Treats missing segments as 0 (e.g., '1.8' equals '1.8.0') * - Returns true if version >= targetVersion */ export function isVersionAtLeast(version: string, targetVersion: string): boolean {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@wework/src/lib/device-capabilities.ts` around lines 17 - 37, Add a concise JSDoc comment to the isVersionAtLeast function describing its algorithm: mention that it strips a leading 'v' (case-insensitive), ignores prerelease suffixes after '-', parses numeric dot-separated segments via the internal parseVersion helper, treats missing segments as 0 for comparison, and returns true when version >= targetVersion; place the comment immediately above the isVersionAtLeast declaration and reference parseVersion in the description so future readers know what helper logic is used.wework/src/lib/device-capabilities.test.ts (2)
18-23: ⚡ Quick winTest combined task detection conditions.
Currently each condition (
slot_used,running_tasks,running_task_ids) is tested in isolation. Consider verifying combinations:test('detects tasks when multiple indicators are present', () => { expect(isDeviceRunningTask({ status: 'online', slot_used: 1, running_task_ids: [7] })).toBe(true) }) test('handles empty arrays vs undefined correctly', () => { expect(isDeviceRunningTask({ status: 'online', running_tasks: [] })).toBe(false) expect(isDeviceRunningTask({ status: 'online' })).toBe(false) })🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@wework/src/lib/device-capabilities.test.ts` around lines 18 - 23, Add tests to cover combined detection and empty-array vs undefined behavior for isDeviceRunningTask: add a test named like "detects tasks when multiple indicators are present" asserting isDeviceRunningTask({ status: 'online', slot_used: 1, running_task_ids: [7] }) returns true, and another test "handles empty arrays vs undefined correctly" asserting isDeviceRunningTask({ status: 'online', running_tasks: [] }) is false and isDeviceRunningTask({ status: 'online' }) is false (also include checks for running_task_ids: [] and slot_used: 0 vs undefined to ensure all empty/absent indicators are treated as not-running). Ensure the new tests live alongside the existing tests that reference isDeviceRunningTask.
12-16: ⚡ Quick winExpand version comparison edge case coverage.
The current tests validate the main scenarios, but consider adding edge cases to ensure robustness:
test('handles version segments of different lengths', () => { expect(isVersionAtLeast('1.8', '1.8.0')).toBe(true) // Treats missing as 0 expect(isVersionAtLeast('1.8.0', '1.8')).toBe(true) expect(isVersionAtLeast('1.8.0.0', '1.8')).toBe(true) }) test('rejects malformed versions', () => { expect(isVersionAtLeast('1.x.5', WEWORK_MIN_EXECUTOR_VERSION)).toBe(false) expect(isVersionAtLeast('', WEWORK_MIN_EXECUTOR_VERSION)).toBe(false) })🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@wework/src/lib/device-capabilities.test.ts` around lines 12 - 16, Add unit tests to cover version-segment-length differences and malformed inputs: in device-capabilities.test.ts, add cases calling isVersionAtLeast with versions like '1.8' vs '1.8.0', '1.8.0' vs '1.8', and '1.8.0.0' vs '1.8' to assert true (treat missing segments as 0), and add malformed inputs such as '1.x.5' and '' compared against WEWORK_MIN_EXECUTOR_VERSION to assert false; reference the existing test block that uses isVersionAtLeast and WEWORK_MIN_EXECUTOR_VERSION to place these new test() cases.wework/src/lib/device-selection.test.ts (1)
8-30: 💤 Low valueConsider expanding test coverage for device selection scenarios.
The current test validates the core upgrade scenario. Additional test cases would improve confidence:
test('prefers current device when it meets compatibility requirements', () => { const devices = [ { device_id: 'current', status: 'online', bind_shell: 'claudecode', executor_version: '1.8.5' }, { device_id: 'other', status: 'online', bind_shell: 'claudecode', executor_version: '1.8.6' } ] expect(getPreferredStandaloneDeviceId(devices, 'current')).toBe('current') }) test('returns null when no devices are compatible', () => { const devices = [ { device_id: 'old', status: 'online', bind_shell: 'claudecode', executor_version: '1.8.4' } ] expect(getPreferredStandaloneDeviceId(devices, 'old')).toBe(null) })🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@wework/src/lib/device-selection.test.ts` around lines 8 - 30, Add two additional unit tests to cover edge cases for standalone device selection: one where the current device meets compatibility so getPreferredStandaloneDeviceId(devices, 'current') returns 'current' (reference is isWeWorkSelectableStandaloneDevice and getPreferredStandaloneDeviceId) and another where no devices are compatible so getPreferredStandaloneDeviceId returns null; place these alongside the existing test to assert preferred selection when current is compatible and null when none qualify.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@wework/src/components/layout/DeviceStatusPrompt.tsx`:
- Around line 89-97: The runUpgrades function can surface unhandled promise
rejections when callers invoke it with void; change its aggregation to a
non-throwing form (e.g., use Promise.allSettled or catch each onUpgradeDevice
promise) so failures are captured rather than propagated, preserve the early
return for empty deviceIds and ensure setManualPending(true) is still set before
starting and setManualPending(false) in the finally/cleanup block; update
references inside runUpgrades to use onUpgradeDevice for individual tasks and
handle results/errors without rethrowing so callers won't receive unhandled
rejections.
In `@wework/src/components/projects/ProjectCreateDialog.tsx`:
- Around line 547-551: The handleUpgradeDevice async function can let rejections
from onUpgradeDevice become unhandled; wrap the await on
onUpgradeDevice(nextDeviceId) in a try/catch inside handleUpgradeDevice (and
keep the early return when onUpgradeDevice is falsy), and on error call
setProjectCreateError with a user-facing message (or error.message) and
optionally log the error so callers who trigger the void click handler won’t
produce an unhandled rejection; reference the handleUpgradeDevice function and
the onUpgradeDevice prop when making this change.
---
Outside diff comments:
In `@wework/src/components/chat/ChatInput.test.tsx`:
- Around line 70-82: The returned object in the test factory contains a
duplicate devices key which triggers the lint rule; remove the second occurrence
of "devices" from the object literal in ChatInput.test.tsx (the object returned
in the test setup/factory), leaving a single "devices" entry and keeping the
...overrides spread as-is so overrides still apply correctly.
---
Nitpick comments:
In `@wework/src/components/chat/ChatInput.test.tsx`:
- Around line 1-2812: The test file ChatInput.test.tsx is over the repository
size limit (>1000 lines); split it into focused test modules by feature (e.g.,
local-skill.tests.tsx, model-menu.tests.tsx, project-work.tests.tsx,
attachments.tests.tsx, compact-desktop-ui.tests.tsx). Extract and reuse shared
helpers and fixtures (ControlledChatInput, projectChatControls,
projectWorkControls, shared imports like fireEvent/render/waitFor, and shared
afterEach cleanup logic including URL.createObjectURL restoration and
vi.restoreAllMocks) into a new test utility module (e.g., chatInputTestUtils)
and import them from each smaller test file; move global stubs
(vi.stubGlobal('fetch')) and any DOM getBoundingClientRect mocks into per-test
setup in the new modules or shared setup functions to preserve behavior; keep
the top-level describe('ChatInput', ...) groups split so each new file contains
a focused describe and tests, retaining test IDs used in assertions (e.g.,
'chat-message-input', 'local-skill-autocomplete', 'model-selector-button',
'project-work-button', 'attachment-badge') so tests continue to target the same
component behavior.
In `@wework/src/components/layout/DeviceStatusPrompt.tsx`:
- Around line 49-341: DeviceStatusPrompt is too large—extract the large
branching render blocks and side-effect logic into smaller helpers/hooks to keep
the main function under ~50 lines; specifically move runUpgrades into a
useDeviceUpgrade hook (or utility) and create small components or render helpers
named e.g. NoDevicesPrompt, ActiveUpgradePrompt (uses activeUpgradeDevice and
upgradingDevices), OutdatedActivePrompt (uses activeDevice,
canRequestDeviceUpgrade, getBlockedReason), ActiveOfflinePrompt (uses
isDeviceRunningTask), OutdatedDevicesPrompt (uses
lowVersionUpgradeCandidates/lowVersionBlockedDevices), RequiresOnlinePrompt, and
UpdateAvailablePrompt (uses updateCandidates and canRequestDeviceUpgrade); wire
these helpers from DeviceStatusPrompt so the top-level function only computes
claudeCodeDevices/compatibleDevices/updateCandidates/activeUpgradeDevice and
returns the appropriate small component. Ensure each helper receives only the
props it needs (device lists, handlers like
onUpgradeDevice/onOpenCloudDeviceSettings, manualPending state from
useDeviceUpgrade) and keep naming exact (DeviceStatusPrompt, runUpgrades,
activeUpgradeDevice, updateCandidates, lowVersionUpgradeCandidates) to locate
code easily.
In `@wework/src/components/projects/ProjectCreateDialog.tsx`:
- Around line 1-1186: This file exceeds the size guideline — split it into
focused sub-modules: 1) move path utilities (sanitizePathSegment, normalizePath,
joinPath, basename, getParentPath, getPathSearchParts, directoryMatchesQuery)
into a new path-utils module; 2) move device-related helpers
(sortDevicesForProjectCreation, getProjectCreationDevices,
getProjectDeviceOptions, canSelectProjectDevice, getDefaultDeviceId,
isProjectDeviceUpgradeActive, canRequestDeviceUpgrade/isDeviceBelowWeWorkVersion
checks wrappers, and getDeviceOptionLabel/ directoryDeviceUnavailableMessage
logic) into a device-utils module; 3) move git helpers (getGitProjectName,
uniqueRepositories, getGitErrorMessage) into a git-utils module; 4) keep the
React components (ProjectCreateDialog and ProjectCreateDialogContent) in the
component file and import the helpers from the new modules; ensure all moved
functions are exported/imported with the same names and update any references
(e.g., ProjectCreateDialogContent uses getDefaultDeviceId,
getProjectDeviceOptions, onGetProjectWorkspaceRoot, onListGitRepositories,
onListGitBranches, handleCreateDirectory, handleBrowsePath,
handleConfirmPathInput) so behavior is unchanged and tests/imports still work.
In `@wework/src/features/workbench/WorkbenchProvider.tsx`:
- Around line 778-781: Replace the hardcoded Chinese strings assigned to the
local variable message (in the device.status !== 'online' ? ... : ...
conditional) with i18n translation keys using the same translation helper used
elsewhere (e.g., the t function from useTranslation or i18n.t), import/use that
helper in WorkbenchProvider.tsx if not already present, and update the
translation resource files with appropriate keys and Chinese/other locales;
ensure the conditional becomes something like t('workbench.device_offline') and
t('workbench.device_busy') so it matches existing status message patterns.
- Around line 1644-1666: Replace the two hardcoded Chinese error messages passed
to dispatch({type: 'error_set', error: ...}) with i18n lookups that include
placeholders for dynamic values; for the device-specific message use the display
name from getWorkbenchDeviceDisplayName and WEWORK_MIN_EXECUTOR_VERSION as
params (e.g. i18n.t('workbench.deviceVersionLow', { deviceName, version })) and
for the no-online-device case use a key like
i18n.t('workbench.noCompatibleDeviceOnline', { version:
WEWORK_MIN_EXECUTOR_VERSION }); update the imports/use of the translator in
WorkbenchProvider (where state.currentProject/state.currentTask/dispatch and
helpers like isWeWorkCompatibleDevice are used) so the dispatch error strings
come from the i18n system rather than hardcoded text.
- Around line 104-111: getUpgradeStatusMessage currently returns hardcoded
Chinese user-facing strings; update it to use the project's i18n translation
function instead (e.g., i18n.t or the existing translate wrapper) so all
messages go through the localization system. Keep the existing behavior for
payload.message and payload.error (using error as fallback), but replace
literals like '升级完成,正在检查设备版本', '升级失败', '设备正在执行任务,空闲后再升级', '设备已是最新版本', and
'设备升级中' with calls to the appropriate translation keys (create/choose sensible
keys like workbench.upgrade.success, workbench.upgrade.error,
workbench.upgrade.busy, workbench.upgrade.skipped,
workbench.upgrade.in_progress) and import/initialize the i18n helper in
WorkbenchProvider.tsx so getUpgradeStatusMessage(DeviceUpgradeStatusPayload)
returns translated strings.
- Line 803: The background call to refreshDevices() currently swallows errors
with .catch(() => undefined); update this to log the error and still swallow it
so UX is unchanged. Replace .catch(() => undefined) with something like
.catch(err => { /* use existing logger if available, e.g.
logger.error('refreshDevices failed', err) */ console.error('refreshDevices
failed', err); }); and reference the refreshDevices call inside
WorkbenchProvider (where void refreshDevices() is invoked).
In `@wework/src/lib/device-capabilities.test.ts`:
- Around line 18-23: Add tests to cover combined detection and empty-array vs
undefined behavior for isDeviceRunningTask: add a test named like "detects tasks
when multiple indicators are present" asserting isDeviceRunningTask({ status:
'online', slot_used: 1, running_task_ids: [7] }) returns true, and another test
"handles empty arrays vs undefined correctly" asserting isDeviceRunningTask({
status: 'online', running_tasks: [] }) is false and isDeviceRunningTask({
status: 'online' }) is false (also include checks for running_task_ids: [] and
slot_used: 0 vs undefined to ensure all empty/absent indicators are treated as
not-running). Ensure the new tests live alongside the existing tests that
reference isDeviceRunningTask.
- Around line 12-16: Add unit tests to cover version-segment-length differences
and malformed inputs: in device-capabilities.test.ts, add cases calling
isVersionAtLeast with versions like '1.8' vs '1.8.0', '1.8.0' vs '1.8', and
'1.8.0.0' vs '1.8' to assert true (treat missing segments as 0), and add
malformed inputs such as '1.x.5' and '' compared against
WEWORK_MIN_EXECUTOR_VERSION to assert false; reference the existing test block
that uses isVersionAtLeast and WEWORK_MIN_EXECUTOR_VERSION to place these new
test() cases.
In `@wework/src/lib/device-capabilities.ts`:
- Around line 17-37: Add a concise JSDoc comment to the isVersionAtLeast
function describing its algorithm: mention that it strips a leading 'v'
(case-insensitive), ignores prerelease suffixes after '-', parses numeric
dot-separated segments via the internal parseVersion helper, treats missing
segments as 0 for comparison, and returns true when version >= targetVersion;
place the comment immediately above the isVersionAtLeast declaration and
reference parseVersion in the description so future readers know what helper
logic is used.
In `@wework/src/lib/device-selection.test.ts`:
- Around line 8-30: Add two additional unit tests to cover edge cases for
standalone device selection: one where the current device meets compatibility so
getPreferredStandaloneDeviceId(devices, 'current') returns 'current' (reference
is isWeWorkSelectableStandaloneDevice and getPreferredStandaloneDeviceId) and
another where no devices are compatible so getPreferredStandaloneDeviceId
returns null; place these alongside the existing test to assert preferred
selection when current is compatible and null when none qualify.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: db8caafc-93bb-41c1-ad37-af3269c96821
📒 Files selected for processing (27)
wework/src/api/devices.test.tswework/src/api/devices.tswework/src/components/chat/ChatInput.test.tsxwework/src/components/chat/composer/ProjectWorkBar.tsxwework/src/components/layout/DesktopSidebar.tsxwework/src/components/layout/DesktopWorkbenchLayout.test.tsxwework/src/components/layout/DesktopWorkbenchLayout.tsxwework/src/components/layout/DesktopWorkbenchMain.tsxwework/src/components/layout/DeviceStatusPrompt.test.tsxwework/src/components/layout/DeviceStatusPrompt.tsxwework/src/components/layout/MobileWorkbenchLayout.tsxwework/src/components/projects/ProjectCreateDialog.test.tsxwework/src/components/projects/ProjectCreateDialog.tsxwework/src/features/workbench/WorkbenchProvider.test.tsxwework/src/features/workbench/WorkbenchProvider.tsxwework/src/i18n/locales/en/common.jsonwework/src/i18n/locales/zh-CN/common.jsonwework/src/lib/device-capabilities.test.tswework/src/lib/device-capabilities.tswework/src/lib/device-selection.test.tswework/src/lib/device-selection.tswework/src/pages/WorkbenchPage.tsxwework/src/stream/chatStream.test.tswework/src/stream/chatStream.tswework/src/types/api.tswework/src/types/device-events.tswework/src/types/devices.ts
| const runUpgrades = async (deviceIds: string[]) => { | ||
| if (deviceIds.length === 0) return | ||
| setManualPending(true) | ||
| try { | ||
| await Promise.all(deviceIds.map(deviceId => onUpgradeDevice(deviceId))) | ||
| } finally { | ||
| setManualPending(false) | ||
| } | ||
| } |
There was a problem hiding this comment.
Guard runUpgrades against unhandled promise rejections.
If one upgrade request rejects, this path can surface an unhandled rejection because callers invoke it with void .... Use a non-throwing aggregation path here.
💡 Suggested patch
const runUpgrades = async (deviceIds: string[]) => {
if (deviceIds.length === 0) return
setManualPending(true)
try {
- await Promise.all(deviceIds.map(deviceId => onUpgradeDevice(deviceId)))
+ await Promise.allSettled(
+ deviceIds.map(deviceId => Promise.resolve().then(() => onUpgradeDevice(deviceId))),
+ )
} finally {
setManualPending(false)
}
}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@wework/src/components/layout/DeviceStatusPrompt.tsx` around lines 89 - 97,
The runUpgrades function can surface unhandled promise rejections when callers
invoke it with void; change its aggregation to a non-throwing form (e.g., use
Promise.allSettled or catch each onUpgradeDevice promise) so failures are
captured rather than propagated, preserve the early return for empty deviceIds
and ensure setManualPending(true) is still set before starting and
setManualPending(false) in the finally/cleanup block; update references inside
runUpgrades to use onUpgradeDevice for individual tasks and handle
results/errors without rethrowing so callers won't receive unhandled rejections.
| const handleUpgradeDevice = async (nextDeviceId: string) => { | ||
| if (!onUpgradeDevice) return | ||
| setProjectCreateError(null) | ||
| await onUpgradeDevice(nextDeviceId) | ||
| } |
There was a problem hiding this comment.
Catch upgrade callback failures in handleUpgradeDevice.
This async path is triggered from void click handlers; if onUpgradeDevice rejects, it can become an unhandled rejection and leaves the user without feedback.
💡 Suggested patch
const handleUpgradeDevice = async (nextDeviceId: string) => {
if (!onUpgradeDevice) return
setProjectCreateError(null)
- await onUpgradeDevice(nextDeviceId)
+ try {
+ await onUpgradeDevice(nextDeviceId)
+ } catch (error) {
+ setProjectCreateError(
+ error instanceof Error
+ ? error.message
+ : t('workbench.project_create_failed', '项目创建失败'),
+ )
+ }
}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@wework/src/components/projects/ProjectCreateDialog.tsx` around lines 547 -
551, The handleUpgradeDevice async function can let rejections from
onUpgradeDevice become unhandled; wrap the await on
onUpgradeDevice(nextDeviceId) in a try/catch inside handleUpgradeDevice (and
keep the early return when onUpgradeDevice is falsy), and on error call
setProjectCreateError with a user-facing message (or error.message) and
optionally log the error so callers who trigger the void click handler won’t
produce an unhandled rejection; reference the handleUpgradeDevice function and
the onUpgradeDevice prop when making this change.
Task
Summary
Validation
Integration
Summary by CodeRabbit
Release Notes
New Features
Localization