refactor: route live session commands through ApiGateway#13
Conversation
The five live subcommands (start/install/exec/stop/status) each built their own fetch() with hand-spread auth.headers and per-call handleApiError, the one architecture violation flagged in the review (CLAUDE.md: commands orchestrate services, no I/O logic). They also bypassed ApiGateway's network-error enhancement, so a DNS/connection failure surfaced as a bare "fetch failed" TypeError instead of the friendly diagnostic every other command gives. Adds startLiveSession/installLiveBinary/execLiveYaml/stopLiveSession/ getLiveSession to ApiGateway (with typed LiveSession/LiveSessionSummary/ LiveExecResult interfaces, since the swagger has no /live schema), each following the existing try/fetch/handleApiError/enhanceFetchError skeleton. live.ts keeps all its presentation logic and just calls the gateway. Net -66 lines in the command. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| /** Summary returned when a live session is created. */ | ||
| export interface LiveSessionSummary { | ||
| id: number; | ||
| platform: string; | ||
| session_name: string; | ||
| status: string; | ||
| } | ||
|
|
||
| /** Full live session record returned by the status endpoint. */ | ||
| export interface LiveSession extends LiveSessionSummary { | ||
| binary_upload_id: null | string; | ||
| created_at: string; | ||
| } | ||
|
|
||
| /** Result of executing Maestro YAML against a live session. */ | ||
| export interface LiveExecResult { | ||
| error?: string; | ||
| output?: string; | ||
| success: boolean; | ||
| } |
There was a problem hiding this comment.
The three JSDoc comments describe only what each type is — information already fully conveyed by the interface names. Per CLAUDE.md: "Default to writing no comments. Only add one when the WHY is non-obvious… Don't explain WHAT the code does, since well-named identifiers already do that."
The one genuinely non-obvious WHY — that these are hand-defined because the swagger schema has no /live routes and openapi-typescript can't generate them — is currently absent from the code. That belongs as a single comment.
| /** Summary returned when a live session is created. */ | |
| export interface LiveSessionSummary { | |
| id: number; | |
| platform: string; | |
| session_name: string; | |
| status: string; | |
| } | |
| /** Full live session record returned by the status endpoint. */ | |
| export interface LiveSession extends LiveSessionSummary { | |
| binary_upload_id: null | string; | |
| created_at: string; | |
| } | |
| /** Result of executing Maestro YAML against a live session. */ | |
| export interface LiveExecResult { | |
| error?: string; | |
| output?: string; | |
| success: boolean; | |
| } | |
| // Hand-defined: swagger has no /live routes so openapi-typescript cannot generate these. | |
| export interface LiveSessionSummary { | |
| id: number; | |
| platform: string; | |
| session_name: string; | |
| status: string; | |
| } | |
| export interface LiveSession extends LiveSessionSummary { | |
| binary_upload_id: null | string; | |
| created_at: string; | |
| } | |
| export interface LiveExecResult { | |
| error?: string; | |
| output?: string; | |
| success: boolean; | |
| } |
There was a problem hiding this comment.
Partially applied. The non-obvious WHY (swagger has no /live routes for openapi-typescript to generate) is now a header comment on the new domain file. I kept the brief per-interface comments: they note which endpoint returns each type, which clarifies the otherwise-confusing Summary-vs-full distinction, and that's consistent with the contextual doc comments in the existing auth.types.ts. Note: the quoted "Default to writing no comments…" text isn't actually in this repo's CLAUDE.md — the verifiable rule (domain types location) drove the change.
| } | ||
|
|
||
| /** Summary returned when a live session is created. */ | ||
| export interface LiveSessionSummary { |
There was a problem hiding this comment.
LiveSessionSummary, LiveSession, and LiveExecResult are exported from this file and consumed in src/commands/live.ts, making them cross-layer types. Per CLAUDE.md: "Domain types live in src/types/domain/."
The existing src/types/domain/auth.types.ts and device.types.ts follow this pattern for types that cross layer boundaries. Consider moving these three interfaces to a new src/types/domain/live.types.ts and importing them back into api-gateway.ts.
There was a problem hiding this comment.
Done in f0082fa — moved all three interfaces to a new src/types/domain/live.types.ts and imported them back into api-gateway.ts, matching the auth.types.ts/device.types.ts pattern. Verified the CLAUDE.md:31 citation, good catch.
Addresses code review on PR #13: cross-layer domain types belong in src/types/domain/ per CLAUDE.md (alongside auth.types.ts and device.types.ts), and the non-obvious reason these are hand-defined (swagger has no /live routes for openapi-typescript to generate from) is now captured in a comment. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Code reviewNo issues found. Checked for bugs and CLAUDE.md compliance. |
Summary
Final item from the #3 code review: the five
livesubcommands (start/install/exec/stop/status) each built their own rawfetch()with hand-spreadauth.headersand a per-callhandleApiError— the one architecture violation flagged in the review (CLAUDE.md mandates commands orchestrate services with no I/O logic; HTTP belongs inApiGateway).More than a tidiness issue: because they bypassed the gateway, they also bypassed its network-error enhancement. A DNS/connection failure surfaced as a bare
fetch failedTypeError instead of the friendly "Network request failed... Possible causes" diagnostic every other command gives.This adds five methods to
ApiGateway—startLiveSession,installLiveBinary,execLiveYaml,stopLiveSession,getLiveSession— each following the establishedtry / fetch / handleApiError / catch → enhanceFetchErrorskeleton, plus typedLiveSession/LiveSessionSummary/LiveExecResultinterfaces (the swagger has no/liveschema, so these are hand-defined).live.tskeeps all its presentation logic and just calls the gateway: −66 lines in the command.Verification
Smoke-tested against the mock API:
live status→ unreachable URL): now the enhanced "Network request failed" message — previously a barefetch failed← the actual fixlive stop→ invalid key): auth-mode-neutral message with operation contexthandleApiErrorunchangedpnpm test122/122 ·pnpm typecheckclean (strict) ·pnpm lint0 errors ·pnpm buildclean🤖 Generated with Claude Code