feat: add CommandName union type derived from COMMAND_SCHEMAS#237
feat: add CommandName union type derived from COMMAND_SCHEMAS#237ken0nek wants to merge 3 commits intocallstackincubator:mainfrom
Conversation
…cation Introduce a CommandName union type derived from a const COMMAND_NAMES array (52 entries: 46 CLI + 6 daemon-internal) and apply it to internal function signatures and lookup tables via satisfies checks. DaemonRequest.command stays string (untrusted wire boundary).
There was a problem hiding this comment.
Pull request overview
This PR introduces a centralized CommandName union type derived from a single COMMAND_NAMES source-of-truth and threads it through key command-related maps and APIs to catch typos at compile time (while keeping daemon wire inputs as string).
Changes:
- Add
src/core/command-names.tswithCOMMAND_NAMESand the derivedCommandNameunion type. - Apply
satisfiesconstraints across command maps (schemas/capabilities/client handlers/RPC mapping) to enforce valid keys/values at compile time. - Centralize CLI command alias normalization via
COMMAND_ALIASESand use it in argument parsing.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/core/command-names.ts | Adds canonical command list, CommandName union, and centralized CLI alias mapping. |
| src/utils/args.ts | Uses COMMAND_ALIASES to normalize CLI command aliases during parsing/usage. |
| src/utils/command-schema.ts | Applies satisfies to schema map to catch invalid command keys at compile time. |
| src/core/capabilities.ts | Applies satisfies to capability matrix to catch invalid command keys at compile time. |
| src/core/dispatch.ts | Types dispatch/batch step command fields with CommandName. |
| src/core/batch.ts | Types normalized batch steps/results with CommandName. |
| src/daemon/request-router.ts | Adds CommandName typing/satisfies usage and casts command when dispatching. |
| src/daemon/http-server.ts | Ensures lease RPC method mapping values are valid CommandNames via satisfies. |
| src/daemon/handlers/session.ts | Narrows internal dispatch parameter types to CommandName. |
| src/daemon/handlers/session-close.ts | Narrows dispatch parameter types to CommandName. |
| src/client.ts | Types internal execute command parameter as CommandName. |
| src/cli-client-commands.ts | Applies satisfies to client handler map to catch invalid command keys. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| normalized.push({ | ||
| command, | ||
| command: command as CommandName, | ||
| positionals: positionals as string[], | ||
| flags: (step.flags ?? {}) as Partial<CommandFlags>, |
| const data = await dispatch(session.device, command as CommandName, resolvedPositionals, resolvedOut, { | ||
| ...contextFromFlags( |
|
I like the intent here, but I don’t think Right now we already have command-bearing structures that act as the practical source of truth, especially I’d be more comfortable with this if the command union were derived from existing structures instead of introduced as a new top-level list. For example:
So overall: good goal, but I don’t think the extra |
Instead of maintaining a separate COMMAND_NAMES array, derive CliCommandName from keyof typeof COMMAND_SCHEMAS. Only the 6 daemon-internal commands remain as a small explicit union.
There was a problem hiding this comment.
Pull request overview
This PR introduces a canonical CommandName union type (derived from CLI command schema keys plus a small daemon-internal set) and threads it through dispatch/client/batch-related types to catch command-key typos at compile time without changing runtime behavior.
Changes:
- Derive
CliCommandNamefrom the CLI command schema keys and introduceCommandNamein a newsrc/core/command-names.ts. - Apply
satisfies-based compile-time checks to several command-keyed maps and tighten types for dispatch, batch steps, and client execution. - Centralize CLI command alias normalization via
COMMAND_ALIASES.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/utils/command-schema.ts | Refactors schema object into commandSchemaEntries and exports CliCommandName derived from its keys. |
| src/utils/args.ts | Uses centralized COMMAND_ALIASES for CLI alias normalization. |
| src/daemon/request-router.ts | Adds CommandName typing to exempt-command lists and casts command for dispatch. |
| src/daemon/http-server.ts | Applies satisfies to ensure RPC→command mapping values conform to CommandName. |
| src/daemon/handlers/session.ts | Narrows runSessionOrSelectorDispatch’s command parameter to CommandName. |
| src/daemon/handlers/session-close.ts | Narrows close handler dispatch signature to CommandName. |
| src/core/dispatch.ts | Types dispatchCommand and BatchStep.command with CommandName. |
| src/core/command-names.ts | Adds CommandName union type and COMMAND_ALIASES mapping. |
| src/core/capabilities.ts | Adds satisfies check tying capability-matrix keys to CommandName. |
| src/core/batch.ts | Types normalized batch command/result command with CommandName and adds casts. |
| src/client.ts | Types internal client execute() command parameter as CommandName. |
| src/cli-client-commands.ts | Applies satisfies to ensure handler-map keys are within CommandName. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| export async function dispatchCommand( | ||
| device: DeviceInfo, | ||
| command: string, | ||
| command: CommandName, | ||
| positionals: string[], |
| const data = await dispatch(session.device, command as CommandName, resolvedPositionals, resolvedOut, { | ||
| ...contextFromFlags( |
| normalized.push({ | ||
| command, | ||
| command: command as CommandName, | ||
| positionals: positionals as string[], | ||
| flags: (step.flags ?? {}) as Partial<CommandFlags>, |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR introduces a canonical CommandName union type (derived from COMMAND_SCHEMAS keys + a small daemon-internal union) and threads it through dispatch/client/batch plumbing to catch command-key typos at compile time, while centralizing legacy CLI aliases.
Changes:
- Derive
CliCommandNamefrom the command schema keys and defineCommandName = CliCommandName | DaemonInternalCommandName. - Add compile-time key validation via
satisfiesfor capability/handler/method maps. - Centralize CLI alias normalization via
COMMAND_ALIASESand update call sites to useCommandNametypes.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/utils/command-schema.ts | Refactors command schema object to enable deriving CliCommandName from keys. |
| src/utils/args.ts | Uses centralized COMMAND_ALIASES for legacy CLI alias normalization. |
| src/daemon/request-router.ts | Tightens command-related typing and adds compile-time checking for exempt command sets; dispatch call now uses CommandName. |
| src/daemon/http-server.ts | Adds satisfies check to ensure lease RPC method mapping values are valid CommandNames. |
| src/daemon/handlers/session.ts | Narrows internal dispatch helper param command to CommandName. |
| src/daemon/handlers/session-close.ts | Narrows close handler dispatch signature command to CommandName. |
| src/core/dispatch.ts | Updates dispatchCommand and batch step typing to use CommandName. |
| src/core/command-names.ts | Adds new canonical CommandName type and COMMAND_ALIASES record. |
| src/core/capabilities.ts | Adds satisfies check to validate capability matrix keys against CommandName. |
| src/core/batch.ts | Updates normalized batch step/result command typing to CommandName. |
| src/client.ts | Narrows internal client execute() command parameter to CommandName. |
| src/cli-client-commands.ts | Adds satisfies check to validate client handler keys against CommandName. |
Comments suppressed due to low confidence (1)
src/core/dispatch.ts:38
BatchStep.commandis now typed asCommandName, but batch steps originate from untrusted JSON (parseBatchStepsJsonreturnsparsed as BatchStep[]). This makes the type unsound: callers can treatcommandas validated even though it isn't. Consider keepingBatchStep.commandasstring(untrusted) and only narrowing toCommandNameafter explicit validation/normalization.
export type BatchStep = {
command: CommandName;
positionals?: string[];
flags?: Partial<CommandFlags>;
runtime?: unknown;
};
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| } | ||
| normalized.push({ | ||
| command, | ||
| command: command as CommandName, | ||
| positionals: positionals as string[], | ||
| flags: (step.flags ?? {}) as Partial<CommandFlags>, |
| ? { ...(lockedReq.flags ?? {}), out: resolvedOut } | ||
| : (lockedReq.flags ?? {}); | ||
| const data = await dispatch(session.device, command, resolvedPositionals, resolvedOut, { | ||
| const data = await dispatch(session.device, command as CommandName, resolvedPositionals, resolvedOut, { | ||
| ...contextFromFlags( |
| ]); | ||
|
|
||
| const COMMAND_SCHEMAS: Record<string, CommandSchema> = { | ||
| const commandSchemaEntries = { |
|
@thymikee Thank you for your feedback! Addressed. Please take a look when you have a chance. |
Summary
CliCommandNametype fromkeyof typeof COMMAND_SCHEMAS— no separate manually-maintained listinstall_source,lease_allocate, etc.) as a small separate unionCommandName = CliCommandName | DaemonInternalCommandName(46 CLI + 6 internal)satisfieschecks toCOMMAND_CAPABILITY_MATRIX,clientCommandHandlers, andLEASE_RPC_METHOD_TO_COMMANDto catch key typos at compile timedispatchCommand,BatchStep.command,NormalizedBatchStep.command, and clientexecute()withCommandNamelong-press→longpress,metrics→perf) intoCOMMAND_ALIASESrecordDaemonRequest.commandstaysstring— no wire-boundary changesZero runtime behavior change. Pure type addition.
Verification
pnpm typecheck— passespnpm test:unit— all 765 tests pass, 0 failurespnpm build— succeeds