diff --git a/console/index.html b/console/index.html index f3321b1..ca357aa 100644 --- a/console/index.html +++ b/console/index.html @@ -92,17 +92,41 @@ - - +
+ + + +
+
diff --git a/console/src/deploy.ts b/console/src/deploy.ts index 0e181bf..e190f3b 100644 --- a/console/src/deploy.ts +++ b/console/src/deploy.ts @@ -37,6 +37,16 @@ function fillOptions(sel: HTMLSelectElement, names: string[], keepNoneFirst: boo sel.innerHTML = opts.join(""); } +// list_k8s_contexts / list_namespaces response shapes (oab-mcp, studio#104) — +// kept minimal (just what this panel reads), not the tools' full contract. +interface K8sContextsResponse { + contexts: { name: string }[]; + current_context: string | null; +} +interface K8sNamespacesResponse { + namespaces: string[]; +} + export type DeployMode = { kind: "new-fleet" } | { kind: "add-instance"; fleetName: string }; // What the panel reports back once a deploy + fleets.toml write both succeed — @@ -75,9 +85,18 @@ export function initDeployPanel(deps: DeployPanelDeps): DeployPanelHandle | null const cancelBtn = document.getElementById("deploy-cancel") as HTMLButtonElement | null; const identityForm = document.getElementById("deploy-identity-form") as HTMLFormElement | null; const nameInput = document.getElementById("deploy-fleet-name") as HTMLInputElement | null; + const providerSel = document.getElementById("deploy-provider") as HTMLSelectElement | null; + const awsFieldsEl = document.getElementById("deploy-aws-fields"); const regionInput = document.getElementById("deploy-region") as HTMLInputElement | null; const profileInput = document.getElementById("deploy-profile") as HTMLInputElement | null; const principalInput = document.getElementById("deploy-principal") as HTMLInputElement | null; + const k8sFieldsEl = document.getElementById("deploy-k8s-fields"); + const k8sContextSel = document.getElementById("deploy-k8s-context") as HTMLSelectElement | null; + // Namespace is a text input with a of what already exists (not a + // back to its + // `selected` default ("aws"), but doesn't touch the field-group `hidden` + // attributes this panel manages by hand — sync those too. + showProviderFields(providerSel.value); + }; + + // Toggle the AWS/k8s field groups per studio#104's design: switching + // providers resets which group is visible; field *values* aren't cleared + // here (identityForm.reset() already did that on open/close) since the + // two groups have no overlapping semantics to accidentally carry over. + const showProviderFields = (provider: string): void => { + awsFieldsEl.hidden = provider !== "aws"; + k8sFieldsEl.hidden = provider !== "k8s"; + }; + + const loadK8sNamespaces = async (): Promise => { + const invoke = tauriInvoke(); + if (!invoke) return; + const context = k8sContextSel.value || undefined; + try { + const res = await invoke( + "list_namespaces", + context ? { context } : {}, + ); + k8sNamespaceOptions.innerHTML = res.namespaces + .map((n) => ``) + .join(""); + } catch (e) { + // Non-fatal: the namespace field is a free-text input either way (list_ + // namespaces failing just means no autocomplete suggestions). + setStatus(identityStatusEl, `namespace list unavailable: ${errText(e)}`, "err"); + } }; + const loadK8sContexts = async (): Promise => { + const invoke = tauriInvoke(); + if (!invoke) return; + try { + const res = await invoke("list_k8s_contexts"); + const opts = ['']; + for (const c of res.contexts) { + const label = c.name === res.current_context ? `${c.name} (current)` : c.name; + opts.push(``); + } + k8sContextSel.innerHTML = opts.join(""); + } catch (e) { + setStatus(identityStatusEl, `k8s context list unavailable: ${errText(e)}`, "err"); + } + void loadK8sNamespaces(); + }; + + providerSel.addEventListener("change", () => { + showProviderFields(providerSel.value); + if (providerSel.value === "k8s") void loadK8sContexts(); + }); + k8sContextSel.addEventListener("change", () => void loadK8sNamespaces()); + const loadLibraryAndPickers = async (): Promise => { const invoke = tauriInvoke(); if (!invoke) { @@ -184,6 +264,18 @@ export function initDeployPanel(deps: DeployPanelDeps): DeployPanelHandle | null setStatus(identityStatusEl, "fleet name is required", "err"); return; } + // k8s provisioning isn't wired end-to-end yet (deploy_provision has no + // k8s dispatch — see openabdev/studio#104's discussion of why this isn't + // just "swap the driver"). Block here rather than let the wizard proceed + // into a Compose step that would fail at the final deploy_provision call. + if (providerSel.value === "k8s") { + setStatus( + identityStatusEl, + "Kubernetes provisioning isn't available yet — tracked in openabdev/studio#104", + "err", + ); + return; + } identityForm.hidden = true; composeSection.hidden = false; if (composeHeading) composeHeading.textContent = "Step 2 — first instance";