Skip to content

Commit a63a99f

Browse files
committed
refactor(cli): own injected daemon dispatches at a typed construction seam
The syntactic scan recognized only a direct sendToDaemon call whose first argument was an inline object literal, so a variable envelope or a computed callee was omitted from every result. Rather than teach the scanner more shapes, the CLI's injected dispatches now flow through one typed construction point whose route/command pairs are declared, and the gate reads that declaration instead of recovering it from syntax. Part of #1739 (wave 0)
1 parent c0ccaa6 commit a63a99f

4 files changed

Lines changed: 258 additions & 303 deletions

File tree

src/cli.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import type { FlagKey } from './commands/cli-grammar/flag-types.ts';
4646
import type { CliFlags } from '@agent-device/contracts/command';
4747
import type { SessionRuntimeHints } from '@agent-device/kernel/contracts';
4848
import { INTERNAL_COMMANDS, isKnownCliCommandName } from './command-catalog.ts';
49+
import { sendInjectedDaemonRequest } from './cli/injected-daemon-dispatch.ts';
4950

5051
type CliDeps = {
5152
sendToDaemon: typeof sendToDaemon;
@@ -342,9 +343,10 @@ async function runReactDevtoolsCli(ctx: CliRunContext, deps: CliDeps): Promise<n
342343
cwd: process.cwd(),
343344
env: process.env,
344345
configureDirectPortReverse: async () => {
345-
const response = await deps.sendToDaemon(
346-
{
347-
command: INTERNAL_COMMANDS.runtime,
346+
const response = await sendInjectedDaemonRequest({
347+
route: 'react-devtools',
348+
command: INTERNAL_COMMANDS.runtime,
349+
request: {
348350
positionals: ['port-reverse'],
349351
flags: {
350352
...directRequestFlags,
@@ -355,8 +357,9 @@ async function runReactDevtoolsCli(ctx: CliRunContext, deps: CliDeps): Promise<n
355357
},
356358
session: ctx.effectiveFlags.session ?? ctx.sessionName,
357359
},
358-
{ authToken: daemonAuthToken },
359-
);
360+
transport: deps.sendToDaemon,
361+
transportOptions: { authToken: daemonAuthToken },
362+
});
360363
if (!response.ok) throwDaemonError(response.error);
361364
},
362365
});
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import type { DaemonResponse, sendToDaemon } from '../daemon/client/daemon-client.ts';
2+
import { INTERNAL_COMMANDS } from '../command-catalog.ts';
3+
import type {
4+
DescriptorCliCommandName,
5+
DescriptorDaemonRouteCommandName,
6+
} from '../core/command-descriptor/registry.ts';
7+
8+
type CliDaemonTransport = typeof sendToDaemon;
9+
type CliDaemonRequest = Parameters<CliDaemonTransport>[0];
10+
type CliDaemonTransportOptions = Parameters<CliDaemonTransport>[1];
11+
12+
/**
13+
* The CLI's injected daemon dispatches (ADR 0019 §6).
14+
*
15+
* Ordinary commands reach the daemon through the client transport, carrying their own
16+
* name. A few CLI routes additionally inject a *different* command into their flow —
17+
* `react-devtools start` on a Limrun Android instance sets up its port reverse by
18+
* dispatching internal `runtime`. That injection is the case where a descriptor's own
19+
* module is platform-free while its route reaches platform behavior, so the pairs must
20+
* be enumerable to keep the migration denominator honest.
21+
*
22+
* This table is that enumeration, and {@link sendInjectedDaemonRequest} is the only way
23+
* to perform one: the route and command are typed parameters checked against this table,
24+
* so an undeclared pair is a compile error rather than something a scanner must find.
25+
*/
26+
export const CLI_INJECTED_DAEMON_DISPATCHES = {
27+
'react-devtools': [INTERNAL_COMMANDS.runtime],
28+
} as const satisfies Partial<
29+
Record<DescriptorCliCommandName, readonly DescriptorDaemonRouteCommandName[]>
30+
>;
31+
32+
export type CliInjectedRoute = keyof typeof CLI_INJECTED_DAEMON_DISPATCHES;
33+
34+
type InjectedCommandFor<Route extends CliInjectedRoute> =
35+
(typeof CLI_INJECTED_DAEMON_DISPATCHES)[Route][number];
36+
37+
/**
38+
* The single construction point for a CLI-injected daemon request. `command` comes from
39+
* the declared pair rather than from the caller's object literal, so the dispatched
40+
* command is known by type at every call site.
41+
*/
42+
export async function sendInjectedDaemonRequest<Route extends CliInjectedRoute>(params: {
43+
route: Route;
44+
command: InjectedCommandFor<Route>;
45+
request: Omit<CliDaemonRequest, 'command'>;
46+
transport: CliDaemonTransport;
47+
transportOptions?: CliDaemonTransportOptions;
48+
}): Promise<DaemonResponse> {
49+
const { command, request, transport, transportOptions } = params;
50+
return await transport({ ...request, command }, transportOptions);
51+
}

0 commit comments

Comments
 (0)