diff --git a/web/workspace/src/lib/workspace-api/http.ts b/web/workspace/src/lib/workspace-api/http.ts new file mode 100644 index 00000000..3f3573c8 --- /dev/null +++ b/web/workspace/src/lib/workspace-api/http.ts @@ -0,0 +1,34 @@ +export type ApiResult = { + data: T | null; + error: string | null; +}; + +export async function loadJson( + fetchFn: typeof fetch, + path: string, +): Promise> { + try { + const response = await fetchFn(path); + if (!response.ok) { + const text = await response.text(); + return { + data: null, + error: text || `${path} request failed (${response.status})`, + }; + } + return { data: (await response.json()) as T, error: null }; + } catch (error) { + return { + data: null, + error: error instanceof Error ? error.message : `${path} request failed`, + }; + } +} + +export function formatDate(value: string): string { + const date = new Date(value); + if (Number.isNaN(date.getTime())) { + return value; + } + return date.toLocaleString(); +} diff --git a/web/workspace/src/lib/workspace-console/worker-console.ui.test.ts b/web/workspace/src/lib/workspace-console/worker-console.ui.test.ts index d2569f92..2bf86df6 100644 --- a/web/workspace/src/lib/workspace-console/worker-console.ui.test.ts +++ b/web/workspace/src/lib/workspace-console/worker-console.ui.test.ts @@ -11,7 +11,7 @@ function assert(condition: unknown, message: string): asserts condition { Deno.test("workspace Worker list and sidebar attach through Worker Console hrefs", async () => { const workspacePage = await Deno.readTextFile( - new URL("../workspace-pages/WorkspacePage.svelte", import.meta.url), + new URL("./../../routes/+page.svelte", import.meta.url), ); const workersNav = await Deno.readTextFile( new URL("../workspace-sidebar/WorkersNavSection.svelte", import.meta.url), diff --git a/web/workspace/src/lib/workspace-pages/WorkspacePage.svelte b/web/workspace/src/lib/workspace-pages/WorkspacePage.svelte deleted file mode 100644 index 3933843c..00000000 --- a/web/workspace/src/lib/workspace-pages/WorkspacePage.svelte +++ /dev/null @@ -1,512 +0,0 @@ - - - - Yoi Workspace Control Plane - - - -
- - -
- {#if route.page === 'repository'} -
-

Repository summary

- {#if repository} -
-
-
ID
-
{repository.id}
-
-
-
Kind
-
{repository.kind}
-
-
-
Provider
-
{repository.provider}
-
-
-
Default selector
-
{repository.default_selector ?? 'none configured'}
-
-
-
Record authority
-
{repository.record_authority}
-
-
-
Git
-
{repository.git?.status ?? 'not available'}
-
- {#if repository.diagnostics && repository.diagnostics.length > 0} -
-
Diagnostics
-
-
    - {#each repository.diagnostics as diagnostic} -
  • {diagnostic.code}: {diagnostic.message}
  • - {/each} -
-
-
- {/if} -
- {:else if repositoryError} -

{repositoryError}

- {:else} -

Waiting for /api/repositories/{route.repositoryId}

- {/if} -
- -
-

Repository Ticket Kanban

-

- Read-only grouping of canonical Ticket records. No drag/drop or lifecycle mutation is exposed. -

- {#if repositoryTickets} - - {:else if repositoryTicketsError} -

{repositoryTicketsError}

- {:else} -

Waiting for /api/repositories/{route.repositoryId}/tickets

- {/if} -
- - {:else if route.page === 'objectives' || route.page === 'objective'} -
-

Objectives

-

- Objectives are read from canonical filesystem records through /api/objectives. -

- {#if objectives} - {#if objectives.items.length === 0} -

No Objective records are present.

- {:else} - - {/if} - {#if objectives.invalid_records.length > 0} -

{objectives.invalid_records.length} invalid objective record(s) hidden.

- {/if} - {:else if objectivesError} -

{objectivesError}

- {:else} -

Waiting for /api/objectives

- {/if} -
- - {#if route.page === 'objective'} -
-

Objective detail

- {#if objectiveDetail} -
-

{objectiveDetail.title}

- {objectiveDetail.state} -
-
-
-
ID
-
{objectiveDetail.id}
-
-
-
Updated
-
{formatDate(objectiveDetail.updated_at)}
-
-
-
Created
-
{formatDate(objectiveDetail.created_at)}
-
-
-
Linked tickets
-
{objectiveDetail.linked_tickets.length ? objectiveDetail.linked_tickets.join(', ') : 'none'}
-
-
-
Source
-
{objectiveDetail.record_source}
-
-
- {#if objectiveDetail.body_truncated} -

Objective body was truncated by the backend response limit.

- {/if} -
{objectiveDetail.body || 'No Objective body text is available.'}
- {:else if objectiveDetailError} -

{objectiveDetailError}

- {:else if objectiveDetailLoading} -

Loading Objective {route.objectiveId}

- {:else} -

Waiting for Objective detail…

- {/if} -
- {/if} - {:else} -
-

Workspace

- {#if workspace} -
-
-
ID
-
{workspace.workspace_id}
-
-
-
Name
-
{workspace.display_name}
-
-
-
Record authority
-
{workspace.record_authority}
-
-
-
Host / Worker bridge
-
{workspace.extension_points.host_worker_bridge.status}
-
-
- {:else if workspaceError} -

{workspaceError}

- {:else} -

Waiting for /api/workspace

- {/if} -
- - -
-
-

Hosts

- {#if hosts} - {#if hosts.items.length === 0} -

No local Hosts are visible.

- {:else} -
- {#each hosts.items as host} -
-
- {host.label} - {host.status} -
-
-
-
ID
-
{host.host_id}
-
-
-
Kind
-
{host.kind}
-
-
-
Runtime
-
{host.runtime_id}
-
-
-
Scope
-
{host.capabilities.workspace_scope}
-
-
-
Platform
-
{host.capabilities.os} / {host.capabilities.arch}
-
-
-
- {/each} -
- {/if} - {:else if hostsError} -

{hostsError}

- {:else} -

Waiting for /api/hosts

- {/if} -
- -
-

Workers

- {#if workers} - {#if workers.items.length === 0} -

No local Workers are visible.

- {:else} -
- - - - - - - - - - - - - {#each workers.items as worker} - - - - - - - - - {/each} - -
WorkerHostStateWorkspaceImplementationAttach
- {worker.label} - {#if worker.role || worker.profile} - {worker.role ?? 'role unknown'} / {worker.profile ?? 'profile unknown'} - {/if} - {worker.host_id}{worker.state} · {worker.status}{worker.workspace.visibility} · {worker.workspace.identity}{worker.implementation.kind}Open Console
-
- {/if} - {:else if workersError} -

{workersError}

- {:else} -

Waiting for /api/workers

- {/if} -
-
- - {/if} -
-
diff --git a/web/workspace/src/lib/workspace-settings/SettingsPage.svelte b/web/workspace/src/lib/workspace-settings/SettingsPage.svelte deleted file mode 100644 index 4d8e7277..00000000 --- a/web/workspace/src/lib/workspace-settings/SettingsPage.svelte +++ /dev/null @@ -1,505 +0,0 @@ - - - - Settings · Yoi Workspace - - -
- - -
-
-
-

Workspace Browser

-

Settings / Admin

-

- Local administration surfaces for the Workspace backend. Runtime Connections v0 is editable through typed APIs; broader admin controls remain bounded placeholders. -

-
- local only -
- -
-
-

Authority boundary

-

No browser admin permission model

-

{SETTINGS_PERMISSION_NOTICE}

-
-
- Restart-required - Runtime config changes are persisted, then applied after backend restart. -
-
- -
- {#each SETTINGS_SECTIONS as section} - - {section.label} - {section.status === "editable" ? "Editable" : section.status === "read-only" ? "Read-only" : "Placeholder"} - - {/each} -
- -
-
-
-

editable

-

Runtime Connections

-
- typed API -
-

{SETTINGS_SECTIONS.find((section) => section.id === "runtime-connections")?.summary}

- - {#if runtimeLoading} -

Loading Runtime connections…

- {:else if runtimeError} -

Runtime connection settings unavailable: {runtimeError}

- {:else if runtimeSettings} -
- -
- - {#if showAddRuntimeForm} -
{ event.preventDefault(); void submitRemoteRuntime(); }}> -

Add remote Runtime

-

Endpoint is submitted to the Backend but not echoed back in settings responses.

- - - - -
- {/if} - - {#if mutationMessage} -

{mutationMessage}

- {/if} - {@render DiagnosticsList({ diagnostics: mutationDiagnostics })} - -
-

Runtimes

-
- - - - - - - - - - - - - - - - - - - {#if runtimeSettings.embedded.diagnostics.length > 0} - - - - {/if} - {#each runtimeSettings.remotes as remote (remote.runtime_id)} - - - - - - - - {#if remote.diagnostics.length > 0} - - - - {/if} - {#if tests[remote.runtime_id]} - {@const test = tests[remote.runtime_id]} - {@const available = capabilityOperations(test, "available")} - {@const unchecked = capabilityOperations(test, "unknown")} - - - - {/if} - {/each} - -
RuntimeSourceConnectionStatusActions
- {runtimeSettings.embedded.display_name} - {runtimeSettings.embedded.runtime_id} - - embedded - Workspace backend process - Local Backend runtime - {runtimeSettings.embedded.status} - {#if runtimeSettings.embedded.restart_required} - restart required - {/if} - Managed by backend
{@render DiagnosticsList({ diagnostics: runtimeSettings.embedded.diagnostics })}
- {remote.display_name} - {remote.runtime_id} - - remote - Configured Runtime endpoint - - Endpoint: {remote.endpoint_configured ? "configured" : "not configured"} - {#if remote.endpoint_configured}hidden{/if} - Token: {remote.token_ref_configured ? "configured" : "not configured"} - - {remote.status} - {#if remote.restart_required} - restart required - {/if} - -
- - -
-
{@render DiagnosticsList({ diagnostics: remote.diagnostics })}
-
- Test: {test.state} - {test.health_result} · {test.checked_at} -

{test.compatibility_basis}

- {#if available.length > 0} -

Verified areas: {available.join(', ')}

- {/if} - {#if unchecked.length > 0} -

Unchecked warning areas: {unchecked.join(', ')}

- {/if} - {@render DiagnosticsList({ diagnostics: test.diagnostics })} -
-
-
- {#if runtimeSettings.remotes.length === 0} -

No remote Runtime connections configured.

- {/if} -
- {/if} -
- -
- {#each SETTINGS_SECTIONS.filter((section) => section.id !== "runtime-connections") as section} -
-
-
-

{section.status}

-

{section.label}

-
- {#if section.status === "placeholder"} - not implemented - {:else} - read-only - {/if} -
-

{section.summary}

-
    - {#each section.bullets as bullet} -
  • {bullet}
  • - {/each} -
- - {#if section.id === "workspace-identity"} -
-
-
Workspace id
-
{workspace?.workspace_id ?? "loading"}
-
-
-
Display name
-
{workspace?.display_name ?? "loading"}
-
-
-
Record authority
-
.yoi tickets/objectives through the Backend projection
-
-
- {/if} -
- {/each} -
- -
-
-

Implementation patterns

-

How settings should appear

-
-
- {#each SETTINGS_PATTERNS as pattern} -
-

{pattern.title}

-

{pattern.body}

-
- {/each} -
-
- - {#if loading} -

Loading workspace summary…

- {:else if error} -

Workspace summary unavailable: {error}

- {/if} -
-
- -{#snippet DiagnosticsList({ diagnostics }: { diagnostics: Diagnostic[] })} - {#if diagnostics.length > 0} -
    - {#each diagnostics as diagnostic} -
  • - {diagnosticLabel(diagnostic)} - {diagnostic.message} -
  • - {/each} -
- {/if} -{/snippet} diff --git a/web/workspace/src/lib/workspace-sidebar/WorkspaceSidebar.svelte b/web/workspace/src/lib/workspace-sidebar/WorkspaceSidebar.svelte index 4bdd781d..174ed657 100644 --- a/web/workspace/src/lib/workspace-sidebar/WorkspaceSidebar.svelte +++ b/web/workspace/src/lib/workspace-sidebar/WorkspaceSidebar.svelte @@ -1,5 +1,4 @@