Skip to content
5 changes: 5 additions & 0 deletions scripts/prepare-resources/desktop-bridge-checks.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ test('getDesktopBridgeExpectations returns stable expectation metadata', () => {
assert.ok(expectations.length > 0);
assert.ok(expectations.some((expectation) => expectation.required === true));
assert.ok(expectations.some((expectation) => expectation.required === false));
assert.ok(expectations.some((expectation) => expectation.label === 'chat transport preference read'));
assert.ok(expectations.some((expectation) => expectation.label === 'chat transport preference write'));
assert.ok(
expectations.some((expectation) => expectation.label === 'standalone chat transport preference read'),
);

for (const expectation of expectations) {
assert.equal(Array.isArray(expectation.filePath), true);
Expand Down
37 changes: 37 additions & 0 deletions scripts/prepare-resources/desktop-bridge-expectations.mjs
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
const escapeRegex = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');

const CHAT_TRANSPORT_MODE_STORAGE_KEY = 'chat.transportMode';
const CHAT_TRANSPORT_MODE_WEBSOCKET = 'websocket';

const CHAT_TRANSPORT_STORAGE_KEY_PATTERN = escapeRegex(CHAT_TRANSPORT_MODE_STORAGE_KEY);
const CHAT_TRANSPORT_WEBSOCKET_PATTERN = escapeRegex(CHAT_TRANSPORT_MODE_WEBSOCKET);

const DESKTOP_BRIDGE_PATTERNS = {
trayRestartGuard: /if\s*\(\s*!desktopBridge\s*\?\.\s*onTrayRestartBackend\s*\)\s*\{/,
trayRestartPromptInvoke:
Expand All @@ -10,6 +18,12 @@ const DESKTOP_BRIDGE_PATTERNS = {
/const\s+runtimeInfo\s*=\s*await\s+getDesktopRuntimeInfo\s*\(\s*\)\s*;?[\s\S]*?isDesktopReleaseMode\.value\s*=\s*runtimeInfo\.isDesktopRuntime/,
desktopReleaseModeFlag: /\bisDesktopReleaseMode\b/,
desktopRuntimeProbeWarn: /console\.warn\([\s\S]*desktop runtime/i,
chatTransportPreferenceRead: new RegExp(
`localStorage\\.getItem\\(["']${CHAT_TRANSPORT_STORAGE_KEY_PATTERN}["']\\)[\\s\\S]*?["']${CHAT_TRANSPORT_WEBSOCKET_PATTERN}["']`,
),
chatTransportPreferenceWrite: new RegExp(
`localStorage\\.setItem\\(["']${CHAT_TRANSPORT_STORAGE_KEY_PATTERN}["']\\s*,`,
),
};

const DESKTOP_BRIDGE_EXPECTATIONS = [
Expand Down Expand Up @@ -62,6 +76,29 @@ const DESKTOP_BRIDGE_EXPECTATIONS = [
hint: 'Expected warning log when desktop runtime detection fails.',
required: false,
},
{
filePath: ['src', 'components', 'chat', 'Chat.vue'],
pattern: DESKTOP_BRIDGE_PATTERNS.chatTransportPreferenceRead,
label: 'chat transport preference read',
hint:
'Expected chat UI to read localStorage["chat.transportMode"] and recognize "websocket".',
required: true,
},
{
filePath: ['src', 'components', 'chat', 'Chat.vue'],
pattern: DESKTOP_BRIDGE_PATTERNS.chatTransportPreferenceWrite,
label: 'chat transport preference write',
hint: 'Expected chat UI to persist transport mode via localStorage.setItem("chat.transportMode", ...).',
required: true,
},
{
filePath: ['src', 'components', 'chat', 'StandaloneChat.vue'],
pattern: DESKTOP_BRIDGE_PATTERNS.chatTransportPreferenceRead,
label: 'standalone chat transport preference read',
hint:
'Expected standalone chat UI to read localStorage["chat.transportMode"] and recognize "websocket".',
required: true,
},
];

export const getDesktopBridgeExpectations = () => [...DESKTOP_BRIDGE_EXPECTATIONS];
Expand Down
33 changes: 33 additions & 0 deletions src-tauri/src/bridge_bootstrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,12 @@

const TOKEN_STORAGE_KEY = 'token';
const SHELL_LOCALE_STORAGE_KEY = 'astrbot-locale';
// Mirror AstrBot dashboard transport persistence. Resource preparation verifies
// the upstream ChatUI still recognizes this storage contract before packaging.
const CHAT_TRANSPORT = Object.freeze({
STORAGE_KEY: 'chat.transportMode',
WEBSOCKET: 'websocket',
});
const STORAGE_SYNC_PATCHED_FLAG = '__astrbotDesktopStorageSyncPatched';
const LEGACY_TOKEN_SYNC_PATCHED_FLAG = '__astrbotDesktopTokenSyncPatched';

Expand Down Expand Up @@ -201,6 +207,12 @@
error,
});
};
const warnDefaultChatTransportModeError = (phase, error) => {
devWarn('[astrbotDesktop] failed to seed default chat transport mode', {
phase,
error,
});
};

const normalizeExternalHttpUrl = (rawUrl) => {
if (rawUrl instanceof URL) {
Expand Down Expand Up @@ -697,6 +709,26 @@
} catch {}
};

const ensureDefaultChatTransportMode = () => {
const storage = window.localStorage;
Comment thread
sourcery-ai[bot] marked this conversation as resolved.
Outdated
if (!storage) return;

let existingTransportMode;
try {
existingTransportMode = storage.getItem(CHAT_TRANSPORT.STORAGE_KEY);
} catch (error) {
warnDefaultChatTransportModeError('read', error);
return;
}
if (existingTransportMode !== null) return;

try {
storage.setItem(CHAT_TRANSPORT.STORAGE_KEY, CHAT_TRANSPORT.WEBSOCKET);
} catch (error) {
warnDefaultChatTransportModeError('write', error);
}
};

window.astrbotDesktop = {
__tauriBridge: true,
isDesktop: true,
Expand Down Expand Up @@ -740,6 +772,7 @@
installNavigationBridges();
void listenToTrayRestartBackendEvent();
patchLocalStorageBridgeSync();
ensureDefaultChatTransportMode();
void syncAuthToken();
void syncShellLocale();
})();