Skip to content

feat(app): serve the whole Skill Hub through the server and retire the Electron bridge - #368

Merged
quickbeard merged 2 commits into
mainfrom
feat/skill-hub-server
Sep 14, 2026
Merged

quickbeard merged 2 commits into
mainfrom
feat/skill-hub-server

Conversation

@quickbeard

Copy link
Copy Markdown
Owner

Both follow-ups from #367 in one PR: single-skill Skill Hub moves onto the server routes (which retires the Electron IPC bridge and #363's off-host block), and the Skill Hub gets a web entry point.

What changes

  • Three new routes on the fork-only server.skillhub group: GET /api/skillhub/skill (browse with search/sort/domain/paging), GET …/skill/{idOrName} (detail with files inlined), POST …/skill/{id}/install (scope, agents, force). Same SkillHubError codes as the stack routes; an existing install is refused with already-installed unless forced, which is what drives the dialog's overwrite offer.
  • The app's Skills tab, detail page, and install dialog call them over the SDK, exactly as the Stacks tab already did. The dialog's success path disposes the instance so the model sees the new skill immediately.
  • Deleted: packages/desktop/src/main/skillhub/ (controller, IPC, test), the preload skillhub namespace, platform.skillhub, packages/app/src/skillhub/types.ts, desktop-only.tsx, the ./skillhub/types package export, the desktop's @codev/skillhub dev-dependency, and the four IPC-era i18n keys in all three locales. Nothing desktop-side remains.
  • fix(desktop): route Skill Hub through the proxy-aware stack and refuse off-host project installs #363's off-host refusal is gone rather than kept: "This project" is now valid for every project the connected server knows — local, WSL, sandbox, or remote — because the server installs where the agent runs. scope: "project" is resolved server-side from the request's directory, never from a renderer-supplied path (closing the hole the IPC parseScope had).
  • Web entry point: SkillHubNav no longer gates on a desktop capability, so the web build reaches both tabs.
  • Sign-in offer on an auth failure survives, but only when the app's own SSO writes the session the server will read — i.e. when this machine is the server (platform.gatewaySso && server.isLocal()). Elsewhere the message points at the CLI.
  • The plaintext netmind host in the detail page (flagged in the original investigation) is now base64'd like every other one in shipped source.

The one trade-off, stated plainly

#363 routed desktop hub traffic through Chromium's net.fetch so system proxy settings and PAC files applied. Hub traffic now takes the sidecar's network path instead — the same one the server's model-gateway traffic already uses — which honours the system trust store (useSystemCertificates) and HTTPS_PROXY-style env settings (setGlobalProxyFromEnv), but not a proxy configured only through OS settings or a PAC file with no env vars. The CLI, TUI, and P3 Stacks tab already had exactly this exposure; this PR extends it to single skills in exchange for remote-server and web support. The changelog entry says so in user terms. If a PAC-only network turns out to matter, the fix belongs in the sidecar (resolving Chromium's proxy into env at spawn) and would cover the model gateway too.

Verification

  • Typecheck clean: schema, protocol, server, app, desktop, tui, opencode.
  • Exerciser --include skillhub: 7/7 routes pass in effect, coverage, and auth modes (--fail-on-missing --fail-on-skip); the fake hub now also serves a skill listing, detail, and download ZIP through the package's injectable transport.
  • packages/client 16 pass; packages/app test:unit 823 pass / 0 fail. Both clients regenerated.
  • DIVERGENCES.md: rows 224, 228, 230 rewritten (bridge gone), routes/generated/exerciser rows extended. CHANGELOG.md: fix(desktop): route Skill Hub through the proxy-aware stack and refuse off-host project installs #363's two Unreleased bullets replaced by one describing the new state.
  • Not done: a visual pass in the running desktop/web app.

🤖 Generated with Claude Code

https://claude.ai/code/session_01P1MfYkJD3tQZRgqBKB4gig

quickbeard and others added 2 commits September 14, 2026 13:58
…e Electron bridge

Single-skill browse, detail, and install join the Skill Stacks on the
server's fork-only v2.skillhub group (skillhub.skill.list / get /
install), and the app's Skill Hub pages call them over the SDK like the
Stacks tab already did. The Electron IPC bridge — the main-process
controller, its proxy warm-up, the preload namespace, platform.skillhub,
and the app-owned wire types — is deleted; nothing desktop-side remains.

That removes #363's off-host refusal rather than keeping it: the server
downloads the skill and installs it where the agent runs, so "This
project" is valid for every project the connected server knows — local,
WSL, sandbox, or remote — and "project" is resolved server-side from the
request's directory, never from a path the renderer sends. It also gives
the web app the Skill Hub: the sidebar entry is no longer gated on a
desktop capability. A newly installed skill is made visible to the model
by disposing the instance, as the stack install already does.

The sign-in offer on an auth failure stays, but only when the app's own
SSO writes the session the server will read — i.e. when this machine is
the server. Hub traffic now takes the sidecar's network path, which
honours the system trust store and HTTPS_PROXY-style settings but not a
proxy configured only through OS settings or a PAC file; the changelog
says so.

The plaintext netmind host in the detail page is base64'd like every
other one in shipped source.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P1MfYkJD3tQZRgqBKB4gig
…ole page

The live hub sends unset columns as null (`metadata.publisher`,
`author.avatar`, …). The old desktop bridge never validated shapes, but
the server's response schema types those fields as optional strings, so
a single null card made the Skills tab 500 into "Something went wrong".

Nulls now become absent fields at the package's coercion seam, once, so
the CLI, the TUI, and the server's strict schema all see one total shape
(avatar keeps its null: the hub means "no picture" by it). The exerciser
fixture carries the same nulls so CI covers the class, and the browse
pages print the server's own message under the generic line instead of
hiding it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P1MfYkJD3tQZRgqBKB4gig
@quickbeard
quickbeard merged commit 072ec6f into main Sep 14, 2026
0 of 5 checks passed
@quickbeard
quickbeard deleted the feat/skill-hub-server branch September 14, 2026 07:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant