fix: harden runtime and worker launch controls

This commit is contained in:
2026-07-03 02:54:48 +09:00
parent f2fead7ebd
commit 47ed0ff825
5 changed files with 755 additions and 88 deletions
@@ -1,5 +1,6 @@
<script lang="ts">
import { workerConsoleHref } from '$lib/workspace-console/model';
import { buildBrowserCreateWorkerRequest, defaultWorkerLaunchForm } from './worker-launch';
import type {
BrowserCreateWorkerResponse,
ListResponse,
@@ -77,16 +78,15 @@
}
const payload = (await response.json()) as WorkerLaunchOptionsResponse;
options = payload;
const preferredRuntime = payload.runtimes.find((runtime) => runtime.can_spawn_worker && runtime.status === 'active')
?? payload.runtimes.find((runtime) => runtime.can_spawn_worker)
?? payload.runtimes[0];
if (preferredRuntime && !runtimeId) {
runtimeId = preferredRuntime.runtime_id;
}
const preferredProfile = payload.profiles.find((candidate) => candidate.id === 'builtin:coder') ?? payload.profiles[0];
if (preferredProfile && !payload.profiles.some((candidate) => candidate.id === profile)) {
profile = preferredProfile.id;
}
const form = defaultWorkerLaunchForm(payload, {
runtime_id: runtimeId,
display_name: displayName,
profile,
initial_text: initialText,
});
runtimeId = form.runtime_id;
displayName = form.display_name;
profile = form.profile;
} catch (err) {
if (err instanceof DOMException && err.name === 'AbortError') {
return;
@@ -102,12 +102,12 @@
const response = await fetch('/api/workers', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
body: JSON.stringify(buildBrowserCreateWorkerRequest({
runtime_id: runtimeId,
display_name: displayName,
profile,
initial_text: initialText,
}),
})),
});
if (!response.ok) {
throw new Error(await responseErrorMessage(response, 'worker create failed'));
@@ -0,0 +1,83 @@
import {
buildBrowserCreateWorkerRequest,
defaultWorkerLaunchForm,
type WorkerLaunchFormState,
} from './worker-launch.ts';
import type { WorkerLaunchOptionsResponse } from './types.ts';
declare const Deno: {
test(name: string, fn: () => void): void;
};
function assert(condition: unknown, message: string): asserts condition {
if (!condition) {
throw new Error(message);
}
}
const options: WorkerLaunchOptionsResponse = {
workspace_id: 'workspace',
runtimes: [
{
runtime_id: 'remote-runtime',
display_name: 'Remote Runtime',
built_in: false,
can_spawn_worker: false,
status: 'active',
diagnostics: [],
},
{
runtime_id: 'embedded-worker-runtime',
display_name: 'Embedded Runtime',
built_in: true,
can_spawn_worker: true,
status: 'active',
diagnostics: [],
},
],
profiles: [
{
id: 'runtime_default',
label: 'Runtime default',
description: 'Runtime default profile.',
},
{
id: 'builtin:coder',
label: 'Coding Worker',
description: 'Coding role.',
},
],
diagnostics: [],
};
Deno.test('new worker form defaults to backend-published runtime and profile candidates', () => {
const current: WorkerLaunchFormState = {
runtime_id: '',
display_name: '',
profile: 'free-text-profile',
initial_text: 'start here',
};
const form = defaultWorkerLaunchForm(options, current);
assert(form.runtime_id === 'embedded-worker-runtime', 'should choose spawn-capable runtime');
assert(form.profile === 'builtin:coder', 'should choose backend-published coder profile');
assert(form.display_name === 'Coding Worker', 'should derive default display name');
assert(form.initial_text === 'start here', 'should preserve initial text');
});
Deno.test('new worker submit payload exposes only browser contract fields', () => {
const request = buildBrowserCreateWorkerRequest({
runtime_id: 'embedded-worker-runtime',
display_name: 'Coding Worker',
profile: 'builtin:coder',
initial_text: 'implement ticket',
});
assert(
JSON.stringify(Object.keys(request).sort()) ===
JSON.stringify(['display_name', 'initial_text', 'profile', 'runtime_id'].sort()),
'submit payload should contain only Browser-facing worker create fields',
);
assert(!('kind' in request), 'kind must not be exposed as a Browser request field');
});
@@ -0,0 +1,41 @@
import type { WorkerLaunchOptionsResponse } from './types';
export type WorkerLaunchFormState = {
runtime_id: string;
display_name: string;
profile: string;
initial_text: string;
};
export type BrowserCreateWorkerRequest = WorkerLaunchFormState;
export function defaultWorkerLaunchForm(
options: WorkerLaunchOptionsResponse | null,
current: WorkerLaunchFormState,
): WorkerLaunchFormState {
const preferredRuntime = options?.runtimes.find((runtime) => runtime.can_spawn_worker && runtime.status === 'active')
?? options?.runtimes.find((runtime) => runtime.can_spawn_worker)
?? options?.runtimes[0];
const preferredProfile = options?.profiles.find((candidate) => candidate.id === 'builtin:coder')
?? options?.profiles[0];
return {
runtime_id: current.runtime_id || preferredRuntime?.runtime_id || '',
display_name: current.display_name || 'Coding Worker',
profile: options?.profiles.some((candidate) => candidate.id === current.profile)
? current.profile
: preferredProfile?.id || '',
initial_text: current.initial_text,
};
}
export function buildBrowserCreateWorkerRequest(
form: WorkerLaunchFormState,
): BrowserCreateWorkerRequest {
return {
runtime_id: form.runtime_id,
display_name: form.display_name,
profile: form.profile,
initial_text: form.initial_text,
};
}