Skip to content

feat(extension): app shelf + engine proxy on the amicode service (fork-cutover static and proxy slice, #822) - #824

Open
aarontrowbridge wants to merge 7 commits into
mainfrom
822-app-shelf-extension-service-static-proxy
Open

feat(extension): app shelf + engine proxy on the amicode service (fork-cutover static and proxy slice, #822)#824
aarontrowbridge wants to merge 7 commits into
mainfrom
822-app-shelf-extension-service-static-proxy

Conversation

@aarontrowbridge

@aarontrowbridge aarontrowbridge commented Sep 5, 2026

Copy link
Copy Markdown
Member

Implements #822 — the fork-cutover static + proxy slice: the extension-host amicode service gains an app shelf (static serving of the built app-bundle dist) and a reverse proxy to the spawned engine, with precedence exact /amicode/ routes → static hit → engine proxy*, and auth accepting both the service's per-boot mint and the engine token.

Acceptance Criteria

  • GET / on the service origin serves the app dist's index document; asset requests get correct content types; unmatched GETs fall back to the SPA index; path traversal is refused — test/amicode_service_app_shelf.test.ts (6 tests, incl. raw-socket traversal carriers refused 403, API-style GETs never handed the SPA doc)
  • Non-amicode, non-static requests proxy to the engine upstream with method, headers, and body preserved; SSE (/event) streams unbuffered through the proxy — test/amicode_service_engine_proxy.test.ts (mock engine records method/headers/body verbatim; SSE chunks arrive ≥180ms apart — progressive, not buffered) + the live probe's real-engine /session + /event
  • The route precedence is asserted: an exact /amicode/* route never shadows behind the proxy, and a static asset hit never reaches the proxy — test/amicode_service_engine_proxy.test.ts "precedence: …" (the mock engine must never see an /amicode/* or /assets hit)
  • Auth: a request bearing the engine token is accepted on /amicode/* routes (alongside the service's own mint); proxied engine requests carry the engine token unchanged — accept-both test incl. the unknown-mint-still-401 case; the passthrough test asserts the client's Authorization rides upstream unchanged
  • With no dist present and no override set, the shelf serves an honest needs-setup placeholder (never a silent 404-as-app) — needs-setup test (placeholder names the build + override fixes; /amicode/* unaffected) and the live probe asserts a live dist never serves the marker
  • The boot proof (env-gated, CI-skipped) boots the real service against the real spawned engine + built dist and asserts: app document from the service origin, an engine API call through the proxy, an SSE connect through the proxy, and one /amicode/* route — end-to-end from the service origin — scripts/amicode_service_boot_probe.mjs esbuild-bundles the REAL service source (no transcribed logic), spawns the vendored engine (password-armed), serves the REAL built dist. Ran it for real: PASS on all four (real build:app output: 4,713-pkg bun install, 13.8s vite build; vendored 1.18.10 engine). CI lane added; it self-skips with the reason printed until the dist-build chore (chore(ci): wire the app-bundle dist build into CI + the release pipeline (the #822 packaging stub's follow-up) #825) lands.

Slices

  • app_shelf.ts — static serving: origin doc, content types, SPA fallback (GET + accepts HTML only), traversal refusal, needs-setup placeholder
  • engine_proxy.ts — transparent reverse proxy (method/headers/body passthrough, RFC 7230 hop-by-hop strip, SSE unbuffered), honest 503 when the upstream is not bound; never-throwing
  • auth accepts the engine token (server.ts authorized() checks both mints, constant-time per mint; Basic is the only carrier the service itself validates — ?auth_token= is the ENGINE's carrier, which the proxy passes through unchanged)
  • wiring: startAmicodeService learns the engine upstream (LATE-BOUND URL getter + engine password — the engine restarts on solver-mode switches while the service deliberately does not) + the app dist root; extension.ts boot call-site; the dev-override setting amicode.appBundleDir
  • packaging: build:app script (materialize → bun install → vite build → stage to dist/app — verified for real end-to-end); rides the VSIX via the existing dist/ packaging (the fetch:opencode precedent). The CI-time bun build is NOT wired in this slice — the ci.yml boot-proof lane is the honest self-skipping stub, follow-up chore chore(ci): wire the app-bundle dist build into CI + the release pipeline (the #822 packaging stub's follow-up) #825
  • env-gated live boot proof script (scripts/amicode_service_boot_probe.mjs, the telaio_app_probe precedent) + its real-service entry src/amicode_service_boot_probe.ts

Honest notes

…the amicode service (#822)

The #822 static slice: an AppShelf module (vscode-free, the service's
founding discipline) serves the built app-bundle dist from the service
origin — origin document, asset content types, SPA fallback for GETs
that accept HTML (API-style GETs fall through honestly), path-traversal
refusal, and the honest needs-setup placeholder when no dist is present.
Precedence: exact route table → /amicode/* namespace 404 (fork parity,
unmatched /amicode/* never proxies or SPA-falls) → shelf → honest 503
until the engine-proxy slice lands next.
@coderabbitai

coderabbitai Bot commented Sep 5, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Team

Run ID: 386cfdd3-137a-4730-bfb9-8eb2150064de


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

…engine-token auth + wiring + packaging remain)

The dead cast's tdd-RED state, preserved verbatim: engine_proxy.ts (transparent
passthrough, late-bound upstream getter, RFC hop-by-hop handling), the proxy
contract tests (3 RED: passthrough, POST body, SSE unbuffered — they 401 at
authorized() because accept-engine-token is not implemented yet), and the
attachEngineProxy precedence wiring in server/index.

Remaining for #822: accept-both auth (service mint AND engine token, incl.
/amicode/* routes), the wiring layer teaching the service the engine upstream,
packaging the dist, the env-gated boot proof, honest comment updates.
…int on every route (#822)

The framed app bootstraps with the ENGINE credential, so the service's
auth now accepts BOTH its own per-boot mint and the engine's: on
/amicode/* routes AND proxied paths. The proxy forwards Authorization
unchanged (transparent by design). Also covers the precedence contract:
an exact /amicode/* route never reaches the proxy; a static shelf hit
never reaches the proxy.
)

startAmicodeService() now takes the #822 boot options: the engine context
(per-boot mint + LATE-BOUND URL getter — the engine restarts on
solver-mode switches while the service deliberately does not, so the
proxy resolves the current upstream per request; a restart gap answers
the honest 503) and the app dist root (override setting > packaged
dist/app). The superseded total-separation comment is updated honestly:
the engine token is accepted ALONGSIDE the service's own mint, not
shared. extension.ts passes serverManager's URL late + the
amicode.appBundleDir override; the service modules stay vscode-free.
build:app runs the app-bundle README's proven recipe — materialize →
bun install → vite build — and stages the dist into packages/extension/
dist/app (the fetch:opencode precedent for a build product riding the
VSIX; resolveAppDistRoot's default already expects it). Verified for real
end-to-end: 4,713-package bun install, 13.8s vite build, 18 top-level
entries staged.

The boot proof (amicode_service_boot_probe.mjs, the telaio_app_probe
convention) esbuild-bundles the REAL service from source — no
transcribed logic — boots it against a real spawned vendored engine +
the real built dist, and asserts all four surfaces with the ENGINE
credential: app document from the origin, engine API through the
proxy, SSE through the proxy, one /amicode/* route. Env-gated: absent
dist → honest SKIP, CI never false-greens. Ran it for real: PASS.

The ci.yml lane is the honest stub: the full bun dist build is NOT
wired into CI in this slice (packaging chore follow-up); the lane runs
the probe, which self-skips with the reason printed.
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