fix(walkthrough): name an outdated server instead of failing to parse its HTML
A server without these routes does not answer 404 with JSON. The unmatched /api path reaches the OpenCode proxy, and OpenCode serves its embedded web UI for anything it does not recognise — HTML, status 200 — so a client newer than its server parsed a web page as JSON and put "Unexpected token '<', "<!doctype" in the panel, naming neither the cause nor the remedy. The client now checks the content type before parsing. A non-JSON answer on 2xx or 404 blocks with "this server is older than the app, update it and refresh". A non-JSON 5xx keeps its own failure: a server that answered badly is not a server missing the feature, and sending that user to upgrade chases the wrong thing.
This commit is contained in:
@@ -6,10 +6,10 @@ import { useI18n } from '@/lib/i18n';
|
||||
import { useConfigStore } from '@/stores/useConfigStore';
|
||||
import { runtimeFetch } from '@/lib/runtime-fetch';
|
||||
import { updateDesktopSettings } from '@/lib/persistence';
|
||||
import type { WalkthroughBlockedReason, WalkthroughModel } from '@/lib/walkthrough/types';
|
||||
import type { WalkthroughBlockedState, WalkthroughModel } from '@/lib/walkthrough/types';
|
||||
|
||||
interface WalkthroughBlockerProps {
|
||||
reason: WalkthroughBlockedReason;
|
||||
reason: WalkthroughBlockedState;
|
||||
model?: WalkthroughModel;
|
||||
requiredChars?: number;
|
||||
availableChars?: number;
|
||||
@@ -102,6 +102,7 @@ export const WalkthroughBlocker = ({
|
||||
if (reason === 'no-model') return t('walkthrough.blocked.noModel.description');
|
||||
if (reason === 'empty-diff') return t('walkthrough.blocked.emptyDiff.description');
|
||||
if (reason === 'only-generated') return t('walkthrough.blocked.onlyGenerated.description');
|
||||
if (reason === 'server-unsupported') return t('walkthrough.blocked.serverUnsupported.description');
|
||||
if (reason === 'output-exhausted') {
|
||||
return label
|
||||
? t('walkthrough.blocked.outputExhausted.description', { model: label })
|
||||
@@ -125,6 +126,7 @@ export const WalkthroughBlocker = ({
|
||||
if (reason === 'no-model') return t('walkthrough.blocked.noModel.title');
|
||||
if (reason === 'empty-diff') return t('walkthrough.blocked.emptyDiff.title');
|
||||
if (reason === 'only-generated') return t('walkthrough.blocked.onlyGenerated.title');
|
||||
if (reason === 'server-unsupported') return t('walkthrough.blocked.serverUnsupported.title');
|
||||
if (reason === 'output-exhausted') return t('walkthrough.blocked.outputExhausted.title');
|
||||
if (reason === 'structured-output-unsupported') return t('walkthrough.blocked.structuredOutput.title');
|
||||
return t('walkthrough.blocked.contextTooSmall.title');
|
||||
@@ -156,7 +158,9 @@ export const WalkthroughBlocker = ({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{(reason === 'empty-diff' || reason === 'only-generated') && (
|
||||
{/* Retry is the whole remedy once the server is updated, so it stays in
|
||||
reach rather than sending the user back through the panel header. */}
|
||||
{(reason === 'empty-diff' || reason === 'only-generated' || reason === 'server-unsupported') && (
|
||||
<Button type="button" variant="outline" size="sm" onClick={onRetry}>
|
||||
{t('walkthrough.action.refresh')}
|
||||
</Button>
|
||||
|
||||
@@ -440,6 +440,9 @@ export const WalkthroughView = ({ directory }: WalkthroughViewProps) => {
|
||||
|| entry.error?.code === 'empty-diff'
|
||||
|| entry.error?.code === 'only-generated'
|
||||
|| entry.error?.code === 'output-exhausted'
|
||||
// Client-detected rather than reported: the server answered something that
|
||||
// was not JSON, so it has no walkthrough routes at all.
|
||||
|| entry.error?.code === 'server-unsupported'
|
||||
? entry.error.code
|
||||
: entry.readiness && !entry.readiness.ready && !view
|
||||
&& entry.readiness.reason !== 'no-provider-login'
|
||||
|
||||
Reference in New Issue
Block a user