Skip to content

feat(prokube): add Kubeflow-ready Docker image with prefix-aware UI#2

Draft
hsteude wants to merge 144 commits intodevfrom
feature/prefix-aware-ui
Draft

feat(prokube): add Kubeflow-ready Docker image with prefix-aware UI#2
hsteude wants to merge 144 commits intodevfrom
feature/prefix-aware-ui

Conversation

@hsteude
Copy link

@hsteude hsteude commented Jan 22, 2026

Summary

  • Add Docker image setup for Kubeflow Notebook deployment
  • Improve UI with OpenCode logo, clickable model selector, and cleaner sidebar
  • Remove upstream GitHub workflows (keep only our prefixable image build)

Docker Image

  • Multi-stage Dockerfile with Bun build and Alpine runtime
  • Bun-based UI server with API proxy
  • s6-overlay for process management
  • Supports NB_PREFIX for Kubeflow notebook URL prefixes

UI Improvements

  • OpenCode wordmark logo on home/session screens
  • PK logo in sidebar
  • Clickable model list to set default model
  • Settings button on home page

Testing

cd prokube/docker
make build
make run              # ohne prefix
make run-with-prefix  # mit NB_PREFIX=/notebook/user/opencode/

@github-actions
Copy link

Thanks for your contribution!

This PR doesn't have a linked issue. All PRs must reference an existing issue.

Please:

  1. Open an issue describing the bug/feature (if one doesn't exist)
  2. Add Fixes #<number> or Closes #<number> to this PR description

See CONTRIBUTING.md for details.

@github-actions
Copy link

github-actions bot commented Jan 22, 2026

Docker Image Published

Version: v0.0.0-95db3b120

Image Tags

Tag URL
latest europe-west3-docker.pkg.dev/prokube-internal/prokube-customer/pk-opencode-prefixable:latest
version europe-west3-docker.pkg.dev/prokube-internal/prokube-customer/pk-opencode-prefixable:v0.0.0-95db3b120
commit europe-west3-docker.pkg.dev/prokube-internal/prokube-customer/pk-opencode-prefixable:commit-95db3b1207c781c784b2eef9c5450ab2913675ef
Full image URLs for copy/paste
europe-west3-docker.pkg.dev/prokube-internal/prokube-customer/pk-opencode-prefixable:latest
europe-west3-docker.pkg.dev/prokube-internal/prokube-customer/pk-opencode-prefixable:v0.0.0-95db3b120
europe-west3-docker.pkg.dev/prokube-internal/prokube-customer/pk-opencode-prefixable:commit-95db3b1207c781c784b2eef9c5450ab2913675ef

…n support

- esbuild + SolidJS build system with PostCSS/Tailwind
- Runtime prefix detection via window.__OPENCODE__.basePath
- Dev server with API proxy for backend at 127.0.0.1:4096
- Improved SSE streaming through proxy with TransformStream
- Session page with message sending and polling fallback
- Markdown rendering for assistant messages
- Full component library (layout, sidebar, session list)
- New ProviderProvider context for state management
- Settings page for connecting providers via API key
- Model picker dropdown in session header
- Agent picker dropdown in session header
- Provider status indicator in sidebar
- Fix SSE proxy idleTimeout issue
- Add CSS design tokens for colors, backgrounds, borders
- Redesign sidebar with cleaner, more elegant styling
- Redesign settings page with tabs for providers/models/agents
- Update session page to use CSS variables throughout
- Update home page with consistent theming
- Add /agent to dev proxy paths
- Add null checks in SSE event handling
Shows the current project in the sidebar header with a dropdown listing
all projects. Includes a hint that switching projects requires running
opencode in the target folder since the backend is project-specific.
- Add directory-layout component that extracts directory from URL
- Add project-picker page for selecting from available projects
- Update SDK context to pass x-opencode-directory header
- Group sessions by project in sidebar with unique display names
- Add per-project session creation with always-visible + button
- Update routing to use /{base64Dir}/session/{id} pattern
- Add MCPProvider context for tracking server status
- Add MCPDialog for connect/disconnect of servers
- Add MCPAddDialog for adding new local/remote servers
- Add MCP indicator in session header showing connection status
- Add /mcp slash command for quick access
- Add environment variables support for local MCP servers
- Add custom HTTP headers for remote MCP servers
- Add OAuth configuration (client ID, secret, scope, toggle)
- Add timeout setting for all server types
- Add MCP Servers tab to Settings page with full server management
- Export MCP config types from context for component use
- Multi-stage Dockerfile with Bun build and Alpine runtime
- Bun-based UI server with API proxy (serve-ui.ts)
- s6-overlay for process management
- GitHub Actions workflow for GCP Artifact Registry
- Supports NB_PREFIX for Kubeflow notebook URL prefixes
- Add OpenCode wordmark logo to home and session welcome screens
- Replace project picker with PK logo in sidebar
- Make model list clickable to select default model
- Add settings button to home page
- Add favicon and SVG assets
- Add command context for future keyboard shortcuts
Keep only opencode-prefixable.yml for our Kubeflow image builds.
Upstream workflows are not relevant for this fork.
Align with prokube-images workflow - always push to GCP Artifact Registry.
Also adds PR comment with image tags.
In Kubeflow context, automatically redirect to the default home
directory instead of showing 'No projects found' error.
- Strip base path prefix before checking API routes
- Remove serverUrl injection (use window.location.origin instead)
- Use prefixed path for EventSource SSE connection
- Fix requests going directly to internal 127.0.0.1:4096
- Add error state and early return in sendMessage() when no model is selected
- Prevents OpenCode from auto-selecting a broken provider (e.g., bedrock from MinIO AWS env vars)
- Display API errors in message bubbles so users see why requests failed
- Add hint text when providers are connected but no model is selected
Default to 'build' agent if none selected. OpenCode throws 'undefined is not an object (evaluating agent.name)' when no agent is provided.
- PK icon in header now links to homescreen (/)
- Homescreen shows folder/project picker instead of auto-redirecting
- Open Folder: search for existing folders using find API
- Create Folder: navigate to new folder path in /home/jovyan/
- Recent Projects: list of previously used projects
- Quick access link to home directory
- PK icon now navigates to basePath instead of hardcoded /
- Current directory always appears in sidebar, even without sessions
- Empty projects show 'No sessions yet' under the project name
- Remove Recent Projects from homescreen (was showing all historical projects)
- Sidebar now shows only current project's sessions, not all projects
- Add project name to sidebar header next to PK icon
- PK icon links to homescreen to switch projects
- Fix dev server API proxy to correctly strip base path prefix
Henrik and others added 7 commits January 31, 2026 16:02
- Store models per agent in modelsByAgent record instead of single selectedModel
- Persist models to localStorage for each agent separately
- Auto-load saved models on mount
- Return correct model for currently selected agent via selectedModel getter
- Matches behavior of original TUI where each agent remembers its model
- Add detailed logging for MCP server addition
- Check server status after adding and throw specific errors
- Show OAuth and connection errors to user
- Clear previous errors when attempting to add server
When adding an MCP server via MCP.add(), the server was only added to
memory state but not persisted to the config file. This caused the
MCP.status() function to return undefined for the newly added server
since it only reads from the config file.

This fix adds Config.update() call to persist the MCP server
configuration to the config file after successfully creating the
client, ensuring the server appears in status checks and persists
across restarts.

Fixes issue where prokube UI would not show newly added MCP servers.
- Add environment variables table
- Add fork & upstream sync strategy section
- Remove redundant app-prefixable/AGENTS.md
- Remove outdated app-prefixable/AGENT_PROMPT.md

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…kube

This moves the mkdir and directory listing features from packages/
(upstream code) to prokube/, avoiding future rebase conflicts.

Changes:
- Add /api/prokube/mkdir and /api/prokube/list-dirs endpoints to
  serve-ui.ts (production) and dev.ts (development)
- Add prokube-api.ts utility for frontend API calls
- Update project-dialog.tsx and terminal.tsx to use new endpoints
- Revert upstream changes in packages/opencode and packages/sdk

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Revert the MCP.add() config persistence change to keep upstream
code unmodified. This feature may need to be reimplemented in
prokube/ layer if still needed.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@hsteude hsteude force-pushed the feature/prefix-aware-ui branch from 8b004a2 to a127440 Compare January 31, 2026 15:11
hsteude and others added 22 commits January 31, 2026 16:47
- Add SessionSidebar component showing tasks and git branch
- Fix processing state not resetting when switching sessions
- Add /vcs to proxy paths in dev.ts and serve-ui.ts

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When adding an MCP server via the UI, the server configuration was
only added to memory but not persisted to the config file. Since
mcp.status() reads from the config file to determine which servers
to include in the response, newly added servers wouldn't show up.

This fix persists the MCP config to the global config file before
calling mcp.add, ensuring the server appears in the status list.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add clear documentation about the separation between upstream OpenCode
code (packages/) and prokube-specific code (prokube/).

Key additions:
- New README.md explaining the architecture and feature split
- AGENTS.md updated with critical boundary rules and examples
- Document prokube-specific endpoints (/api/prokube/*)
- Document frontend workarounds (MCP persistence, mkdir)
- Real example: MCP persistence bug fix approach

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Move duplicated prokube API endpoints (/api/prokube/mkdir, /api/prokube/list-dirs)
from dev.ts and serve-ui.ts into shared/prokube-endpoints.ts.

This eliminates code duplication between the dev and production servers.
Both servers now import from the shared module.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add ability to remove MCP servers from the global config via the UI.

Changes:
- Add remove() function to MCP context that:
  - Disconnects the server
  - Removes entry from global config
- Add delete button (trash icon) next to each MCP server in the dialog
- Confirm before deleting

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The serve-ui.ts imports from ../shared/prokube-endpoints but the
shared directory wasn't being copied to the Docker image.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add ability to delete MCP servers from the Settings page MCP tab,
matching the functionality already available in the MCP dialog.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
After deleting an MCP server, the backend restarts due to config change.
The UI wasn't refreshing after reconnect, so deleted servers still appeared.
Now MCP status refreshes on server.connected event.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The backend does a deep merge on config.update(), so omitting a key
doesn't delete it. We now explicitly set the server to null to signal
deletion.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The OpenCode SDK's global.config.update() does a deep merge and doesn't
support deleting keys. We now use a custom prokube endpoint that directly
modifies the config file to remove MCP servers.

- Add DELETE /api/prokube/mcp/:name endpoint in prokube-endpoints.ts
- Update MCP context to use this endpoint for removal

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…URLs

The regex to strip JSONC comments was breaking URLs like http://...
Now we try JSON.parse first, and only strip comments if that fails.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
After deleting from config file, we need to trigger a backend restart
so it reloads the updated config. We do this by calling global.config.update()
which causes server.instance.disposed and a restart.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The backend needs a moment to fully initialize after restart.
Add 500ms delay before refreshing MCP status on server.connected.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add smart auto-scroll that detects when user manually scrolls up
and pauses auto-scrolling. Auto-scroll resumes when user scrolls
back to the bottom or sends a new message.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The server.connected event wasn't reliably triggering refresh.
Now we explicitly wait 1.5s and refresh after triggering backend restart.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add Terminal button to HomeLayout sidebar (available without active session)
- Add resizable Terminal panel at bottom of home screen
- Simplify SSH key settings to read-only display with copy functionality
- Show ssh-keygen command for users to generate keys themselves
- Remove Terminal ActionCard from project-picker (now in sidebar instead)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Users with multiple SSH keys can now see all of them in the Git settings,
making it easier to copy the right key for different Git providers.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Now lists all .pub files in ~/.ssh/ directory instead of only checking
for id_ed25519, id_ecdsa, id_rsa, id_dsa. Standard keys are shown first,
followed by custom-named keys alphabetically.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Use reconcile() when updating the SolidJS store to properly handle
deleted keys. Without this, removed servers would persist in the store
until a full page refresh.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When the backend echoes back the user's message, the event handler was
creating a new "assistant" message because the messageID didn't match
the optimistic user message ID.

Now we track the pending user message text and match it against incoming
parts - if it matches, we update the optimistic message's ID instead of
creating a duplicate assistant message.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
findLastIndex is ES2023 and may not be available in all environments.
Use a simple reverse loop instead.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Show clear error message when selected model's provider is not connected
- Remove polling loop that was causing constant console logs
- Rely on SSE events for real-time updates (like the original UI does)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant