Merge branch 'orchestration' into impl/00001KVNG9B9Z-workspace-sidebar
# Conflicts: # web/workspace/src/routes/+page.svelte
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
<script lang="ts">
|
||||
import type { WorkerSummary } from './types';
|
||||
import type { ListResponse, Worker } from './types';
|
||||
|
||||
const MAX_VISIBLE_WORKERS = 6;
|
||||
|
||||
let loading = $state(true);
|
||||
let error = $state<string | null>(null);
|
||||
let workers = $state<WorkerSummary[]>([]);
|
||||
let workers = $state<Worker[]>([]);
|
||||
let placeholder = $state<string | null>(null);
|
||||
|
||||
$effect(() => {
|
||||
@@ -28,8 +28,8 @@
|
||||
if (!response.ok) {
|
||||
throw new Error(`workers request failed (${response.status})`);
|
||||
}
|
||||
const payload = await response.json();
|
||||
workers = normalizeWorkers(payload).slice(0, MAX_VISIBLE_WORKERS);
|
||||
const payload = (await response.json()) as ListResponse<Worker>;
|
||||
workers = Array.isArray(payload.items) ? payload.items.slice(0, MAX_VISIBLE_WORKERS) : [];
|
||||
if (workers.length === 0) {
|
||||
placeholder = 'No workers reported by the current API.';
|
||||
}
|
||||
@@ -45,47 +45,6 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeWorkers(payload: unknown): WorkerSummary[] {
|
||||
const items = Array.isArray(payload)
|
||||
? payload
|
||||
: isRecord(payload) && Array.isArray(payload.items)
|
||||
? payload.items
|
||||
: [];
|
||||
|
||||
return items.map((item, index) => normalizeWorker(item, index));
|
||||
}
|
||||
|
||||
function normalizeWorker(item: unknown, index: number): WorkerSummary {
|
||||
if (!isRecord(item)) {
|
||||
return {
|
||||
id: `worker-${index + 1}`,
|
||||
label: `worker ${index + 1}`,
|
||||
status: 'unknown'
|
||||
};
|
||||
}
|
||||
|
||||
const id = readText(item, ['id', 'worker_id', 'name']) ?? `worker-${index + 1}`;
|
||||
const label = readText(item, ['display_name', 'label', 'name', 'worker_id', 'id']) ?? id;
|
||||
const status = readText(item, ['status', 'state', 'lifecycle']) ?? 'unknown';
|
||||
const detail = readText(item, ['role', 'profile', 'note']);
|
||||
|
||||
return { id, label, status, detail };
|
||||
}
|
||||
|
||||
function readText(record: Record<string, unknown>, keys: string[]): string | null {
|
||||
for (const key of keys) {
|
||||
const value = record[key];
|
||||
if (typeof value === 'string' && value.trim().length > 0) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === 'object' && value !== null;
|
||||
}
|
||||
</script>
|
||||
|
||||
<section class="nav-section" aria-labelledby="workers-heading">
|
||||
@@ -104,11 +63,11 @@
|
||||
<p class="section-state">{placeholder ?? 'Workers will appear here when an API is connected.'}</p>
|
||||
{:else}
|
||||
<ul class="nav-list" aria-label="Workers">
|
||||
{#each workers as worker (worker.id)}
|
||||
{#each workers as worker (worker.worker_id)}
|
||||
<li class="nav-item">
|
||||
<span class="item-title">{worker.label}</span>
|
||||
<span class="item-meta">
|
||||
{worker.status}{worker.detail ? ` · ${worker.detail}` : ''}
|
||||
{worker.state} · {worker.status}{worker.role ? ` · ${worker.role}` : ''}
|
||||
</span>
|
||||
</li>
|
||||
{/each}
|
||||
|
||||
@@ -1,13 +1,64 @@
|
||||
export type ExtensionPoint = {
|
||||
status: string;
|
||||
note: string;
|
||||
};
|
||||
|
||||
export type WorkspaceResponse = {
|
||||
workspace_id: string;
|
||||
display_name: string;
|
||||
record_authority: string;
|
||||
extension_points: {
|
||||
event_stream: { status: string; note: string };
|
||||
runner_connection: { status: string; note: string };
|
||||
event_stream: ExtensionPoint;
|
||||
host_worker_bridge: ExtensionPoint;
|
||||
};
|
||||
};
|
||||
|
||||
export type Diagnostic = {
|
||||
code: string;
|
||||
severity: string;
|
||||
message: string;
|
||||
};
|
||||
|
||||
export type Host = {
|
||||
host_id: string;
|
||||
label: string;
|
||||
kind: string;
|
||||
status: string;
|
||||
observed_at: string;
|
||||
last_seen_at: string;
|
||||
capabilities: {
|
||||
local_pod_inspection: string;
|
||||
workspace_root: string;
|
||||
os: string;
|
||||
arch: string;
|
||||
max_workers: number;
|
||||
};
|
||||
diagnostics: Diagnostic[];
|
||||
};
|
||||
|
||||
export type Worker = {
|
||||
worker_id: string;
|
||||
host_id: string;
|
||||
label: string;
|
||||
pod_name: string;
|
||||
role?: string;
|
||||
profile?: string;
|
||||
workspace_root?: string;
|
||||
state: string;
|
||||
status: string;
|
||||
last_seen_at?: string;
|
||||
implementation: { kind: string; pod_name: string };
|
||||
diagnostics: Diagnostic[];
|
||||
};
|
||||
|
||||
export type ListResponse<T> = {
|
||||
workspace_id: string;
|
||||
limit: number;
|
||||
items: T[];
|
||||
source: string;
|
||||
diagnostics: Diagnostic[];
|
||||
};
|
||||
|
||||
export type ObjectiveSummary = {
|
||||
id: string;
|
||||
title: string;
|
||||
@@ -29,10 +80,3 @@ export type ObjectiveListResponse = {
|
||||
invalid_records: InvalidProjectRecord[];
|
||||
record_authority: string;
|
||||
};
|
||||
|
||||
export type WorkerSummary = {
|
||||
id: string;
|
||||
label: string;
|
||||
status: string;
|
||||
detail?: string | null;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user