ui: show worker spawn diagnostics
This commit is contained in:
parent
a6fe68301c
commit
9745bf041c
|
|
@ -1411,3 +1411,38 @@
|
||||||
background: rgba(255, 255, 255, 0.04);
|
background: rgba(255, 255, 255, 0.04);
|
||||||
padding: 0.75rem;
|
padding: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@layer components {
|
||||||
|
.worker-submit-error {
|
||||||
|
display: grid;
|
||||||
|
gap: var(--space-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.worker-submit-error p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.worker-error-diagnostics {
|
||||||
|
display: grid;
|
||||||
|
gap: var(--space-1);
|
||||||
|
margin: 0;
|
||||||
|
padding-left: var(--space-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.worker-error-diagnostics li {
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.worker-error-diagnostics li.error {
|
||||||
|
color: var(--danger);
|
||||||
|
}
|
||||||
|
|
||||||
|
.worker-error-diagnostics strong {
|
||||||
|
color: inherit;
|
||||||
|
font-weight: 750;
|
||||||
|
}
|
||||||
|
|
||||||
|
.worker-error-diagnostics span {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
import type {
|
import type {
|
||||||
BrowserCreateWorkerResponse,
|
BrowserCreateWorkerResponse,
|
||||||
BrowserWorkingDirectoryCreateResponse,
|
BrowserWorkingDirectoryCreateResponse,
|
||||||
|
Diagnostic,
|
||||||
ListResponse,
|
ListResponse,
|
||||||
Worker,
|
Worker,
|
||||||
WorkerLaunchOptionsResponse,
|
WorkerLaunchOptionsResponse,
|
||||||
|
|
@ -17,6 +18,17 @@
|
||||||
workspaceId: string;
|
workspaceId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type DisplayError = {
|
||||||
|
message: string;
|
||||||
|
diagnostics: Diagnostic[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type ErrorPayload = {
|
||||||
|
error?: { message?: string; code?: string } | string;
|
||||||
|
message?: string;
|
||||||
|
diagnostics?: Diagnostic[];
|
||||||
|
};
|
||||||
|
|
||||||
let { currentPath = '/', workspaceId }: Props = $props();
|
let { currentPath = '/', workspaceId }: Props = $props();
|
||||||
|
|
||||||
function workerApiPath(path: string): string {
|
function workerApiPath(path: string): string {
|
||||||
|
|
@ -31,7 +43,7 @@
|
||||||
let optionsError = $state<string | null>(null);
|
let optionsError = $state<string | null>(null);
|
||||||
let showNewWorker = $state(false);
|
let showNewWorker = $state(false);
|
||||||
let submitting = $state(false);
|
let submitting = $state(false);
|
||||||
let submitError = $state<string | null>(null);
|
let submitError = $state<DisplayError | null>(null);
|
||||||
let displayName = $state('Coding Worker');
|
let displayName = $state('Coding Worker');
|
||||||
let runtimeId = $state('');
|
let runtimeId = $state('');
|
||||||
let profile = $state('builtin:coder');
|
let profile = $state('builtin:coder');
|
||||||
|
|
@ -124,7 +136,7 @@
|
||||||
|
|
||||||
async function createWorkingDirectory() {
|
async function createWorkingDirectory() {
|
||||||
if (!workingDirectoryRepositoryId) {
|
if (!workingDirectoryRepositoryId) {
|
||||||
submitError = 'select a repository before creating a working directory';
|
submitError = { message: 'select a repository before creating a working directory', diagnostics: [] };
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
creatingWorkingDirectory = true;
|
creatingWorkingDirectory = true;
|
||||||
|
|
@ -140,7 +152,8 @@
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(await responseErrorMessage(response, 'working directory create failed'));
|
submitError = await responseDisplayError(response, 'working directory create failed');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
const payload = (await response.json()) as BrowserWorkingDirectoryCreateResponse;
|
const payload = (await response.json()) as BrowserWorkingDirectoryCreateResponse;
|
||||||
const items = options?.working_directories ?? [];
|
const items = options?.working_directories ?? [];
|
||||||
|
|
@ -149,7 +162,7 @@
|
||||||
: options;
|
: options;
|
||||||
workingDirectoryAllocationId = payload.item.allocation_id;
|
workingDirectoryAllocationId = payload.item.allocation_id;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
submitError = err instanceof Error ? err.message : 'working directory create failed';
|
submitError = exceptionDisplayError(err, 'working directory create failed');
|
||||||
} finally {
|
} finally {
|
||||||
creatingWorkingDirectory = false;
|
creatingWorkingDirectory = false;
|
||||||
}
|
}
|
||||||
|
|
@ -157,7 +170,7 @@
|
||||||
|
|
||||||
async function createWorker() {
|
async function createWorker() {
|
||||||
if (!workspaceId) {
|
if (!workspaceId) {
|
||||||
submitError = 'workspace id is unavailable';
|
submitError = { message: 'workspace id is unavailable', diagnostics: [] };
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -179,32 +192,44 @@
|
||||||
})),
|
})),
|
||||||
});
|
});
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(await responseErrorMessage(response, 'worker create failed'));
|
submitError = await responseDisplayError(response, 'worker create failed');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
const payload = (await response.json()) as BrowserCreateWorkerResponse;
|
const payload = (await response.json()) as BrowserCreateWorkerResponse;
|
||||||
await loadWorkers();
|
await loadWorkers();
|
||||||
window.location.href = payload.console_href;
|
window.location.href = payload.console_href;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
submitError = err instanceof Error ? err.message : 'worker create failed';
|
submitError = exceptionDisplayError(err, 'worker create failed');
|
||||||
} finally {
|
} finally {
|
||||||
submitting = false;
|
submitting = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function responseErrorMessage(response: Response, fallback: string): Promise<string> {
|
async function responseDisplayError(response: Response, fallback: string): Promise<DisplayError> {
|
||||||
try {
|
try {
|
||||||
const payload = (await response.json()) as { error?: { message?: string; code?: string } | string; message?: string };
|
const payload = (await response.json()) as ErrorPayload;
|
||||||
|
const diagnostics = Array.isArray(payload.diagnostics) ? payload.diagnostics : [];
|
||||||
if (typeof payload.error === 'object' && payload.error?.message) {
|
if (typeof payload.error === 'object' && payload.error?.message) {
|
||||||
return `${payload.error.code ?? 'request_failed'}: ${payload.error.message}`;
|
return {
|
||||||
|
message: `${payload.error.code ?? 'request_failed'}: ${payload.error.message}`,
|
||||||
|
diagnostics,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
if (payload.message) {
|
if (payload.message) {
|
||||||
const code = typeof payload.error === 'string' ? payload.error : 'request_failed';
|
const code = typeof payload.error === 'string' ? payload.error : 'request_failed';
|
||||||
return `${code}: ${payload.message}`;
|
return { message: `${code}: ${payload.message}`, diagnostics };
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// fall through
|
// fall through
|
||||||
}
|
}
|
||||||
return `${fallback} (${response.status})`;
|
return { message: `${fallback} (${response.status})`, diagnostics: [] };
|
||||||
|
}
|
||||||
|
|
||||||
|
function exceptionDisplayError(err: unknown, fallback: string): DisplayError {
|
||||||
|
return {
|
||||||
|
message: err instanceof Error ? err.message : fallback,
|
||||||
|
diagnostics: [],
|
||||||
|
};
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -296,7 +321,19 @@
|
||||||
<p class="section-state error">{optionsError}</p>
|
<p class="section-state error">{optionsError}</p>
|
||||||
{/if}
|
{/if}
|
||||||
{#if submitError}
|
{#if submitError}
|
||||||
<p class="section-state error">{submitError}</p>
|
<div class="section-state error worker-submit-error">
|
||||||
|
<p>{submitError.message}</p>
|
||||||
|
{#if submitError.diagnostics.length > 0}
|
||||||
|
<ul class="worker-error-diagnostics">
|
||||||
|
{#each submitError.diagnostics as diagnostic}
|
||||||
|
<li class={diagnostic.severity}>
|
||||||
|
<strong>{diagnostic.code}</strong>
|
||||||
|
<span>{diagnostic.message}</span>
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<button type="submit" disabled={submitting || !runtimeId || !profile || !workingDirectoryAllocationId}>
|
<button type="submit" disabled={submitting || !runtimeId || !profile || !workingDirectoryAllocationId}>
|
||||||
{submitting ? 'Starting…' : 'Start Coding Worker'}
|
{submitting ? 'Starting…' : 'Start Coding Worker'}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user