fix: route worker consoles through logical resources
This commit is contained in:
@@ -13439,6 +13439,14 @@ fn browser_worker_spawn_policy(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn browser_worker_console_href(workspace_id: &str, resource_key: &str) -> String {
|
||||||
|
format!(
|
||||||
|
"/w/{}/workers/{}/console",
|
||||||
|
encode_path_segment(workspace_id),
|
||||||
|
encode_path_segment(resource_key)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
async fn create_workspace_worker(
|
async fn create_workspace_worker(
|
||||||
State(api): State<WorkspaceApi>,
|
State(api): State<WorkspaceApi>,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
@@ -13750,18 +13758,22 @@ fn browser_worker_response_from_summary(
|
|||||||
let runtime_id = worker.worker.runtime_id.clone();
|
let runtime_id = worker.worker.runtime_id.clone();
|
||||||
let worker_id = worker.worker.worker_id.clone();
|
let worker_id = worker.worker.worker_id.clone();
|
||||||
let workspace_id = api.workspace_id().to_string();
|
let workspace_id = api.workspace_id().to_string();
|
||||||
let console_href = format!(
|
let resource_key = api
|
||||||
"/w/{}/runtimes/{}/workers/{}/console",
|
.store
|
||||||
encode_path_segment(&workspace_id),
|
.resource_key(&workspace_id, WorkspaceResourceKind::Worker, &worker_id)?
|
||||||
encode_path_segment(&runtime_id),
|
.ok_or_else(|| {
|
||||||
encode_path_segment(&worker_id)
|
Error::Store(format!(
|
||||||
);
|
"Workspace Worker `{worker_id}` has no resource key after registration"
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
let worker = worker_launch_worker_summary(worker);
|
||||||
|
let console_href = browser_worker_console_href(&workspace_id, &resource_key);
|
||||||
Ok(BrowserCreateWorkerResponse {
|
Ok(BrowserCreateWorkerResponse {
|
||||||
workspace_id,
|
workspace_id,
|
||||||
runtime_id,
|
runtime_id,
|
||||||
worker_id,
|
worker_id,
|
||||||
console_href,
|
console_href,
|
||||||
worker: worker_launch_worker_summary(worker),
|
worker,
|
||||||
diagnostics: diagnostics
|
diagnostics: diagnostics
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(workspace_api::Diagnostic::from)
|
.map(workspace_api::Diagnostic::from)
|
||||||
@@ -17193,6 +17205,13 @@ mod tests {
|
|||||||
SqliteWorkspaceStore, UserRecord, WorkspaceRecord, WorkspaceRuntimeBinding,
|
SqliteWorkspaceStore, UserRecord, WorkspaceRecord, WorkspaceRuntimeBinding,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn browser_worker_console_href_uses_logical_worker_route() {
|
||||||
|
let href = browser_worker_console_href("workspace/one", "W-7");
|
||||||
|
assert_eq!(href, "/w/workspace%2Fone/workers/W-7/console");
|
||||||
|
assert!(!href.contains("/runtimes/"));
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn workspace_mutation_gate_serializes_deletion_with_active_mutations() {
|
async fn workspace_mutation_gate_serializes_deletion_with_active_mutations() {
|
||||||
let locks = Arc::new(AsyncMutex::new(HashMap::new()));
|
let locks = Arc::new(AsyncMutex::new(HashMap::new()));
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ context ではない。`crypto.randomUUID()` は secure context 限定なので
|
|||||||
Worker Console の `connectProtocolTransport` は、最初に `protocolState` を
|
Worker Console の `connectProtocolTransport` は、最初に `protocolState` を
|
||||||
`"connecting"` に設定してから `WorkspaceMultiplexer.subscribe()` を呼ぶ。
|
`"connecting"` に設定してから `WorkspaceMultiplexer.subscribe()` を呼ぶ。
|
||||||
|
|
||||||
- `web/workspace/src/routes/w/[workspaceId]/runtimes/[runtimeId]/workers/[workerId]/console/+page.svelte`
|
- `web/workspace/src/routes/w/[workspaceId]/workers/[workerRef]/console/+page.svelte`
|
||||||
- `protocolState = "connecting"`
|
- `protocolState = "connecting"`
|
||||||
- 直後に `workspaceMultiplexer(...).subscribe(...)`
|
- 直後に `workspaceMultiplexer(...).subscribe(...)`
|
||||||
- `web/workspace/src/lib/workspace/multiplexer.ts`
|
- `web/workspace/src/lib/workspace/multiplexer.ts`
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ import {
|
|||||||
resolveConsoleWorkerView,
|
resolveConsoleWorkerView,
|
||||||
segmentsToText,
|
segmentsToText,
|
||||||
selectConsoleTimelineLines,
|
selectConsoleTimelineLines,
|
||||||
workerConsoleHref,
|
|
||||||
} from "./model.ts";
|
} from "./model.ts";
|
||||||
|
import { workerConsoleHref } from "../resource-links.ts";
|
||||||
|
|
||||||
declare const Deno: {
|
declare const Deno: {
|
||||||
test(name: string, fn: () => void): void;
|
test(name: string, fn: () => void): void;
|
||||||
@@ -461,14 +461,13 @@ Deno.test("reload snapshot projects provenance-annotated history entries", () =>
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
Deno.test("workerConsoleHref encodes runtime and worker target authority", () => {
|
Deno.test("workerConsoleHref uses logical Worker resource authority", () => {
|
||||||
assert(
|
assert(
|
||||||
workerConsoleHref({
|
workerConsoleHref("workspace-1", {
|
||||||
runtime_id: "local runtime",
|
resource_key: "W-123",
|
||||||
worker_id: "worker/one",
|
display_name: "Worker One",
|
||||||
}, "workspace-1") ===
|
}) === "/w/workspace-1/workers/W-123-worker-one/console",
|
||||||
"/w/workspace-1/runtimes/local%20runtime/workers/worker%2Fone/console",
|
"href should contain the canonical logical Worker reference",
|
||||||
"href should contain encoded runtime_id and worker_id segments",
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import type {
|
|||||||
WorkerStatus,
|
WorkerStatus,
|
||||||
} from "$lib/generated/protocol";
|
} from "$lib/generated/protocol";
|
||||||
import { stringify as stringifyYaml } from "yaml";
|
import { stringify as stringifyYaml } from "yaml";
|
||||||
import { workspaceRoute } from "$lib/workspace/api/http";
|
|
||||||
import {
|
import {
|
||||||
applyRunActivityEvent,
|
applyRunActivityEvent,
|
||||||
emptyRunActivityStats,
|
emptyRunActivityStats,
|
||||||
@@ -220,31 +219,6 @@ export type WorkerTarget = {
|
|||||||
worker_id: string;
|
worker_id: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function workerConsoleHref(
|
|
||||||
target: WorkerTarget,
|
|
||||||
workspaceId: string,
|
|
||||||
): string {
|
|
||||||
return workspaceRoute(
|
|
||||||
workspaceId,
|
|
||||||
`/runtimes/${encodeURIComponent(target.runtime_id)}/workers/${
|
|
||||||
encodeURIComponent(
|
|
||||||
target.worker_id,
|
|
||||||
)
|
|
||||||
}/console`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function workerConsolePath(
|
|
||||||
workspaceId: string,
|
|
||||||
runtimeId: string,
|
|
||||||
workerId: string,
|
|
||||||
): string {
|
|
||||||
return workerConsoleHref(
|
|
||||||
{ runtime_id: runtimeId, worker_id: workerId },
|
|
||||||
workspaceId,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ConsoleEventInput = {
|
export type ConsoleEventInput = {
|
||||||
eventId: string;
|
eventId: string;
|
||||||
event: ProtocolEvent;
|
event: ProtocolEvent;
|
||||||
|
|||||||
@@ -371,7 +371,7 @@ Deno.test("root layout keeps Workspace selection explicit", async () => {
|
|||||||
Deno.test("Worker Console uses protocol observation events without transcript fetch", async () => {
|
Deno.test("Worker Console uses protocol observation events without transcript fetch", async () => {
|
||||||
const consolePage = await Deno.readTextFile(
|
const consolePage = await Deno.readTextFile(
|
||||||
new URL(
|
new URL(
|
||||||
"./../../../routes/w/[workspaceId]/runtimes/[runtimeId]/workers/[workerId]/console/+page.svelte",
|
"./../../../routes/w/[workspaceId]/workers/[workerRef]/console/+page.svelte",
|
||||||
import.meta.url,
|
import.meta.url,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -395,7 +395,7 @@ Deno.test("Worker Console uses protocol observation events without transcript fe
|
|||||||
Deno.test("Worker Console owns its narrower centered shell width", async () => {
|
Deno.test("Worker Console owns its narrower centered shell width", async () => {
|
||||||
const page = await Deno.readTextFile(
|
const page = await Deno.readTextFile(
|
||||||
new URL(
|
new URL(
|
||||||
"./../../../routes/w/[workspaceId]/runtimes/[runtimeId]/workers/[workerId]/console/+page.svelte",
|
"./../../../routes/w/[workspaceId]/workers/[workerRef]/console/+page.svelte",
|
||||||
import.meta.url,
|
import.meta.url,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -494,7 +494,7 @@ Deno.test("Worker Console renders Edit diffs without preformatted template gaps"
|
|||||||
Deno.test("Worker Console exposes a foldable timeline beside the scroll body", async () => {
|
Deno.test("Worker Console exposes a foldable timeline beside the scroll body", async () => {
|
||||||
const consolePage = await Deno.readTextFile(
|
const consolePage = await Deno.readTextFile(
|
||||||
new URL(
|
new URL(
|
||||||
"./../../../routes/w/[workspaceId]/runtimes/[runtimeId]/workers/[workerId]/console/+page.svelte",
|
"./../../../routes/w/[workspaceId]/workers/[workerRef]/console/+page.svelte",
|
||||||
import.meta.url,
|
import.meta.url,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -529,7 +529,7 @@ Deno.test("Worker Console exposes a foldable timeline beside the scroll body", a
|
|||||||
Deno.test("Worker Console removes redundant chrome and uses shared alerts", async () => {
|
Deno.test("Worker Console removes redundant chrome and uses shared alerts", async () => {
|
||||||
const page = await Deno.readTextFile(
|
const page = await Deno.readTextFile(
|
||||||
new URL(
|
new URL(
|
||||||
"./../../../routes/w/[workspaceId]/runtimes/[runtimeId]/workers/[workerId]/console/+page.svelte",
|
"./../../../routes/w/[workspaceId]/workers/[workerRef]/console/+page.svelte",
|
||||||
import.meta.url,
|
import.meta.url,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -558,7 +558,7 @@ Deno.test("Worker Console removes redundant chrome and uses shared alerts", asyn
|
|||||||
Deno.test("Worker Console composer keeps a compact bounded chip editor", async () => {
|
Deno.test("Worker Console composer keeps a compact bounded chip editor", async () => {
|
||||||
const consolePage = await Deno.readTextFile(
|
const consolePage = await Deno.readTextFile(
|
||||||
new URL(
|
new URL(
|
||||||
"./../../../routes/w/[workspaceId]/runtimes/[runtimeId]/workers/[workerId]/console/+page.svelte",
|
"./../../../routes/w/[workspaceId]/workers/[workerRef]/console/+page.svelte",
|
||||||
import.meta.url,
|
import.meta.url,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -592,7 +592,7 @@ Deno.test("Worker Console composer keeps a compact bounded chip editor", async (
|
|||||||
Deno.test("Worker Console paste chips preserve typed draft and target authority", async () => {
|
Deno.test("Worker Console paste chips preserve typed draft and target authority", async () => {
|
||||||
const consolePage = await Deno.readTextFile(
|
const consolePage = await Deno.readTextFile(
|
||||||
new URL(
|
new URL(
|
||||||
"./../../../routes/w/[workspaceId]/runtimes/[runtimeId]/workers/[workerId]/console/+page.svelte",
|
"./../../../routes/w/[workspaceId]/workers/[workerRef]/console/+page.svelte",
|
||||||
import.meta.url,
|
import.meta.url,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -724,24 +724,28 @@ Deno.test("workspace Worker sidebar links New to the dedicated create page", asy
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
Deno.test("Worker Console page is routed by runtime_id and worker_id through backend APIs", async () => {
|
Deno.test("Worker Console route resolves logical Worker authority before Runtime APIs", async () => {
|
||||||
const consolePage = await Deno.readTextFile(
|
const consolePage = await Deno.readTextFile(
|
||||||
new URL(
|
new URL(
|
||||||
"./../../../routes/w/[workspaceId]/runtimes/[runtimeId]/workers/[workerId]/console/+page.svelte",
|
"./../../../routes/w/[workspaceId]/workers/[workerRef]/console/+page.svelte",
|
||||||
import.meta.url,
|
import.meta.url,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
const routeLoad = await Deno.readTextFile(
|
const routeLoad = await Deno.readTextFile(
|
||||||
new URL(
|
new URL(
|
||||||
"./../../../routes/w/[workspaceId]/runtimes/[runtimeId]/workers/[workerId]/console/+page.ts",
|
"./../../../routes/w/[workspaceId]/workers/[workerRef]/console/+page.ts",
|
||||||
import.meta.url,
|
import.meta.url,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert(
|
assert(
|
||||||
routeLoad.includes("workspaceId") &&
|
routeLoad.includes("params.workerRef") &&
|
||||||
routeLoad.includes("runtimeId") && routeLoad.includes("workerId"),
|
routeLoad.includes("resourceKey(params.workerRef)") &&
|
||||||
"route load should expose workspace and target ids",
|
routeLoad.includes("/workers/${encodeURIComponent(reference)}") &&
|
||||||
|
routeLoad.includes("canonicalResourceReference") &&
|
||||||
|
routeLoad.includes("runtimeId: result.data?.runtime_id") &&
|
||||||
|
routeLoad.includes("workerId: result.data?.worker_id"),
|
||||||
|
"route load should resolve the logical Worker reference before exposing its execution target",
|
||||||
);
|
);
|
||||||
assert(
|
assert(
|
||||||
consolePage.includes("workspaceApiPath(workspaceId, path)") &&
|
consolePage.includes("workspaceApiPath(workspaceId, path)") &&
|
||||||
@@ -951,7 +955,7 @@ Deno.test("Account UI owns browser passkey session state without workspace autho
|
|||||||
Deno.test("Workspace Worker list and Console share the multiplexed connection", async () => {
|
Deno.test("Workspace Worker list and Console share the multiplexed connection", async () => {
|
||||||
const consolePage = await Deno.readTextFile(
|
const consolePage = await Deno.readTextFile(
|
||||||
new URL(
|
new URL(
|
||||||
"./../../../routes/w/[workspaceId]/runtimes/[runtimeId]/workers/[workerId]/console/+page.svelte",
|
"./../../../routes/w/[workspaceId]/workers/[workerRef]/console/+page.svelte",
|
||||||
import.meta.url,
|
import.meta.url,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -988,7 +992,7 @@ Deno.test("Workspace Worker list and Console share the multiplexed connection",
|
|||||||
Deno.test("Web Console renders the client-projected Worker task store", async () => {
|
Deno.test("Web Console renders the client-projected Worker task store", async () => {
|
||||||
const consolePage = await Deno.readTextFile(
|
const consolePage = await Deno.readTextFile(
|
||||||
new URL(
|
new URL(
|
||||||
"./../../../routes/w/[workspaceId]/runtimes/[runtimeId]/workers/[workerId]/console/+page.svelte",
|
"./../../../routes/w/[workspaceId]/workers/[workerRef]/console/+page.svelte",
|
||||||
import.meta.url,
|
import.meta.url,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -1027,7 +1031,7 @@ Deno.test("Web Console renders the client-projected Worker task store", async ()
|
|||||||
Deno.test("Web Console switches main and direct SubWorker views from the Tasks row", async () => {
|
Deno.test("Web Console switches main and direct SubWorker views from the Tasks row", async () => {
|
||||||
const consolePage = await Deno.readTextFile(
|
const consolePage = await Deno.readTextFile(
|
||||||
new URL(
|
new URL(
|
||||||
"./../../../routes/w/[workspaceId]/runtimes/[runtimeId]/workers/[workerId]/console/+page.svelte",
|
"./../../../routes/w/[workspaceId]/workers/[workerRef]/console/+page.svelte",
|
||||||
import.meta.url,
|
import.meta.url,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -1071,7 +1075,7 @@ Deno.test("Web Console switches main and direct SubWorker views from the Tasks r
|
|||||||
Deno.test("Web Console uses Notify while running and exposes durable pending controls", async () => {
|
Deno.test("Web Console uses Notify while running and exposes durable pending controls", async () => {
|
||||||
const consolePage = await Deno.readTextFile(
|
const consolePage = await Deno.readTextFile(
|
||||||
new URL(
|
new URL(
|
||||||
"./../../../routes/w/[workspaceId]/runtimes/[runtimeId]/workers/[workerId]/console/+page.svelte",
|
"./../../../routes/w/[workspaceId]/workers/[workerRef]/console/+page.svelte",
|
||||||
import.meta.url,
|
import.meta.url,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ Deno.test("Ticket detail breadcrumbs expose the Ticket list and current id", ()
|
|||||||
Deno.test("Worker console breadcrumbs use the logical Workers route and display name", () => {
|
Deno.test("Worker console breadcrumbs use the logical Workers route and display name", () => {
|
||||||
assertEquals(
|
assertEquals(
|
||||||
buildWorkspaceBreadcrumbs(
|
buildWorkspaceBreadcrumbs(
|
||||||
"/w/workspace/runtimes/runtime-a/workers/worker-7/console",
|
"/w/workspace/workers/W-7-review-worker/console",
|
||||||
"workspace",
|
"workspace",
|
||||||
{ workerName: "Review Worker" },
|
{ workerName: "Review Worker" },
|
||||||
),
|
),
|
||||||
@@ -39,12 +39,12 @@ Deno.test("Worker console breadcrumbs use the logical Workers route and display
|
|||||||
Deno.test("Worker console breadcrumbs fall back to Worker id", () => {
|
Deno.test("Worker console breadcrumbs fall back to Worker id", () => {
|
||||||
assertEquals(
|
assertEquals(
|
||||||
buildWorkspaceBreadcrumbs(
|
buildWorkspaceBreadcrumbs(
|
||||||
"/w/workspace/runtimes/runtime-a/workers/worker-7/console",
|
"/w/workspace/workers/W-7-review-worker/console",
|
||||||
"workspace",
|
"workspace",
|
||||||
),
|
),
|
||||||
[
|
[
|
||||||
{ label: "workers", href: "/w/workspace/workers" },
|
{ label: "workers", href: "/w/workspace/workers" },
|
||||||
{ label: "worker-7" },
|
{ label: "W-7-review-worker" },
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -24,13 +24,13 @@ export function buildWorkspaceBreadcrumbs(
|
|||||||
.map(decodeURIComponent);
|
.map(decodeURIComponent);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
segments[0] === "runtimes" &&
|
segments[0] === "workers" &&
|
||||||
segments[2] === "workers" &&
|
segments[1] &&
|
||||||
segments[3]
|
segments[2] === "console"
|
||||||
) {
|
) {
|
||||||
return [
|
return [
|
||||||
{ label: "workers", href: `${workspaceRoot}/workers` },
|
{ label: "workers", href: `${workspaceRoot}/workers` },
|
||||||
{ label: context.workerName?.trim() || segments[3] },
|
{ label: context.workerName?.trim() || segments[1] },
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,3 +45,10 @@ export function workerHref(
|
|||||||
const reference = canonicalResourceReference(worker.resource_key, worker.display_name);
|
const reference = canonicalResourceReference(worker.resource_key, worker.display_name);
|
||||||
return `/w/${encodeURIComponent(workspaceId)}/workers/${encodeURIComponent(reference)}`;
|
return `/w/${encodeURIComponent(workspaceId)}/workers/${encodeURIComponent(reference)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function workerConsoleHref(
|
||||||
|
workspaceId: string,
|
||||||
|
worker: { resource_key: string; display_name: string },
|
||||||
|
): string {
|
||||||
|
return `${workerHref(workspaceId, worker)}/console`;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Spinner from '$lib/workspace/console/Spinner.svelte';
|
import Spinner from '$lib/workspace/console/Spinner.svelte';
|
||||||
import { workerConsoleHref } from '$lib/workspace/console/model';
|
import { workerConsoleHref } from '$lib/workspace/resource-links';
|
||||||
import { pushWorkspaceAlert } from '$lib/workspace/alerts/store';
|
import { pushWorkspaceAlert } from '$lib/workspace/alerts/store';
|
||||||
import {
|
import {
|
||||||
canDeleteSidebarWorker,
|
canDeleteSidebarWorker,
|
||||||
@@ -168,7 +168,7 @@
|
|||||||
{#if error}<p class="section-state error">{error}</p>{/if}
|
{#if error}<p class="section-state error">{error}</p>{/if}
|
||||||
<ul class="nav-list" aria-label="Workers">
|
<ul class="nav-list" aria-label="Workers">
|
||||||
{#each visibleWorkers as worker (`${worker.runtime_id}:${worker.worker_id}`)}
|
{#each visibleWorkers as worker (`${worker.runtime_id}:${worker.worker_id}`)}
|
||||||
{@const href = workerConsoleHref(worker, workspaceId)}
|
{@const href = workerConsoleHref(workspaceId, worker)}
|
||||||
{@const activity = sidebarWorkerActivity(worker)}
|
{@const activity = sidebarWorkerActivity(worker)}
|
||||||
{@const key = workerKey(worker)}
|
{@const key = workerKey(worker)}
|
||||||
{@const label = worker.display_name || worker.label}
|
{@const label = worker.display_name || worker.label}
|
||||||
|
|||||||
-23
@@ -1,23 +0,0 @@
|
|||||||
import { loadJson, workspaceApiPath } from "$lib/workspace/api/http";
|
|
||||||
import type { Worker } from "$lib/workspace/sidebar/types";
|
|
||||||
import type { PageLoad } from "./$types";
|
|
||||||
|
|
||||||
export const load: PageLoad = async ({ fetch, params }) => {
|
|
||||||
const worker = await loadJson<Worker>(
|
|
||||||
fetch,
|
|
||||||
workspaceApiPath(
|
|
||||||
params.workspaceId,
|
|
||||||
`/runtimes/${encodeURIComponent(params.runtimeId)}/workers/${
|
|
||||||
encodeURIComponent(params.workerId)
|
|
||||||
}`,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
workspaceId: params.workspaceId,
|
|
||||||
runtimeId: params.runtimeId,
|
|
||||||
workerId: params.workerId,
|
|
||||||
worker: worker.data,
|
|
||||||
workerError: worker.error,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { workspaceRoute } from '$lib/workspace/api/http';
|
import { workerConsoleHref } from '$lib/workspace/resource-links';
|
||||||
import { liveWorkerState } from '$lib/workspace/sidebar/worker-state';
|
import { liveWorkerState } from '$lib/workspace/sidebar/worker-state';
|
||||||
import type { PageData } from './$types';
|
import type { PageData } from './$types';
|
||||||
let { data }: { data: PageData } = $props();
|
let { data }: { data: PageData } = $props();
|
||||||
@@ -18,10 +18,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<a
|
<a
|
||||||
class="button-primary"
|
class="button-primary"
|
||||||
href={workspaceRoute(
|
href={workerConsoleHref(data.workspaceId, data.worker)}
|
||||||
data.workspaceId,
|
|
||||||
`/runtimes/${data.worker.runtime_id}/workers/${data.worker.worker_id}/console`,
|
|
||||||
)}
|
|
||||||
>Open console</a>
|
>Open console</a>
|
||||||
</header>
|
</header>
|
||||||
<dl class="resource-meta">
|
<dl class="resource-meta">
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { redirect } from "@sveltejs/kit";
|
||||||
|
import { loadJson, workspaceApiPath } from "$lib/workspace/api/http";
|
||||||
|
import {
|
||||||
|
canonicalResourceReference,
|
||||||
|
resourceKey,
|
||||||
|
} from "$lib/workspace/resource-links";
|
||||||
|
import type { Worker } from "$lib/workspace/sidebar/types";
|
||||||
|
import type { PageLoad } from "./$types";
|
||||||
|
|
||||||
|
export const load = (async ({ fetch, params }) => {
|
||||||
|
const reference = resourceKey(params.workerRef);
|
||||||
|
const result = await loadJson<Worker>(
|
||||||
|
fetch,
|
||||||
|
workspaceApiPath(
|
||||||
|
params.workspaceId,
|
||||||
|
`/workers/${encodeURIComponent(reference)}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (result.data?.resource_key) {
|
||||||
|
const canonical = canonicalResourceReference(
|
||||||
|
result.data.resource_key,
|
||||||
|
result.data.display_name,
|
||||||
|
);
|
||||||
|
if (params.workerRef !== canonical) {
|
||||||
|
redirect(
|
||||||
|
308,
|
||||||
|
`/w/${encodeURIComponent(params.workspaceId)}/workers/${encodeURIComponent(canonical)}/console`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
workspaceId: params.workspaceId,
|
||||||
|
runtimeId: result.data?.runtime_id ?? "",
|
||||||
|
workerId: result.data?.worker_id ?? "",
|
||||||
|
worker: result.data,
|
||||||
|
workerError: result.error,
|
||||||
|
};
|
||||||
|
}) satisfies PageLoad;
|
||||||
@@ -186,7 +186,7 @@ Deno.test("Composer input uses boundary-aware Up and Down history navigation", a
|
|||||||
);
|
);
|
||||||
const consoleSource = await Deno.readTextFile(
|
const consoleSource = await Deno.readTextFile(
|
||||||
new URL(
|
new URL(
|
||||||
"../src/routes/w/[workspaceId]/runtimes/[runtimeId]/workers/[workerId]/console/+page.svelte",
|
"../src/routes/w/[workspaceId]/workers/[workerRef]/console/+page.svelte",
|
||||||
import.meta.url,
|
import.meta.url,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ Deno.test("sidebar running status reuses the green symbol spinner", async () =>
|
|||||||
Deno.test("running status is Composer-side above mini Tasks", async () => {
|
Deno.test("running status is Composer-side above mini Tasks", async () => {
|
||||||
const page = await Deno.readTextFile(
|
const page = await Deno.readTextFile(
|
||||||
new URL(
|
new URL(
|
||||||
"../src/routes/w/[workspaceId]/runtimes/[runtimeId]/workers/[workerId]/console/+page.svelte",
|
"../src/routes/w/[workspaceId]/workers/[workerRef]/console/+page.svelte",
|
||||||
import.meta.url,
|
import.meta.url,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ function assert(condition: unknown, message: string): asserts condition {
|
|||||||
Deno.test("Worker Console exposes Overview and Normal display modes", async () => {
|
Deno.test("Worker Console exposes Overview and Normal display modes", async () => {
|
||||||
const page = await Deno.readTextFile(
|
const page = await Deno.readTextFile(
|
||||||
new URL(
|
new URL(
|
||||||
"../src/routes/w/[workspaceId]/runtimes/[runtimeId]/workers/[workerId]/console/+page.svelte",
|
"../src/routes/w/[workspaceId]/workers/[workerRef]/console/+page.svelte",
|
||||||
import.meta.url,
|
import.meta.url,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user