1- import type { AgentDeviceClient , CommandRequestResult } from '../../client.ts' ;
1+ import {
2+ serializeCloseResult ,
3+ serializeDeployResult ,
4+ serializeDevice ,
5+ serializeInstallFromSourceResult ,
6+ serializeOpenResult ,
7+ serializeSessionListEntry ,
8+ serializeSnapshotResult ,
9+ } from '../../client-shared.ts' ;
10+ import type {
11+ AgentDeviceClient ,
12+ AgentDeviceDevice ,
13+ AgentDeviceSession ,
14+ AppCloseResult ,
15+ AppDeployResult ,
16+ AppInstallFromSourceResult ,
17+ AppOpenResult ,
18+ CaptureSnapshotResult ,
19+ CommandRequestResult ,
20+ SessionCloseResult ,
21+ } from '../../client.ts' ;
222import { announceReplayTestRun } from '../../cli-test.ts' ;
323import { runSemanticCliCommand } from '../../commands/semantic-cli.ts' ;
4- import { listSemanticGenericCliCommands } from '../../commands/semantic-command-surface.ts' ;
24+ import {
25+ listSemanticGenericCliCommands ,
26+ type SemanticCliCommand ,
27+ } from '../../commands/semantic-command-surface.ts' ;
28+ import { assertResolvedAppsFilter } from '../../commands/app-inventory-contract.ts' ;
529import type { CliFlags } from '../../utils/command-schema.ts' ;
30+ import { AppError } from '../../utils/errors.ts' ;
31+ import { formatSnapshotText } from '../../utils/output.ts' ;
632import { writeCommandCliOutput } from './output.ts' ;
33+ import { writeCommandMessage , writeCommandOutput } from './shared.ts' ;
734import type { PublicCommandName } from '../../command-catalog.ts' ;
835import type { ClientCommandHandler } from './router-types.ts' ;
936
@@ -37,6 +64,104 @@ export const genericClientCommandHandlers = Object.fromEntries(
3764 ] ) ,
3865) as { [ TCommand in keyof typeof genericClientCommandRunners ] : ClientCommandHandler } ;
3966
67+ const formattedSemanticCommandHandlers = {
68+ devices : createFormattedSemanticHandler ( 'devices' , {
69+ write : ( { flags, result } ) => {
70+ const devices = result as unknown as AgentDeviceDevice [ ] ;
71+ const data = { devices : devices . map ( serializeDevice ) } ;
72+ writeCommandOutput ( flags , data , ( ) => devices . map ( formatDeviceLine ) . join ( '\n' ) ) ;
73+ } ,
74+ } ) ,
75+ apps : createFormattedSemanticHandler ( 'apps' , {
76+ write : ( { flags, result } ) => {
77+ const appsFilter = assertResolvedAppsFilter ( flags . appsFilter ) ;
78+ const apps = result as unknown as string [ ] ;
79+ const data = { apps } ;
80+ writeCommandOutput ( flags , data , ( ) => {
81+ if ( ! flags . json ) {
82+ process . stderr . write (
83+ appsFilter === 'all'
84+ ? 'Showing all apps, including system apps.\n'
85+ : 'Showing user-installed apps. Use --all to include system apps.\n' ,
86+ ) ;
87+ }
88+ if ( apps . length > 0 ) return apps . join ( '\n' ) ;
89+ return appsFilter === 'all' ? 'No apps found.' : 'No user-installed apps found.' ;
90+ } ) ;
91+ } ,
92+ } ) ,
93+ session : createFormattedSemanticHandler ( 'session' , {
94+ beforeRun : ( { positionals } ) => {
95+ const subcommand = positionals [ 0 ] ?? 'list' ;
96+ if ( subcommand !== 'list' ) {
97+ throw new AppError ( 'INVALID_ARGS' , 'session only supports list' ) ;
98+ }
99+ } ,
100+ write : ( { flags, result } ) => {
101+ const sessions = ( result as { sessions : AgentDeviceSession [ ] } ) . sessions ;
102+ const data = { sessions : sessions . map ( serializeSessionListEntry ) } ;
103+ writeCommandOutput ( flags , data , ( ) => JSON . stringify ( data , null , 2 ) ) ;
104+ } ,
105+ } ) ,
106+ open : createFormattedSemanticHandler ( 'open' , {
107+ write : ( { flags, result } ) => {
108+ writeCommandMessage ( flags , serializeOpenResult ( result as AppOpenResult ) ) ;
109+ } ,
110+ } ) ,
111+ close : createFormattedSemanticHandler ( 'close' , {
112+ write : ( { flags, result } ) => {
113+ writeCommandMessage (
114+ flags ,
115+ serializeCloseResult ( result as AppCloseResult | SessionCloseResult ) ,
116+ ) ;
117+ } ,
118+ } ) ,
119+ install : createFormattedSemanticHandler ( 'install' , {
120+ write : ( { flags, result } ) => {
121+ writeCommandMessage ( flags , serializeDeployResult ( result as AppDeployResult ) ) ;
122+ } ,
123+ } ) ,
124+ reinstall : createFormattedSemanticHandler ( 'reinstall' , {
125+ write : ( { flags, result } ) => {
126+ writeCommandMessage ( flags , serializeDeployResult ( result as AppDeployResult ) ) ;
127+ } ,
128+ } ) ,
129+ 'install-from-source' : createFormattedSemanticHandler ( 'install-from-source' , {
130+ write : ( { flags, result } ) => {
131+ writeCommandMessage (
132+ flags ,
133+ serializeInstallFromSourceResult ( result as AppInstallFromSourceResult ) ,
134+ ) ;
135+ } ,
136+ } ) ,
137+ snapshot : createFormattedSemanticHandler ( 'snapshot' , {
138+ positionals : ( ) => [ ] ,
139+ write : ( { flags, result } ) => {
140+ const data = serializeSnapshotResult ( result as CaptureSnapshotResult ) ;
141+ // Programmatic SDK callers can see `unchanged`; CLI --json hides it for schema compatibility.
142+ const outputData = flags . json ? withoutUnchanged ( data ) : data ;
143+ writeCommandOutput ( flags , outputData , ( ) =>
144+ formatSnapshotText ( outputData , {
145+ raw : flags . snapshotRaw ,
146+ flatten : flags . snapshotInteractiveOnly ,
147+ } ) ,
148+ ) ;
149+ } ,
150+ } ) ,
151+ metro : createFormattedSemanticHandler ( 'metro' , {
152+ write : ( { positionals, flags, result } ) => {
153+ const action = ( positionals [ 0 ] ?? '' ) . toLowerCase ( ) ;
154+ writeCommandOutput ( flags , result , ( ) =>
155+ action === 'reload'
156+ ? `Reloaded React Native apps via ${ ( result as { reloadUrl ?: unknown } ) . reloadUrl } `
157+ : JSON . stringify ( result , null , 2 ) ,
158+ ) ;
159+ } ,
160+ } ) ,
161+ } satisfies Partial < Record < SemanticCliCommand , ClientCommandHandler > > ;
162+
163+ export const dedicatedSemanticCommandHandlers = formattedSemanticCommandHandlers ;
164+
40165function createGenericClientCommandHandler (
41166 command : PublicCommandName ,
42167 run : GenericClientCommandRunner ,
@@ -50,3 +175,41 @@ function createGenericClientCommandHandler(
50175 return true ;
51176 } ;
52177}
178+
179+ function createFormattedSemanticHandler (
180+ command : SemanticCliCommand ,
181+ options : {
182+ positionals ?: ( positionals : string [ ] ) => string [ ] ;
183+ beforeRun ?: ( params : { positionals : string [ ] ; flags : CliFlags } ) => void ;
184+ write : ( params : {
185+ positionals : string [ ] ;
186+ flags : CliFlags ;
187+ result : Awaited < ReturnType < typeof runSemanticCliCommand > > ;
188+ } ) => void ;
189+ } ,
190+ ) : ClientCommandHandler {
191+ return async ( { positionals, flags, client } ) => {
192+ options . beforeRun ?.( { positionals, flags } ) ;
193+ const semanticPositionals = options . positionals ?.( positionals ) ?? positionals ;
194+ const result = await runSemanticCliCommand ( {
195+ client,
196+ command,
197+ positionals : semanticPositionals ,
198+ flags,
199+ } ) ;
200+ options . write ( { positionals, flags, result } ) ;
201+ return true ;
202+ } ;
203+ }
204+
205+ function formatDeviceLine ( device : AgentDeviceDevice ) : string {
206+ const kind = device . kind ? ` ${ device . kind } ` : '' ;
207+ const target = device . target ? ` target=${ device . target } ` : '' ;
208+ const booted = typeof device . booted === 'boolean' ? ` booted=${ device . booted } ` : '' ;
209+ return `${ device . name } (${ device . platform } ${ kind } ${ target } )${ booted } ` ;
210+ }
211+
212+ function withoutUnchanged ( data : Record < string , unknown > ) : Record < string , unknown > {
213+ const { unchanged : _unchanged , ...outputData } = data ;
214+ return outputData ;
215+ }
0 commit comments