Skip to content

Commit 0b4fa1c

Browse files
committed
refactor: collapse semantic cli wrappers
1 parent c0a0f32 commit 0b4fa1c

9 files changed

Lines changed: 167 additions & 214 deletions

File tree

src/cli/commands/apps.ts

Lines changed: 0 additions & 27 deletions
This file was deleted.

src/cli/commands/devices.ts

Lines changed: 0 additions & 24 deletions
This file was deleted.

src/cli/commands/generic.ts

Lines changed: 165 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,36 @@
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';
222
import { announceReplayTestRun } from '../../cli-test.ts';
323
import { 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';
529
import type { CliFlags } from '../../utils/command-schema.ts';
30+
import { AppError } from '../../utils/errors.ts';
31+
import { formatSnapshotText } from '../../utils/output.ts';
632
import { writeCommandCliOutput } from './output.ts';
33+
import { writeCommandMessage, writeCommandOutput } from './shared.ts';
734
import type { PublicCommandName } from '../../command-catalog.ts';
835
import 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+
40165
function 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+
}

src/cli/commands/install.ts

Lines changed: 0 additions & 45 deletions
This file was deleted.

src/cli/commands/metro.ts

Lines changed: 0 additions & 16 deletions
This file was deleted.

src/cli/commands/open.ts

Lines changed: 0 additions & 29 deletions
This file was deleted.

src/cli/commands/router.ts

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,10 @@
11
import { applyCommandDefaults, type CliFlags } from '../../utils/command-schema.ts';
22
import type { AgentDeviceClient } from '../../client.ts';
3-
import { sessionCommand } from './session.ts';
4-
import { devicesCommand } from './devices.ts';
5-
import { metroCommand } from './metro.ts';
6-
import { appsCommand } from './apps.ts';
7-
import { installCommand, reinstallCommand, installFromSourceCommand } from './install.ts';
8-
import { openCommand, closeCommand } from './open.ts';
93
import { connectCommand, connectionCommand, disconnectCommand } from './connection.ts';
104
import { authCommand } from './auth.ts';
11-
import { snapshotCommand } from './snapshot.ts';
125
import { screenshotCommand, diffCommand } from './screenshot.ts';
136
import { clientCommandMethodHandlers } from './client-command.ts';
14-
import { genericClientCommandHandlers } from './generic.ts';
7+
import { dedicatedSemanticCommandHandlers, genericClientCommandHandlers } from './generic.ts';
158
import type { ClientCommandHandlerMap } from './router-types.ts';
169

1710
export type {
@@ -21,26 +14,17 @@ export type {
2114
} from './router-types.ts';
2215

2316
const dedicatedCliCommandHandlers = {
24-
session: sessionCommand,
25-
devices: devicesCommand,
26-
apps: appsCommand,
27-
metro: metroCommand,
28-
install: installCommand,
29-
reinstall: reinstallCommand,
30-
'install-from-source': installFromSourceCommand,
3117
connect: connectCommand,
3218
disconnect: disconnectCommand,
3319
connection: connectionCommand,
3420
auth: authCommand,
35-
open: openCommand,
36-
close: closeCommand,
37-
snapshot: snapshotCommand,
3821
screenshot: screenshotCommand,
3922
diff: diffCommand,
4023
} satisfies ClientCommandHandlerMap;
4124

4225
const clientCommandHandlers: ClientCommandHandlerMap = {
4326
...dedicatedCliCommandHandlers,
27+
...dedicatedSemanticCommandHandlers,
4428
...clientCommandMethodHandlers,
4529
...genericClientCommandHandlers,
4630
};

0 commit comments

Comments
 (0)