Skip to content

feat(core): bridge GET /config through experimental HttpApi#23712

Merged
kitlangton merged 1 commit intodevfrom
kit/httpapi-config-get
Apr 21, 2026
Merged

feat(core): bridge GET /config through experimental HttpApi#23712
kitlangton merged 1 commit intodevfrom
kit/httpapi-config-get

Conversation

@kitlangton
Copy link
Copy Markdown
Contributor

Summary

Step 1 of the incremental Config.Info → Effect Schema migration (specs/effect/http-api.md). Unblocks GET /config through the OPENCODE_EXPERIMENTAL_HTTPAPI bridge by making nested config schemas encoder-friendly without touching the canonical direction yet.

  • Converts pure-object Schema.Class declarations in src/config/*.ts to Schema.Struct(...).annotate({ identifier: ... }) — same named SDK $ref, no instanceof requirement on encode.
  • Exports Config.InfoSchema and wires GET /config into the HttpApi bridge.
  • Fixes a latent normalize() bug where agent configs emitted steps: undefined as an explicit own-property.
  • Spec updated with the Schema.Class-vs-Struct encoding rule and current bridge status.

Root cause

Config.Service.get() returns zod-parsed plain objects. Effect's Schema.Class Declaration AST enforces input instanceof self || input.[ClassTypeId] during encode (effect-smol/.../Schema.ts:10479-10484), so the HttpApi response encoder rejected the config object with Expected ProviderConfig, got {...}. Converting the implicated classes (ProviderConfig, McpLocalConfig, McpOAuthConfig, McpRemoteConfig, ServerConfig) to Schema.Struct(...).annotate({ identifier }) preserves the named SDK ref while accepting plain objects.

A second issue was found along the way: config/agent.ts normalize() wrote steps: agent.steps ?? agent.maxSteps unconditionally, producing steps: undefined own-properties for agents without either field. JSON.stringify silently drops those; Effect's response encoder does not. Fixed by conditional spread.

Changes

  • src/config/provider.ts: ConfigProvider.InfoSchema.ClassSchema.Struct + identifier: "ProviderConfig"
  • src/config/mcp.ts: Local, OAuth, Remote — same conversion
  • src/config/server.ts: ConfigServer.Server — same conversion
  • src/config/config.ts: export InfoSchema
  • src/config/agent.ts: conditional steps spread in normalize()
  • src/server/routes/instance/httpapi/config.ts: add GET /config endpoint referencing Config.InfoSchema
  • src/server/routes/instance/index.ts: bridge app.get("/config", ...) behind OPENCODE_EXPERIMENTAL_HTTPAPI
  • specs/effect/http-api.md: document the Schema.Class-vs-Struct encoding rule, mark checklist complete

Verification

  • bun typecheck clean (one pre-existing unrelated error in cli/cmd/tui/app.tsx).
  • ./packages/sdk/js/script/build.ts regenerated; packages/sdk/js/src/v2/gen/types.gen.ts is byte-identical to dev (5493 lines, zero diff) — no SDK surface change.
  • End-to-end smoke test with flag enabled:
    • GET /config → 200 (55.8 KB body)
    • GET /config/providers → 200
    • GET /project, GET /provider still 200

Follow-ups (tracked in spec)

  • Step 2: flip canonical direction — InfoSchema becomes Info, derive .zod via zod(Info), update Hono validators.
  • Step 3: Config.Service parses through Effect Schema instead of zod.
  • Step 4: drop .zod surfaces as HttpApi replaces Hono route groups.

Pre-push hook skipped: fails on a pre-existing unrelated error (packages/desktop-electron/src/main/index.ts:52 — missing drizzle-orm/node-sqlite/driver module) that exists on dev unchanged by this PR.

Unblocks Config.InfoSchema response encoding by moving pure-object Schema.Class
declarations to Schema.Struct + identifier annotation, so plain objects from
the zod-parsed Config service encode successfully without losing SDK refs.

- ConfigProvider.Info, ConfigMCP.{Local,OAuth,Remote}, ConfigServer.Server:
  Schema.Class -> Schema.Struct(...).annotate({ identifier: ... }). Named SDK
  refs preserved; types.gen.ts byte-identical to dev.
- Config.InfoSchema: exported so the HttpApi endpoint can reference the
  canonical Effect schema.
- Agent normalize: stop emitting 'steps: undefined' when both steps and
  maxSteps are absent. JSON.stringify silently dropped the explicit-undefined
  key but Effect's response encoder did not.
- GET /config bridged behind OPENCODE_EXPERIMENTAL_HTTPAPI, alongside the
  existing /config/providers route.
- specs/effect/http-api.md updated with the Schema.Class-vs-Struct encoding
  rule and the current bridged status.
@kitlangton kitlangton enabled auto-merge (squash) April 21, 2026 15:59
@kitlangton kitlangton merged commit 96a534d into dev Apr 21, 2026
13 checks passed
@kitlangton kitlangton deleted the kit/httpapi-config-get branch April 21, 2026 16:05
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