From 2e2e6fae992cea351c03fe030e8d8b94a2d6ad03 Mon Sep 17 00:00:00 2001 From: wesgreen Date: Mon, 24 Aug 2026 15:23:51 -0400 Subject: [PATCH 01/16] feat(web-console): Adding buildSandboxConnectCommand to the gateway connections, enables the syntax for displaying sandbox connect to the Hypershell UI --- .../src/gateways/gateway-connections.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/gateway-management-ui/src/gateways/gateway-connections.ts b/packages/gateway-management-ui/src/gateways/gateway-connections.ts index 43ec8b0c..2e6b6b12 100644 --- a/packages/gateway-management-ui/src/gateways/gateway-connections.ts +++ b/packages/gateway-management-ui/src/gateways/gateway-connections.ts @@ -162,6 +162,12 @@ export function buildSandboxCreateCommand( return `${variable}\n\n${command}`; } +export function buildSandboxConnectCommand( + name: string = sandboxName, +): string { + return `openshell sandbox connect --name ${shellArgument(name)}`; +} + /** * One-time setup script that logs in to the gateway, adds the Claude on Vertex AI * provider, and selects the model, combined into a single copyable block so From 6afc6b36c1389abf6fa63454543705e7edca48ca Mon Sep 17 00:00:00 2001 From: wesgreen Date: Mon, 24 Aug 2026 15:40:22 -0400 Subject: [PATCH 02/16] feat(web-console): Adding internalization strings to display in the gateway UI --- packages/gateway-management-ui/src/messages.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/gateway-management-ui/src/messages.ts b/packages/gateway-management-ui/src/messages.ts index 01ef91e4..01f9847f 100644 --- a/packages/gateway-management-ui/src/messages.ts +++ b/packages/gateway-management-ui/src/messages.ts @@ -113,6 +113,17 @@ export const messages = defineMessages({ description: "Shown in the login step while the gateway has not yet reached a running, ready-to-connect phase.", }, + connectionSandboxConnectDescription: { + id: "app.gateway.connection.sandboxConnect.description", + defaultMessage: + "Already have a sandbox from an earlier session? Run this to reattach.", + description: "Supporting text for the connect-to-sandbox connection step.", + }, + connectionSandboxConnectTitle: { + id: "app.gateway.connection.sandboxConnect.title", + defaultMessage: "Connect to a sandbox", + description: "Title for the connect-to-sandbox connection step.", + }, connectionSandboxDescription: { id: "app.gateway.connection.sandbox.description", defaultMessage: @@ -177,6 +188,11 @@ export const messages = defineMessages({ defaultMessage: "Copy the create-sandbox command", description: "Accessible label for copying the create-sandbox command.", }, + copySandboxConnectCommand: { + id: "app.gateway.connection.copySandboxConnectCommand", + defaultMessage: "Copy the connect-sandbox command", + description: "Accessible label for copying the connect-sandbox command.", + }, copySetupCommand: { id: "app.gateway.connection.copySetupCommand", defaultMessage: "Copy the one-time setup commands", From 0096a6eca465efe809076166bba8b9b1c24af8c1 Mon Sep 17 00:00:00 2001 From: wesgreen Date: Mon, 24 Aug 2026 15:49:42 -0400 Subject: [PATCH 03/16] feat(web-console): Adding the associated UI elements to the gateway page, shared naming state between the create and connect setup sections for parity --- .../src/gateways/gateway-connection-steps.tsx | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/packages/gateway-management-ui/src/gateways/gateway-connection-steps.tsx b/packages/gateway-management-ui/src/gateways/gateway-connection-steps.tsx index d8908330..a09881b9 100644 --- a/packages/gateway-management-ui/src/gateways/gateway-connection-steps.tsx +++ b/packages/gateway-management-ui/src/gateways/gateway-connection-steps.tsx @@ -12,6 +12,7 @@ import { useIntl } from "react-intl"; import { messages } from "../messages"; import { EditableCommand } from "./editable-command"; import { + buildSandboxConnectCommand, buildSandboxCreateCommand, buildSetupScript, claudeModel, @@ -32,6 +33,7 @@ const modelMarker = "OSMODELNAMEZ"; const sandboxMarker = "OSSANDBOXNAMEZ"; const setupMarkers = [providerMarker, modelMarker]; const sandboxMarkers = [sandboxMarker, modelMarker]; +const sandboxConnectMarkers = [sandboxMarker]; function ConnectionStep({ children, @@ -161,6 +163,29 @@ export function GatewayConnectionSteps({ values={{ [modelMarker]: model, [sandboxMarker]: sandboxName }} /> + + + { + if (marker === sandboxMarker) { + setSandboxName(value); + } + }} + templateCommand={buildSandboxConnectCommand(sandboxMarker)} + values={{ [sandboxMarker]: sandboxName }} + /> + ); } From 33b5df7b7280e16d2611e80085cfb0bc18a96599 Mon Sep 17 00:00:00 2001 From: wesgreen Date: Fri, 4 Sep 2026 14:22:35 -0400 Subject: [PATCH 04/16] Reconciling rebase errors --- .../gateway-connection-steps.test.tsx | 54 +++++++++++++++++-- .../src/gateways/gateway-connection-steps.tsx | 4 +- .../src/gateways/gateway-connections.test.ts | 19 +++++++ .../src/gateways/gateway-connections.ts | 4 +- .../gateway-management-ui/src/messages.ts | 6 +++ 5 files changed, 79 insertions(+), 8 deletions(-) diff --git a/packages/gateway-management-ui/src/gateways/gateway-connection-steps.test.tsx b/packages/gateway-management-ui/src/gateways/gateway-connection-steps.test.tsx index 173aa90e..66555d2b 100644 --- a/packages/gateway-management-ui/src/gateways/gateway-connection-steps.test.tsx +++ b/packages/gateway-management-ui/src/gateways/gateway-connection-steps.test.tsx @@ -5,6 +5,7 @@ import { describe, expect, it } from "vitest"; import { GatewayConnectionSteps } from "./gateway-connection-steps"; import { + buildSandboxConnectCommand, buildSandboxCreateCommand, type GatewayConnection, installDocsUrl, @@ -32,7 +33,7 @@ function renderSteps(gateway: GatewayConnection) { } describe("GatewayConnectionSteps", () => { - it("renders the setup and sandbox steps", () => { + it("renders the setup, sandbox create, and sandbox connect steps", () => { renderSteps(readyGateway); expect( @@ -41,6 +42,9 @@ describe("GatewayConnectionSteps", () => { expect( screen.getByRole("heading", { name: "Create a sandbox" }), ).toBeTruthy(); + expect( + screen.getByRole("heading", { name: "Connect to a sandbox" }), + ).toBeTruthy(); }); it("renders a prerequisite alert with an install docs link", () => { @@ -62,11 +66,11 @@ describe("GatewayConnectionSteps", () => { expect(link.textContent).toContain("Install the OpenShell CLI"); }); - it("highlights both command blocks with Shiki once they resolve", async () => { + it("highlights all command blocks with Shiki once they resolve", async () => { const { container } = renderSteps(readyGateway); await waitFor(() => { - expect(container.querySelectorAll(".shiki").length).toBe(2); + expect(container.querySelectorAll(".shiki").length).toBe(3); }); }); @@ -88,7 +92,7 @@ describe("GatewayConnectionSteps", () => { const { container } = renderSteps(readyGateway); await waitFor(() => { - expect(container.querySelectorAll(".shiki").length).toBe(2); + expect(container.querySelectorAll(".shiki").length).toBe(3); }); const providerFields = screen.getAllByRole("textbox", { @@ -134,6 +138,48 @@ describe("GatewayConnectionSteps", () => { ); }); + it("copies the raw sandbox connect command", async () => { + const user = userEvent.setup(); + renderSteps(readyGateway); + + await user.click( + screen.getByRole("button", { + name: "Copy the connect-sandbox command", + }), + ); + + expect(await navigator.clipboard.readText()).toBe( + buildSandboxConnectCommand(), + ); + }); + + it("mirrors an edited sandbox name into both create and connect commands", async () => { + const user = userEvent.setup(); + renderSteps(readyGateway); + + const createField = await screen.findByRole("textbox", { + name: "Sandbox name (editable)", + }); + createField.textContent = "scratch"; + fireEvent.input(createField); + + await user.click( + screen.getByRole("button", { name: "Copy the create-sandbox command" }), + ); + expect(await navigator.clipboard.readText()).toBe( + buildSandboxCreateCommand("scratch"), + ); + + await user.click( + screen.getByRole("button", { + name: "Copy the connect-sandbox command", + }), + ); + expect(await navigator.clipboard.readText()).toBe( + buildSandboxConnectCommand("scratch"), + ); + }); + it("shows a pending placeholder until the gateway is ready", () => { renderSteps({ ...readyGateway, diff --git a/packages/gateway-management-ui/src/gateways/gateway-connection-steps.tsx b/packages/gateway-management-ui/src/gateways/gateway-connection-steps.tsx index a09881b9..2913a339 100644 --- a/packages/gateway-management-ui/src/gateways/gateway-connection-steps.tsx +++ b/packages/gateway-management-ui/src/gateways/gateway-connection-steps.tsx @@ -174,7 +174,9 @@ export function GatewayConnectionSteps({ copyAriaLabel={intl.formatMessage(messages.copySandboxConnectCommand)} copyText={buildSandboxConnectCommand(sandboxName)} labels={{ - [sandboxMarker]: intl.formatMessage(messages.editSandboxName), + [sandboxMarker]: intl.formatMessage( + messages.editExistingSandboxName, + ), }} markers={sandboxConnectMarkers} onFieldChange={(marker, value) => { diff --git a/packages/gateway-management-ui/src/gateways/gateway-connections.test.ts b/packages/gateway-management-ui/src/gateways/gateway-connections.test.ts index cefee386..50e2e474 100644 --- a/packages/gateway-management-ui/src/gateways/gateway-connections.test.ts +++ b/packages/gateway-management-ui/src/gateways/gateway-connections.test.ts @@ -4,6 +4,7 @@ import { buildGatewayAddCommand, buildInferenceSetCommand, buildProviderCreateCommand, + buildSandboxConnectCommand, buildSandboxCreateCommand, buildSetupScript, claudeModel, @@ -196,6 +197,24 @@ describe("gateway connections", () => { ); }); + it("builds a sandbox connect command with the default name", () => { + expect(buildSandboxConnectCommand()).toBe( + `openshell sandbox connect --name ${sandboxName}`, + ); + }); + + it("substitutes a custom name into the sandbox connect command", () => { + expect(buildSandboxConnectCommand("demo")).toBe( + "openshell sandbox connect --name demo", + ); + }); + + it("quotes shell-unsafe names in the sandbox connect command", () => { + expect(buildSandboxConnectCommand("my $(sandbox)")).toBe( + `openshell sandbox connect --name 'my $(sandbox)'`, + ); + }); + it("combines login, provider, and inference into one setup script when ready", () => { const script = buildSetupScript(gateway); diff --git a/packages/gateway-management-ui/src/gateways/gateway-connections.ts b/packages/gateway-management-ui/src/gateways/gateway-connections.ts index 2e6b6b12..f8614f48 100644 --- a/packages/gateway-management-ui/src/gateways/gateway-connections.ts +++ b/packages/gateway-management-ui/src/gateways/gateway-connections.ts @@ -162,9 +162,7 @@ export function buildSandboxCreateCommand( return `${variable}\n\n${command}`; } -export function buildSandboxConnectCommand( - name: string = sandboxName, -): string { +export function buildSandboxConnectCommand(name: string = sandboxName): string { return `openshell sandbox connect --name ${shellArgument(name)}`; } diff --git a/packages/gateway-management-ui/src/messages.ts b/packages/gateway-management-ui/src/messages.ts index 01f9847f..29a9e846 100644 --- a/packages/gateway-management-ui/src/messages.ts +++ b/packages/gateway-management-ui/src/messages.ts @@ -237,6 +237,12 @@ export const messages = defineMessages({ defaultMessage: "Details", description: "Label for the gateway detail Details tab.", }, + editExistingSandboxName: { + id: "app.gateway.connection.editExistingSandboxName", + defaultMessage: "Existing sandbox name (editable)", + description: + "Accessible label for the inline-editable sandbox name in the connect-sandbox command.", + }, editModel: { id: "app.gateway.connection.editModel", defaultMessage: "Model (editable)", From 4464914f1e688fb49e73e487dd47a8d66b22f93f Mon Sep 17 00:00:00 2001 From: wesgreen Date: Fri, 4 Sep 2026 14:25:34 -0400 Subject: [PATCH 05/16] Reconciling rebase errors --- .../src/gateways/gateway-connections.test.ts | 6 +++--- .../src/gateways/gateway-connections.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/gateway-management-ui/src/gateways/gateway-connections.test.ts b/packages/gateway-management-ui/src/gateways/gateway-connections.test.ts index 50e2e474..2b0a1ae5 100644 --- a/packages/gateway-management-ui/src/gateways/gateway-connections.test.ts +++ b/packages/gateway-management-ui/src/gateways/gateway-connections.test.ts @@ -199,19 +199,19 @@ describe("gateway connections", () => { it("builds a sandbox connect command with the default name", () => { expect(buildSandboxConnectCommand()).toBe( - `openshell sandbox connect --name ${sandboxName}`, + `openshell sandbox connect ${sandboxName}`, ); }); it("substitutes a custom name into the sandbox connect command", () => { expect(buildSandboxConnectCommand("demo")).toBe( - "openshell sandbox connect --name demo", + "openshell sandbox connect demo", ); }); it("quotes shell-unsafe names in the sandbox connect command", () => { expect(buildSandboxConnectCommand("my $(sandbox)")).toBe( - `openshell sandbox connect --name 'my $(sandbox)'`, + `openshell sandbox connect 'my $(sandbox)'`, ); }); diff --git a/packages/gateway-management-ui/src/gateways/gateway-connections.ts b/packages/gateway-management-ui/src/gateways/gateway-connections.ts index f8614f48..c76b0391 100644 --- a/packages/gateway-management-ui/src/gateways/gateway-connections.ts +++ b/packages/gateway-management-ui/src/gateways/gateway-connections.ts @@ -163,7 +163,7 @@ export function buildSandboxCreateCommand( } export function buildSandboxConnectCommand(name: string = sandboxName): string { - return `openshell sandbox connect --name ${shellArgument(name)}`; + return `openshell sandbox connect ${shellArgument(name)}`; } /** From 55eecc5455a0586633eba7d958dbd1db58f3f1f0 Mon Sep 17 00:00:00 2001 From: wesgreen Date: Fri, 4 Sep 2026 14:27:37 -0400 Subject: [PATCH 06/16] Reconciling rebase errors --- .../src/gateways/editable-command.tsx | 2 - .../src/gateways/gateway-connection-steps.tsx | 45 +++++++++++++++++-- .../src/gateways/gateway-connections.test.ts | 16 +++++-- .../src/gateways/gateway-connections.ts | 16 +++++++ .../gateway-management-ui/src/messages.ts | 39 ++++++++++++++++ 5 files changed, 109 insertions(+), 9 deletions(-) diff --git a/packages/gateway-management-ui/src/gateways/editable-command.tsx b/packages/gateway-management-ui/src/gateways/editable-command.tsx index a8d44a15..1e007437 100644 --- a/packages/gateway-management-ui/src/gateways/editable-command.tsx +++ b/packages/gateway-management-ui/src/gateways/editable-command.tsx @@ -88,8 +88,6 @@ function EditableField({ contentEditable="plaintext-only" onInput={handleInput} onKeyDown={(event) => { - // The command is a single logical line per field; a newline would break - // both the layout and the shell command, so swallow Enter. if (event.key === "Enter") { event.preventDefault(); } diff --git a/packages/gateway-management-ui/src/gateways/gateway-connection-steps.tsx b/packages/gateway-management-ui/src/gateways/gateway-connection-steps.tsx index 2913a339..241171b4 100644 --- a/packages/gateway-management-ui/src/gateways/gateway-connection-steps.tsx +++ b/packages/gateway-management-ui/src/gateways/gateway-connection-steps.tsx @@ -16,8 +16,10 @@ import { buildSandboxCreateCommand, buildSetupScript, claudeModel, + defaultEditor, type GatewayConnection, installDocsUrl, + sandboxConnectDocsUrl, sandboxName as defaultSandboxName, vertexProviderName, } from "./gateway-connections"; @@ -31,9 +33,10 @@ import styles from "./gateway-connection-steps.module.css"; const providerMarker = "OSPROVIDERNAMEZ"; const modelMarker = "OSMODELNAMEZ"; const sandboxMarker = "OSSANDBOXNAMEZ"; +const editorMarker = "OSEDITORNAMEZ"; const setupMarkers = [providerMarker, modelMarker]; const sandboxMarkers = [sandboxMarker, modelMarker]; -const sandboxConnectMarkers = [sandboxMarker]; +const sandboxConnectMarkers = [sandboxMarker, editorMarker]; function ConnectionStep({ children, @@ -64,6 +67,7 @@ export function GatewayConnectionSteps({ const [providerName, setProviderName] = useState(vertexProviderName); const [model, setModel] = useState(claudeModel); const [sandboxName, setSandboxName] = useState(defaultSandboxName); + const [editor, setEditor] = useState(defaultEditor); // Marker form drives the (stable) highlight; the resolved form drives copy and // matches a whole-block text selection exactly. @@ -170,10 +174,38 @@ export function GatewayConnectionSteps({ )} title={intl.formatMessage(messages.connectionSandboxConnectTitle)} > + } + iconPosition="end" + rel="noopener noreferrer" + target="_blank" + > + {intl.formatMessage(messages.connectionEditorOptionsLink)} + + } + className={styles.prereqAlert} + component="h3" + isInline + title={intl.formatMessage(messages.connectionEditorOptionsTitle)} + variant="info" + > + {intl.formatMessage(messages.connectionEditorOptions, { + cursor: cursor, + vscode: vscode, + })} + { if (marker === sandboxMarker) { setSandboxName(value); + } else if (marker === editorMarker) { + setEditor(value); } }} - templateCommand={buildSandboxConnectCommand(sandboxMarker)} - values={{ [sandboxMarker]: sandboxName }} + templateCommand={buildSandboxConnectCommand( + sandboxMarker, + editorMarker, + )} + values={{ [editorMarker]: editor, [sandboxMarker]: sandboxName }} /> diff --git a/packages/gateway-management-ui/src/gateways/gateway-connections.test.ts b/packages/gateway-management-ui/src/gateways/gateway-connections.test.ts index 2b0a1ae5..68f9b4d7 100644 --- a/packages/gateway-management-ui/src/gateways/gateway-connections.test.ts +++ b/packages/gateway-management-ui/src/gateways/gateway-connections.test.ts @@ -180,6 +180,7 @@ describe("gateway connections", () => { expect(cmd).toContain("-- claude --bare --model claude-opus-5"); }); +<<<<<<< HEAD it("quotes sandbox names that contain shell metacharacters", () => { const cmd = buildSandboxCreateCommand("my sandbox"); expect(cmd).toContain("--name 'my sandbox'"); @@ -198,20 +199,29 @@ describe("gateway connections", () => { }); it("builds a sandbox connect command with the default name", () => { +======= + it("builds a sandbox connect command with defaults", () => { +>>>>>>> b61d600 (feat(web-console): Adding an info alert for OpenShell connect) expect(buildSandboxConnectCommand()).toBe( - `openshell sandbox connect ${sandboxName}`, + `openshell sandbox connect ${sandboxName} \\\n --editor cursor`, ); }); it("substitutes a custom name into the sandbox connect command", () => { expect(buildSandboxConnectCommand("demo")).toBe( - "openshell sandbox connect demo", + "openshell sandbox connect demo \\\n --editor cursor", + ); + }); + + it("substitutes a custom editor into the sandbox connect command", () => { + expect(buildSandboxConnectCommand("demo", "vscode")).toBe( + "openshell sandbox connect demo \\\n --editor vscode", ); }); it("quotes shell-unsafe names in the sandbox connect command", () => { expect(buildSandboxConnectCommand("my $(sandbox)")).toBe( - `openshell sandbox connect 'my $(sandbox)'`, + `openshell sandbox connect 'my $(sandbox)' \\\n --editor cursor`, ); }); diff --git a/packages/gateway-management-ui/src/gateways/gateway-connections.ts b/packages/gateway-management-ui/src/gateways/gateway-connections.ts index c76b0391..951b955e 100644 --- a/packages/gateway-management-ui/src/gateways/gateway-connections.ts +++ b/packages/gateway-management-ui/src/gateways/gateway-connections.ts @@ -77,6 +77,9 @@ export const vertexProviderName = "my-gcp"; export const installDocsUrl = "https://docs.nvidia.com/openshell/about/installation"; +export const sandboxConnectDocsUrl = + "https://nvidia-openshell.mintlify.app/reference/cli-sandbox#openshell-sandbox-connect"; + /** Default sandbox name shown in the copyable create-sandbox command. */ export const sandboxName = "mysand"; @@ -162,8 +165,21 @@ export function buildSandboxCreateCommand( return `${variable}\n\n${command}`; } +<<<<<<< HEAD export function buildSandboxConnectCommand(name: string = sandboxName): string { return `openshell sandbox connect ${shellArgument(name)}`; +======= +export const defaultEditor = "cursor"; + +export function buildSandboxConnectCommand( + name: string = sandboxName, + editor: string = defaultEditor, +): string { + return [ + `openshell sandbox connect ${shellArgument(name)}`, + `--editor ${shellArgument(editor)}`, + ].join(" \\\n "); +>>>>>>> b61d600 (feat(web-console): Adding an info alert for OpenShell connect) } /** diff --git a/packages/gateway-management-ui/src/messages.ts b/packages/gateway-management-ui/src/messages.ts index 29a9e846..28699000 100644 --- a/packages/gateway-management-ui/src/messages.ts +++ b/packages/gateway-management-ui/src/messages.ts @@ -255,6 +255,45 @@ export const messages = defineMessages({ description: "Accessible label for the inline-editable provider name in the setup command.", }, +<<<<<<< HEAD +======= + connectionEditorOptions: { + id: "app.gateway.connection.editorOptions", + defaultMessage: + "Supported interactive editors are {vscode} and {cursor}.", + description: + "Info note listing the supported editor values for the connect-sandbox command. {vscode} and {cursor} are code-formatted editor names.", + }, + connectionEditorOptionsLink: { + id: "app.gateway.connection.editorOptionsLink", + defaultMessage: "OpenShell connect documentation", + description: + "Link text pointing to the OpenShell sandbox connect CLI documentation.", + }, + connectionEditorOptionsLinkNewTab: { + id: "app.gateway.connection.editorOptionsLinkNewTab", + defaultMessage: "Sandbox connect reference (opens in a new tab)", + description: + "Accessible name for the sandbox connect docs link, including that it opens in a new tab.", + }, + connectionEditorOptionsTitle: { + id: "app.gateway.connection.editorOptionsTitle", + defaultMessage: "Editor options", + description: "Title for the editor options info alert.", + }, + editEditor: { + id: "app.gateway.connection.editEditor", + defaultMessage: "Editor (editable)", + description: + "Accessible label for the inline-editable editor name in the connect-sandbox command.", + }, + editExistingSandboxName: { + id: "app.gateway.connection.editExistingSandboxName", + defaultMessage: "Existing sandbox name (editable)", + description: + "Accessible label for the inline-editable sandbox name in the connect-sandbox command.", + }, +>>>>>>> b61d600 (feat(web-console): Adding an info alert for OpenShell connect) editSandboxName: { id: "app.gateway.connection.editSandboxName", defaultMessage: "Sandbox name (editable)", From d203fad5366ee46e2fab98b3926440e7840bb252 Mon Sep 17 00:00:00 2001 From: wesgreen Date: Fri, 4 Sep 2026 15:20:27 -0400 Subject: [PATCH 07/16] Reconciling rebase errors --- .../src/gateways/gateway-connection-steps.tsx | 21 ++---- .../src/gateways/gateway-connections.test.ts | 16 +---- .../src/gateways/gateway-connections.ts | 16 +---- .../gateway-management-ui/src/messages.ts | 69 ++++++++----------- 4 files changed, 41 insertions(+), 81 deletions(-) diff --git a/packages/gateway-management-ui/src/gateways/gateway-connection-steps.tsx b/packages/gateway-management-ui/src/gateways/gateway-connection-steps.tsx index 241171b4..d70b53b3 100644 --- a/packages/gateway-management-ui/src/gateways/gateway-connection-steps.tsx +++ b/packages/gateway-management-ui/src/gateways/gateway-connection-steps.tsx @@ -16,7 +16,6 @@ import { buildSandboxCreateCommand, buildSetupScript, claudeModel, - defaultEditor, type GatewayConnection, installDocsUrl, sandboxConnectDocsUrl, @@ -33,10 +32,9 @@ import styles from "./gateway-connection-steps.module.css"; const providerMarker = "OSPROVIDERNAMEZ"; const modelMarker = "OSMODELNAMEZ"; const sandboxMarker = "OSSANDBOXNAMEZ"; -const editorMarker = "OSEDITORNAMEZ"; const setupMarkers = [providerMarker, modelMarker]; const sandboxMarkers = [sandboxMarker, modelMarker]; -const sandboxConnectMarkers = [sandboxMarker, editorMarker]; +const sandboxConnectMarkers = [sandboxMarker]; function ConnectionStep({ children, @@ -67,8 +65,6 @@ export function GatewayConnectionSteps({ const [providerName, setProviderName] = useState(vertexProviderName); const [model, setModel] = useState(claudeModel); const [sandboxName, setSandboxName] = useState(defaultSandboxName); - const [editor, setEditor] = useState(defaultEditor); - // Marker form drives the (stable) highlight; the resolved form drives copy and // matches a whole-block text selection exactly. const setupTemplate = buildSetupScript(gateway, { @@ -197,15 +193,13 @@ export function GatewayConnectionSteps({ variant="info" > {intl.formatMessage(messages.connectionEditorOptions, { - cursor: cursor, - vscode: vscode, + code: (chunks) => {chunks}, })} { if (marker === sandboxMarker) { setSandboxName(value); - } else if (marker === editorMarker) { - setEditor(value); } }} - templateCommand={buildSandboxConnectCommand( - sandboxMarker, - editorMarker, - )} - values={{ [editorMarker]: editor, [sandboxMarker]: sandboxName }} + templateCommand={buildSandboxConnectCommand(sandboxMarker)} + values={{ [sandboxMarker]: sandboxName }} /> diff --git a/packages/gateway-management-ui/src/gateways/gateway-connections.test.ts b/packages/gateway-management-ui/src/gateways/gateway-connections.test.ts index 68f9b4d7..2b0a1ae5 100644 --- a/packages/gateway-management-ui/src/gateways/gateway-connections.test.ts +++ b/packages/gateway-management-ui/src/gateways/gateway-connections.test.ts @@ -180,7 +180,6 @@ describe("gateway connections", () => { expect(cmd).toContain("-- claude --bare --model claude-opus-5"); }); -<<<<<<< HEAD it("quotes sandbox names that contain shell metacharacters", () => { const cmd = buildSandboxCreateCommand("my sandbox"); expect(cmd).toContain("--name 'my sandbox'"); @@ -199,29 +198,20 @@ describe("gateway connections", () => { }); it("builds a sandbox connect command with the default name", () => { -======= - it("builds a sandbox connect command with defaults", () => { ->>>>>>> b61d600 (feat(web-console): Adding an info alert for OpenShell connect) expect(buildSandboxConnectCommand()).toBe( - `openshell sandbox connect ${sandboxName} \\\n --editor cursor`, + `openshell sandbox connect ${sandboxName}`, ); }); it("substitutes a custom name into the sandbox connect command", () => { expect(buildSandboxConnectCommand("demo")).toBe( - "openshell sandbox connect demo \\\n --editor cursor", - ); - }); - - it("substitutes a custom editor into the sandbox connect command", () => { - expect(buildSandboxConnectCommand("demo", "vscode")).toBe( - "openshell sandbox connect demo \\\n --editor vscode", + "openshell sandbox connect demo", ); }); it("quotes shell-unsafe names in the sandbox connect command", () => { expect(buildSandboxConnectCommand("my $(sandbox)")).toBe( - `openshell sandbox connect 'my $(sandbox)' \\\n --editor cursor`, + `openshell sandbox connect 'my $(sandbox)'`, ); }); diff --git a/packages/gateway-management-ui/src/gateways/gateway-connections.ts b/packages/gateway-management-ui/src/gateways/gateway-connections.ts index 951b955e..8cb42927 100644 --- a/packages/gateway-management-ui/src/gateways/gateway-connections.ts +++ b/packages/gateway-management-ui/src/gateways/gateway-connections.ts @@ -165,21 +165,11 @@ export function buildSandboxCreateCommand( return `${variable}\n\n${command}`; } -<<<<<<< HEAD -export function buildSandboxConnectCommand(name: string = sandboxName): string { - return `openshell sandbox connect ${shellArgument(name)}`; -======= +export const validEditors = ["cursor", "vscode"] as const; export const defaultEditor = "cursor"; -export function buildSandboxConnectCommand( - name: string = sandboxName, - editor: string = defaultEditor, -): string { - return [ - `openshell sandbox connect ${shellArgument(name)}`, - `--editor ${shellArgument(editor)}`, - ].join(" \\\n "); ->>>>>>> b61d600 (feat(web-console): Adding an info alert for OpenShell connect) +export function buildSandboxConnectCommand(name: string = sandboxName): string { + return `openshell sandbox connect ${shellArgument(name)}`; } /** diff --git a/packages/gateway-management-ui/src/messages.ts b/packages/gateway-management-ui/src/messages.ts index 28699000..66d4f154 100644 --- a/packages/gateway-management-ui/src/messages.ts +++ b/packages/gateway-management-ui/src/messages.ts @@ -82,6 +82,30 @@ export const messages = defineMessages({ defaultMessage: "Region: {region}", description: "Region context that distinguishes a managed cluster option.", }, + connectionEditorOptions: { + id: "app.gateway.connection.editorOptions", + defaultMessage: + "Supported interactive editors are vscode and cursor.", + description: + "Info note listing the supported editor values for the connect-sandbox command. Editor names are wrapped in tags.", + }, + connectionEditorOptionsLink: { + id: "app.gateway.connection.editorOptionsLink", + defaultMessage: "OpenShell connect documentation", + description: + "Link text pointing to the OpenShell sandbox connect CLI documentation.", + }, + connectionEditorOptionsLinkNewTab: { + id: "app.gateway.connection.editorOptionsLinkNewTab", + defaultMessage: "Sandbox connect reference (opens in a new tab)", + description: + "Accessible name for the sandbox connect docs link, including that it opens in a new tab.", + }, + connectionEditorOptionsTitle: { + id: "app.gateway.connection.editorOptionsTitle", + defaultMessage: "Editor options", + description: "Title for the editor options info alert.", + }, connectionInstallLink: { id: "app.gateway.connection.installLink", defaultMessage: "Install the OpenShell CLI", @@ -237,6 +261,12 @@ export const messages = defineMessages({ defaultMessage: "Details", description: "Label for the gateway detail Details tab.", }, + editEditor: { + id: "app.gateway.connection.editEditor", + defaultMessage: "Editor", + description: + "Accessible label for the editor selector in the connect-sandbox command.", + }, editExistingSandboxName: { id: "app.gateway.connection.editExistingSandboxName", defaultMessage: "Existing sandbox name (editable)", @@ -255,45 +285,6 @@ export const messages = defineMessages({ description: "Accessible label for the inline-editable provider name in the setup command.", }, -<<<<<<< HEAD -======= - connectionEditorOptions: { - id: "app.gateway.connection.editorOptions", - defaultMessage: - "Supported interactive editors are {vscode} and {cursor}.", - description: - "Info note listing the supported editor values for the connect-sandbox command. {vscode} and {cursor} are code-formatted editor names.", - }, - connectionEditorOptionsLink: { - id: "app.gateway.connection.editorOptionsLink", - defaultMessage: "OpenShell connect documentation", - description: - "Link text pointing to the OpenShell sandbox connect CLI documentation.", - }, - connectionEditorOptionsLinkNewTab: { - id: "app.gateway.connection.editorOptionsLinkNewTab", - defaultMessage: "Sandbox connect reference (opens in a new tab)", - description: - "Accessible name for the sandbox connect docs link, including that it opens in a new tab.", - }, - connectionEditorOptionsTitle: { - id: "app.gateway.connection.editorOptionsTitle", - defaultMessage: "Editor options", - description: "Title for the editor options info alert.", - }, - editEditor: { - id: "app.gateway.connection.editEditor", - defaultMessage: "Editor (editable)", - description: - "Accessible label for the inline-editable editor name in the connect-sandbox command.", - }, - editExistingSandboxName: { - id: "app.gateway.connection.editExistingSandboxName", - defaultMessage: "Existing sandbox name (editable)", - description: - "Accessible label for the inline-editable sandbox name in the connect-sandbox command.", - }, ->>>>>>> b61d600 (feat(web-console): Adding an info alert for OpenShell connect) editSandboxName: { id: "app.gateway.connection.editSandboxName", defaultMessage: "Sandbox name (editable)", From 28815a9efd514107f7ab5c30f020e056e6bb8931 Mon Sep 17 00:00:00 2001 From: wesgreen Date: Wed, 26 Aug 2026 15:28:21 -0400 Subject: [PATCH 08/16] fix(web-console): Fixing i18n failures, correcting web console quality checks --- components/web-console/locales/en.json | 36 ++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/components/web-console/locales/en.json b/components/web-console/locales/en.json index 5b37cb50..8609122c 100644 --- a/components/web-console/locales/en.json +++ b/components/web-console/locales/en.json @@ -123,10 +123,22 @@ "defaultMessage": "Copy the create-sandbox command", "description": "Accessible label for copying the create-sandbox command." }, + "app.gateway.connection.copySandboxConnectCommand": { + "defaultMessage": "Copy the connect-sandbox command", + "description": "Accessible label for copying the connect-sandbox command." + }, "app.gateway.connection.copySetupCommand": { "defaultMessage": "Copy the one-time setup commands", "description": "Accessible label for copying the consolidated one-time setup script." }, + "app.gateway.connection.editEditor": { + "defaultMessage": "Editor (editable)", + "description": "Accessible label for the inline-editable editor name in the connect-sandbox command." + }, + "app.gateway.connection.editExistingSandboxName": { + "defaultMessage": "Existing sandbox name (editable)", + "description": "Accessible label for the inline-editable sandbox name in the connect-sandbox command." + }, "app.gateway.connection.editModel": { "defaultMessage": "Model (editable)", "description": "Accessible label for the inline-editable model name in the setup command." @@ -139,6 +151,22 @@ "defaultMessage": "Sandbox name (editable)", "description": "Accessible label for the inline-editable sandbox name in the create-sandbox command." }, + "app.gateway.connection.editorOptions": { + "defaultMessage": "Supported interactive editors are vscode and cursor.", + "description": "Info note listing the supported editor values for the connect-sandbox command. Editor names are wrapped in tags." + }, + "app.gateway.connection.editorOptionsLink": { + "defaultMessage": "OpenShell connect documentation", + "description": "Link text pointing to the OpenShell sandbox connect CLI documentation." + }, + "app.gateway.connection.editorOptionsLinkNewTab": { + "defaultMessage": "Sandbox connect reference (opens in a new tab)", + "description": "Accessible name for the sandbox connect docs link, including that it opens in a new tab." + }, + "app.gateway.connection.editorOptionsTitle": { + "defaultMessage": "Editor options", + "description": "Title for the editor options info alert." + }, "app.gateway.connection.installLink": { "defaultMessage": "Install the OpenShell CLI", "description": "Link text pointing to the NVIDIA OpenShell installation documentation." @@ -167,6 +195,14 @@ "defaultMessage": "Create a sandbox", "description": "Title for the create-sandbox connection step." }, + "app.gateway.connection.sandboxConnect.description": { + "defaultMessage": "Already have a sandbox from an earlier session? Run this to reattach.", + "description": "Supporting text for the connect-to-sandbox connection step." + }, + "app.gateway.connection.sandboxConnect.title": { + "defaultMessage": "Connect to a sandbox", + "description": "Title for the connect-to-sandbox connection step." + }, "app.gateway.connection.setup.description": { "defaultMessage": "Run these once to log in, add the Claude on Vertex AI provider, and select the model.", "description": "Supporting text for the one-time setup connection step." From 4ca4e28d6c222d5c17b21cd1e83cd2f45e306788 Mon Sep 17 00:00:00 2001 From: wesgreen Date: Thu, 3 Sep 2026 17:45:16 -0400 Subject: [PATCH 09/16] Refactoring NVIDIA docs URL, readding comment, bumping Shiki container test expectation --- .../gateway-management-ui/src/gateways/editable-command.tsx | 2 ++ .../src/gateways/gateway-connection-steps.test.tsx | 2 +- .../gateway-management-ui/src/gateways/gateway-connections.ts | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/gateway-management-ui/src/gateways/editable-command.tsx b/packages/gateway-management-ui/src/gateways/editable-command.tsx index 1e007437..a8d44a15 100644 --- a/packages/gateway-management-ui/src/gateways/editable-command.tsx +++ b/packages/gateway-management-ui/src/gateways/editable-command.tsx @@ -88,6 +88,8 @@ function EditableField({ contentEditable="plaintext-only" onInput={handleInput} onKeyDown={(event) => { + // The command is a single logical line per field; a newline would break + // both the layout and the shell command, so swallow Enter. if (event.key === "Enter") { event.preventDefault(); } diff --git a/packages/gateway-management-ui/src/gateways/gateway-connection-steps.test.tsx b/packages/gateway-management-ui/src/gateways/gateway-connection-steps.test.tsx index 66555d2b..2a223f28 100644 --- a/packages/gateway-management-ui/src/gateways/gateway-connection-steps.test.tsx +++ b/packages/gateway-management-ui/src/gateways/gateway-connection-steps.test.tsx @@ -70,7 +70,7 @@ describe("GatewayConnectionSteps", () => { const { container } = renderSteps(readyGateway); await waitFor(() => { - expect(container.querySelectorAll(".shiki").length).toBe(3); + expect(container.querySelectorAll(".shiki").length).toBe(4); }); }); diff --git a/packages/gateway-management-ui/src/gateways/gateway-connections.ts b/packages/gateway-management-ui/src/gateways/gateway-connections.ts index 8cb42927..bc83a551 100644 --- a/packages/gateway-management-ui/src/gateways/gateway-connections.ts +++ b/packages/gateway-management-ui/src/gateways/gateway-connections.ts @@ -78,7 +78,7 @@ export const installDocsUrl = "https://docs.nvidia.com/openshell/about/installation"; export const sandboxConnectDocsUrl = - "https://nvidia-openshell.mintlify.app/reference/cli-sandbox#openshell-sandbox-connect"; + "https://docs.nvidia.com/openshell/sandboxes/manage-sandboxes#connect-to-a-sandbox"; /** Default sandbox name shown in the copyable create-sandbox command. */ export const sandboxName = "mysand"; From 741b7145f6a7cd6645506041bf742a2ff1eb21e5 Mon Sep 17 00:00:00 2001 From: wesgreen Date: Fri, 4 Sep 2026 15:23:52 -0400 Subject: [PATCH 10/16] Reconciling rebase errors --- .../src/gateways/gateway-connection-steps.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/gateway-management-ui/src/gateways/gateway-connection-steps.test.tsx b/packages/gateway-management-ui/src/gateways/gateway-connection-steps.test.tsx index 2a223f28..66555d2b 100644 --- a/packages/gateway-management-ui/src/gateways/gateway-connection-steps.test.tsx +++ b/packages/gateway-management-ui/src/gateways/gateway-connection-steps.test.tsx @@ -70,7 +70,7 @@ describe("GatewayConnectionSteps", () => { const { container } = renderSteps(readyGateway); await waitFor(() => { - expect(container.querySelectorAll(".shiki").length).toBe(4); + expect(container.querySelectorAll(".shiki").length).toBe(3); }); }); From 798e676c0f11c97974cb2a79217e65c78147ba91 Mon Sep 17 00:00:00 2001 From: wesgreen Date: Thu, 3 Sep 2026 18:48:24 -0400 Subject: [PATCH 11/16] Updating the spec to address the correct number of elements on the Gateway UI --- specs/web-console/architecture.spec.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/specs/web-console/architecture.spec.md b/specs/web-console/architecture.spec.md index ca27728a..f7193ebd 100644 --- a/specs/web-console/architecture.spec.md +++ b/specs/web-console/architecture.spec.md @@ -494,7 +494,7 @@ The selected tab SHALL use the validated `tab` search parameter per `WEB-DATA-02 The `Details` tab SHALL contain the existing gateway description list (status, cluster, endpoint, CLI connection command, namespace, release identifier, and managed-database identifier), using the same values, loading, unavailable, and placement-resolution behavior required elsewhere in this specification. -The `Connection` tab SHALL guide the user through three ordered steps using an accessible ordered structure with a visible label and description for each step: +The `Connection` tab SHALL guide the user through four ordered steps using an accessible ordered structure with a visible label and description for each step: 1. **Log in to the gateway.** The step SHALL present the `openshell gateway add` command produced from the authorized gateway response as a read-only PatternFly Clipboard Copy value, using the same command construction, shell-argument encoding, and missing-value handling required by `WEB-UI-03`. When required connection values are absent, the step SHALL explain that login is unavailable rather than present an incomplete command. @@ -502,6 +502,8 @@ The `Connection` tab SHALL guide the user through three ordered steps using an a 3. **Create a sandbox.** The step SHALL present an `openshell sandbox create` command that names a sandbox, specifies CPU and memory resource requests and limits via a `--driver-config-json` flag, attaches the provider from step 2, and launches the agent, as a read-only Clipboard Copy value. The default resource values SHALL be CPU requests `100m`, CPU limits `500m`, memory requests `512Mi`, and memory limits `512Mi`. +4. **Connect to a sandbox.** The step SHALL present an `openshell sandbox connect` command that references the sandbox name from step 3 and a supported editor value. The sandbox name field SHALL share state with the create step so both commands stay in sync. The editor field SHALL accept only supported values (`cursor`, `vscode`); unsupported input SHALL fall back to the default editor. An info alert SHALL link to the sandbox connect documentation on the canonical docs host. + The `Connection` tab SHALL also contain a secondary `Create or manage service accounts` link with its introductory content, before the walkthrough. The link SHALL set `tab=service-accounts`. It SHALL not start a create operation. The `Service accounts` tab SHALL own the gateway-scoped automation identity workflow. It SHALL include the heading, create action, responsive collection, one-time credential handoff, command groups, and lifecycle actions defined in [`openshell-gateway-service-accounts.spec.md`](../platform/openshell-gateway-service-accounts.spec.md). @@ -521,7 +523,7 @@ Every Clipboard Copy control SHALL have a localized accessible name and visible - GIVEN an authenticated user opens a gateway detail page - WHEN the page renders - THEN the `Connection` tab SHALL be selected -- AND the three ordered connection steps SHALL be visible +- AND the four ordered connection steps SHALL be visible - AND the `Service accounts` tab SHALL be available - AND the operational description list SHALL be available under the `Details` tab From 98a7e54b55e0da16b401f29791896f530187576c Mon Sep 17 00:00:00 2001 From: wesgreen Date: Thu, 3 Sep 2026 18:56:35 -0400 Subject: [PATCH 12/16] Correcting spec language for connect command --- specs/web-console/architecture.spec.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/web-console/architecture.spec.md b/specs/web-console/architecture.spec.md index f7193ebd..a57b0193 100644 --- a/specs/web-console/architecture.spec.md +++ b/specs/web-console/architecture.spec.md @@ -502,7 +502,7 @@ The `Connection` tab SHALL guide the user through four ordered steps using an ac 3. **Create a sandbox.** The step SHALL present an `openshell sandbox create` command that names a sandbox, specifies CPU and memory resource requests and limits via a `--driver-config-json` flag, attaches the provider from step 2, and launches the agent, as a read-only Clipboard Copy value. The default resource values SHALL be CPU requests `100m`, CPU limits `500m`, memory requests `512Mi`, and memory limits `512Mi`. -4. **Connect to a sandbox.** The step SHALL present an `openshell sandbox connect` command that references the sandbox name from step 3 and a supported editor value. The sandbox name field SHALL share state with the create step so both commands stay in sync. The editor field SHALL accept only supported values (`cursor`, `vscode`); unsupported input SHALL fall back to the default editor. An info alert SHALL link to the sandbox connect documentation on the canonical docs host. +4. **Connect to a sandbox.** The step SHALL present an `openshell sandbox connect` command that references the sandbox name from step 3 and an editor value. The sandbox name field SHALL share state with the create step so both commands stay in sync. The editor field defaults to `cursor`; supported values (`cursor`, `vscode`) are documented in an adjacent info alert that SHALL link to the sandbox connect documentation on the canonical docs host. The CLI validates the editor value at runtime. The `Connection` tab SHALL also contain a secondary `Create or manage service accounts` link with its introductory content, before the walkthrough. The link SHALL set `tab=service-accounts`. It SHALL not start a create operation. From b2e51fd865fbb6994c0e10fb2ddeeec69a31fe5f Mon Sep 17 00:00:00 2001 From: wesgreen Date: Fri, 4 Sep 2026 15:33:24 -0400 Subject: [PATCH 13/16] Reconciling rebase errors --- components/web-console/locales/en.json | 6 +++--- packages/gateway-management-ui/src/messages.ts | 2 +- specs/web-console/architecture.spec.md | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/components/web-console/locales/en.json b/components/web-console/locales/en.json index 8609122c..cecdcba5 100644 --- a/components/web-console/locales/en.json +++ b/components/web-console/locales/en.json @@ -132,8 +132,8 @@ "description": "Accessible label for copying the consolidated one-time setup script." }, "app.gateway.connection.editEditor": { - "defaultMessage": "Editor (editable)", - "description": "Accessible label for the inline-editable editor name in the connect-sandbox command." + "defaultMessage": "Editor", + "description": "Accessible label for the editor selector in the connect-sandbox command." }, "app.gateway.connection.editExistingSandboxName": { "defaultMessage": "Existing sandbox name (editable)", @@ -196,7 +196,7 @@ "description": "Title for the create-sandbox connection step." }, "app.gateway.connection.sandboxConnect.description": { - "defaultMessage": "Already have a sandbox from an earlier session? Run this to reattach.", + "defaultMessage": "Disconnecting does not stop the sandbox process. Connect attaches to the same process instance and replays recent output.", "description": "Supporting text for the connect-to-sandbox connection step." }, "app.gateway.connection.sandboxConnect.title": { diff --git a/packages/gateway-management-ui/src/messages.ts b/packages/gateway-management-ui/src/messages.ts index 66d4f154..bf890c52 100644 --- a/packages/gateway-management-ui/src/messages.ts +++ b/packages/gateway-management-ui/src/messages.ts @@ -140,7 +140,7 @@ export const messages = defineMessages({ connectionSandboxConnectDescription: { id: "app.gateway.connection.sandboxConnect.description", defaultMessage: - "Already have a sandbox from an earlier session? Run this to reattach.", + "Disconnecting does not stop the sandbox process. Connect attaches to the same process instance and replays recent output.", description: "Supporting text for the connect-to-sandbox connection step.", }, connectionSandboxConnectTitle: { diff --git a/specs/web-console/architecture.spec.md b/specs/web-console/architecture.spec.md index a57b0193..67a08baa 100644 --- a/specs/web-console/architecture.spec.md +++ b/specs/web-console/architecture.spec.md @@ -502,7 +502,7 @@ The `Connection` tab SHALL guide the user through four ordered steps using an ac 3. **Create a sandbox.** The step SHALL present an `openshell sandbox create` command that names a sandbox, specifies CPU and memory resource requests and limits via a `--driver-config-json` flag, attaches the provider from step 2, and launches the agent, as a read-only Clipboard Copy value. The default resource values SHALL be CPU requests `100m`, CPU limits `500m`, memory requests `512Mi`, and memory limits `512Mi`. -4. **Connect to a sandbox.** The step SHALL present an `openshell sandbox connect` command that references the sandbox name from step 3 and an editor value. The sandbox name field SHALL share state with the create step so both commands stay in sync. The editor field defaults to `cursor`; supported values (`cursor`, `vscode`) are documented in an adjacent info alert that SHALL link to the sandbox connect documentation on the canonical docs host. The CLI validates the editor value at runtime. +4. **Connect to a sandbox.** The step SHALL present an `openshell sandbox connect` command that references the sandbox name from step 3 and an editor value. The sandbox name field SHALL share state with the create step so both commands stay in sync. The editor field SHALL be a constrained inline selector defaulting to `cursor`, offering only the supported values (`cursor`, `vscode`). An adjacent info alert SHALL document the supported values and link to the sandbox connect documentation on the canonical docs host. The `Connection` tab SHALL also contain a secondary `Create or manage service accounts` link with its introductory content, before the walkthrough. The link SHALL set `tab=service-accounts`. It SHALL not start a create operation. From 3c46a82884e96ba609e5e2ccf81a69ef5e219981 Mon Sep 17 00:00:00 2001 From: wesgreen Date: Fri, 4 Sep 2026 16:21:07 -0400 Subject: [PATCH 14/16] Reimplementing lost Gateway UI changes, readding connect command with --editor flag --- .../src/gateways/editable-command.tsx | 79 ++++++++++++++----- .../gateway-connection-steps.module.css | 20 +++++ .../src/gateways/gateway-connection-steps.tsx | 20 ++++- .../src/gateways/gateway-connections.test.ts | 14 +++- .../src/gateways/gateway-connections.ts | 10 ++- .../gateway-management-ui/src/messages.ts | 3 +- 6 files changed, 117 insertions(+), 29 deletions(-) diff --git a/packages/gateway-management-ui/src/gateways/editable-command.tsx b/packages/gateway-management-ui/src/gateways/editable-command.tsx index a8d44a15..bf1c96a7 100644 --- a/packages/gateway-management-ui/src/gateways/editable-command.tsx +++ b/packages/gateway-management-ui/src/gateways/editable-command.tsx @@ -105,22 +105,44 @@ function EditableField({ ); } -/** - * A copyable command block whose marked value slots are editable in place. - * - * `templateCommand` carries the edit markers and is highlighted once; `copyText` - * is the same command with the operator's current values resolved and drives - * both the copy button and (identically) a whole-block text selection. Editing a - * field calls `onFieldChange(marker, value)`; a marker used twice in the command - * (the mirrored provider name) is kept in lockstep because both slots read the - * same entry in `values`. - */ +function SelectField({ + colorClassName, + label, + onChange, + options, + value, +}: { + colorClassName: string; + label: string; + onChange: (value: string) => void; + options: readonly string[]; + value: string; +}) { + return ( + + ); +} + export function EditableCommand({ copyAriaLabel, copyText, labels, markers, onFieldChange, + selectOptions, templateCommand, values, }: { @@ -129,6 +151,7 @@ export function EditableCommand({ labels: Record; markers: readonly string[]; onFieldChange: (marker: string, value: string) => void; + selectOptions?: Record; templateCommand: string; values: Record; }) { @@ -183,12 +206,32 @@ export function EditableCommand({
             
-              {parts.map((part, index) =>
-                part.kind === "text" ? (
-                  
-                    {part.value}
-                  
-                ) : (
+              {parts.map((part, index) => {
+                if (part.kind === "text") {
+                  return (
+                    
+                      {part.value}
+                    
+                  );
+                }
+
+                const options = selectOptions?.[part.marker];
+                if (options) {
+                  return (
+                     {
+                        onFieldChange(part.marker, value);
+                      }}
+                      options={options}
+                      value={values[part.marker] ?? ""}
+                    />
+                  );
+                }
+
+                return (
                   
-                ),
-              )}
+                );
+              })}
             
           
diff --git a/packages/gateway-management-ui/src/gateways/gateway-connection-steps.module.css b/packages/gateway-management-ui/src/gateways/gateway-connection-steps.module.css index 46675f7e..24bc5352 100644 --- a/packages/gateway-management-ui/src/gateways/gateway-connection-steps.module.css +++ b/packages/gateway-management-ui/src/gateways/gateway-connection-steps.module.css @@ -153,6 +153,26 @@ outline-offset: 2px; } +.selectField { + appearance: auto; + background: transparent; + border: none; + border-radius: var(--pf-t--global--border--radius--small); + cursor: pointer; + font: inherit; + padding: 0; +} + +.selectField:hover { + background-color: var(--pf-t--global--background--color--secondary--default); +} + +.selectField:focus-visible { + background-color: var(--pf-t--global--background--color--secondary--default); + outline: 2px solid currentColor; + outline-offset: 2px; +} + .prereqAlert { margin-block: var(--pf-t--global--spacer--sm); } diff --git a/packages/gateway-management-ui/src/gateways/gateway-connection-steps.tsx b/packages/gateway-management-ui/src/gateways/gateway-connection-steps.tsx index d70b53b3..fd6234e3 100644 --- a/packages/gateway-management-ui/src/gateways/gateway-connection-steps.tsx +++ b/packages/gateway-management-ui/src/gateways/gateway-connection-steps.tsx @@ -16,10 +16,12 @@ import { buildSandboxCreateCommand, buildSetupScript, claudeModel, + defaultEditor, type GatewayConnection, installDocsUrl, sandboxConnectDocsUrl, sandboxName as defaultSandboxName, + validEditors, vertexProviderName, } from "./gateway-connections"; import styles from "./gateway-connection-steps.module.css"; @@ -32,9 +34,10 @@ import styles from "./gateway-connection-steps.module.css"; const providerMarker = "OSPROVIDERNAMEZ"; const modelMarker = "OSMODELNAMEZ"; const sandboxMarker = "OSSANDBOXNAMEZ"; +const editorMarker = "OSEDITORNAMEZ"; const setupMarkers = [providerMarker, modelMarker]; const sandboxMarkers = [sandboxMarker, modelMarker]; -const sandboxConnectMarkers = [sandboxMarker]; +const sandboxConnectMarkers = [sandboxMarker, editorMarker]; function ConnectionStep({ children, @@ -65,6 +68,8 @@ export function GatewayConnectionSteps({ const [providerName, setProviderName] = useState(vertexProviderName); const [model, setModel] = useState(claudeModel); const [sandboxName, setSandboxName] = useState(defaultSandboxName); + const [editor, setEditor] = useState(defaultEditor); + // Marker form drives the (stable) highlight; the resolved form drives copy and // matches a whole-block text selection exactly. const setupTemplate = buildSetupScript(gateway, { @@ -198,8 +203,9 @@ export function GatewayConnectionSteps({ { if (marker === sandboxMarker) { setSandboxName(value); + } else if (marker === editorMarker) { + setEditor(value); } }} - templateCommand={buildSandboxConnectCommand(sandboxMarker)} - values={{ [sandboxMarker]: sandboxName }} + selectOptions={{ [editorMarker]: validEditors }} + templateCommand={buildSandboxConnectCommand( + sandboxMarker, + editorMarker, + )} + values={{ [editorMarker]: editor, [sandboxMarker]: sandboxName }} /> diff --git a/packages/gateway-management-ui/src/gateways/gateway-connections.test.ts b/packages/gateway-management-ui/src/gateways/gateway-connections.test.ts index 2b0a1ae5..fd8f1adf 100644 --- a/packages/gateway-management-ui/src/gateways/gateway-connections.test.ts +++ b/packages/gateway-management-ui/src/gateways/gateway-connections.test.ts @@ -197,21 +197,27 @@ describe("gateway connections", () => { ); }); - it("builds a sandbox connect command with the default name", () => { + it("builds a sandbox connect command with defaults", () => { expect(buildSandboxConnectCommand()).toBe( - `openshell sandbox connect ${sandboxName}`, + `openshell sandbox connect ${sandboxName} \\\n --editor cursor`, ); }); it("substitutes a custom name into the sandbox connect command", () => { expect(buildSandboxConnectCommand("demo")).toBe( - "openshell sandbox connect demo", + "openshell sandbox connect demo \\\n --editor cursor", + ); + }); + + it("substitutes a custom editor into the sandbox connect command", () => { + expect(buildSandboxConnectCommand("demo", "vscode")).toBe( + "openshell sandbox connect demo \\\n --editor vscode", ); }); it("quotes shell-unsafe names in the sandbox connect command", () => { expect(buildSandboxConnectCommand("my $(sandbox)")).toBe( - `openshell sandbox connect 'my $(sandbox)'`, + `openshell sandbox connect 'my $(sandbox)' \\\n --editor cursor`, ); }); diff --git a/packages/gateway-management-ui/src/gateways/gateway-connections.ts b/packages/gateway-management-ui/src/gateways/gateway-connections.ts index bc83a551..c2855571 100644 --- a/packages/gateway-management-ui/src/gateways/gateway-connections.ts +++ b/packages/gateway-management-ui/src/gateways/gateway-connections.ts @@ -168,8 +168,14 @@ export function buildSandboxCreateCommand( export const validEditors = ["cursor", "vscode"] as const; export const defaultEditor = "cursor"; -export function buildSandboxConnectCommand(name: string = sandboxName): string { - return `openshell sandbox connect ${shellArgument(name)}`; +export function buildSandboxConnectCommand( + name: string = sandboxName, + editor: string = defaultEditor, +): string { + return [ + `openshell sandbox connect ${shellArgument(name)}`, + `--editor ${shellArgument(editor)}`, + ].join(" \\\n "); } /** diff --git a/packages/gateway-management-ui/src/messages.ts b/packages/gateway-management-ui/src/messages.ts index bf890c52..f6a4a9fc 100644 --- a/packages/gateway-management-ui/src/messages.ts +++ b/packages/gateway-management-ui/src/messages.ts @@ -215,7 +215,8 @@ export const messages = defineMessages({ copySandboxConnectCommand: { id: "app.gateway.connection.copySandboxConnectCommand", defaultMessage: "Copy the connect-sandbox command", - description: "Accessible label for copying the connect-sandbox command.", + description: + "Accessible label for the button that copies the sandbox connect command.", }, copySetupCommand: { id: "app.gateway.connection.copySetupCommand", From 7190a9e3cf688e732ef8ac48ea0bcf9e22cd5e0c Mon Sep 17 00:00:00 2001 From: wesgreen Date: Fri, 4 Sep 2026 16:23:20 -0400 Subject: [PATCH 15/16] Modifying the spec to match connect command changes --- specs/web-console/architecture.spec.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/web-console/architecture.spec.md b/specs/web-console/architecture.spec.md index 67a08baa..2bfb6dfa 100644 --- a/specs/web-console/architecture.spec.md +++ b/specs/web-console/architecture.spec.md @@ -502,7 +502,7 @@ The `Connection` tab SHALL guide the user through four ordered steps using an ac 3. **Create a sandbox.** The step SHALL present an `openshell sandbox create` command that names a sandbox, specifies CPU and memory resource requests and limits via a `--driver-config-json` flag, attaches the provider from step 2, and launches the agent, as a read-only Clipboard Copy value. The default resource values SHALL be CPU requests `100m`, CPU limits `500m`, memory requests `512Mi`, and memory limits `512Mi`. -4. **Connect to a sandbox.** The step SHALL present an `openshell sandbox connect` command that references the sandbox name from step 3 and an editor value. The sandbox name field SHALL share state with the create step so both commands stay in sync. The editor field SHALL be a constrained inline selector defaulting to `cursor`, offering only the supported values (`cursor`, `vscode`). An adjacent info alert SHALL document the supported values and link to the sandbox connect documentation on the canonical docs host. +4. **Connect to a sandbox.** The step SHALL present an `openshell sandbox connect` command that references the sandbox name from step 3 and an `--editor` flag. The sandbox name field SHALL share state with the create step so both commands stay in sync. The editor field SHALL be a constrained inline selector defaulting to `cursor`, offering only the supported values (`cursor`, `vscode`). An adjacent info alert SHALL document the supported values and link to the sandbox connect documentation on the canonical docs host. The `Connection` tab SHALL also contain a secondary `Create or manage service accounts` link with its introductory content, before the walkthrough. The link SHALL set `tab=service-accounts`. It SHALL not start a create operation. From b5c9973c4969969b27478f1e8c16fabff6acffcd Mon Sep 17 00:00:00 2001 From: wesgreen Date: Fri, 4 Sep 2026 16:39:16 -0400 Subject: [PATCH 16/16] fix(web-console): Sync en.json with extracted i18n messages Co-Authored-By: Claude Opus 4.6 (1M context) --- components/web-console/locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/web-console/locales/en.json b/components/web-console/locales/en.json index cecdcba5..02740896 100644 --- a/components/web-console/locales/en.json +++ b/components/web-console/locales/en.json @@ -125,7 +125,7 @@ }, "app.gateway.connection.copySandboxConnectCommand": { "defaultMessage": "Copy the connect-sandbox command", - "description": "Accessible label for copying the connect-sandbox command." + "description": "Accessible label for the button that copies the sandbox connect command." }, "app.gateway.connection.copySetupCommand": { "defaultMessage": "Copy the one-time setup commands",