11import { execFileSync } from 'node:child_process'
22import { fileURLToPath } from 'node:url'
3+ import type { Command } from 'commander'
34import { describe , expect , it } from 'vitest'
45import {
56 commandReference ,
@@ -9,6 +10,7 @@ import {
910import { runEmbeddedCli } from '#sim-cli/embed'
1011import { V2_OPERATIONS } from '#sim-cli/generated/v2-api'
1112import { buildProgram , UNPUBLISHED_GROUPS } from '#sim-cli/program'
13+ import { operationOf } from '#sim-cli/runtime/called-operations'
1214import { cursorSlot } from '#sim-cli/runtime/request'
1315
1416const ROOT = fileURLToPath ( new URL ( '../../../../' , import . meta. url ) )
@@ -19,10 +21,30 @@ interface InventoryEntry {
1921 body ?: string
2022 mothershipUnavailable ?: true
2123 unpublished ?: true
24+ pathAliases ?: string [ ] [ ]
25+ renamedFrom ?: string [ ] [ ]
2226}
2327
2428let produced : InventoryEntry [ ] | undefined
2529
30+ /** The requests the embedded CLI sends for `argv`, answered with one generic success. */
31+ async function requests ( argv : string [ ] ) {
32+ const seen : string [ ] = [ ]
33+ const result = await runEmbeddedCli ( [ '--output' , 'json' , ...argv ] , {
34+ endpoint : 'https://sim.internal.test' ,
35+ apiKey : 'test-key' ,
36+ workspaceId : 'a2e3ab27-2f9d-4b8a-a2f2-3c47a1b0c9d1' ,
37+ transport : async ( input , init ) => {
38+ seen . push ( `${ init ?. method ?? 'GET' } ${ String ( input ) } ${ String ( init ?. body ?? '' ) } ` )
39+ return new Response ( JSON . stringify ( { data : { id : 'folder-1' , count : 0 } } ) , {
40+ status : 200 ,
41+ headers : { 'content-type' : 'application/json' } ,
42+ } )
43+ } ,
44+ } )
45+ return { exitCode : result . exitCode , stderr : result . stderr , seen }
46+ }
47+
2648/** Read the actual producer, including command aliases and generated API documents. */
2749function inventory ( ) : InventoryEntry [ ] {
2850 produced ??= JSON . parse (
@@ -118,6 +140,55 @@ describe('CLI reference producer', () => {
118140 expect ( flag ( 'files get' ) ) . toBeUndefined ( )
119141 } )
120142
143+ it ( 'publishes the aliases of each path segment, which the real CLI runs as the command' , async ( ) => {
144+ const commands = inventory ( )
145+ const aliasesOf = ( path : string ) =>
146+ commands . find ( ( entry ) => entry . path . join ( ' ' ) === path ) ?. pathAliases
147+ expect ( aliasesOf ( 'files move' ) ) . toEqual ( [ [ 'file' ] , [ 'mv' ] ] )
148+ expect ( aliasesOf ( 'knowledge folders list' ) ) . toEqual ( [ [ 'kb' ] , [ ] , [ 'ls' ] ] )
149+ expect ( aliasesOf ( 'workflows deploy' ) ) . toEqual ( [ [ 'workflow' ] , [ ] ] )
150+ expect ( aliasesOf ( 'blocks get' ) ) . toBeUndefined ( )
151+
152+ const canonical = await requests ( [ 'files' , 'move' , '--file-ids' , 'f1' , '--to' , 'folder-1' ] )
153+ expect ( canonical . exitCode , canonical . stderr ) . toBe ( 0 )
154+ expect ( canonical . seen . length ) . toBeGreaterThan ( 0 )
155+ expect ( await requests ( [ 'file' , 'mv' , '--file-ids' , 'f1' , '--to' , 'folder-1' ] ) ) . toEqual (
156+ canonical
157+ )
158+ } )
159+
160+ it ( 'publishes each renamed path on the command the real CLI still runs it as' , async ( ) => {
161+ const commands = inventory ( )
162+ const renamed = commands . flatMap ( ( entry ) =>
163+ ( entry . renamedFrom ?? [ ] ) . map ( ( from ) => ( { from, to : entry . path } ) )
164+ )
165+ expect ( renamed . map ( ( { from } ) => from . join ( ' ' ) ) . sort ( ) ) . toEqual ( [
166+ 'files restore create' ,
167+ 'tables columns run' ,
168+ 'tables count create' ,
169+ 'tables rows find' ,
170+ 'workflows deployment list' ,
171+ ] )
172+ const program = buildProgram ( )
173+ const resolve = ( path : readonly string [ ] ) =>
174+ path . reduce < Command | undefined > (
175+ ( node , name ) =>
176+ node ?. commands . find ( ( child ) => child . name ( ) === name || child . aliases ( ) . includes ( name ) ) ,
177+ program
178+ )
179+ for ( const { from, to } of renamed ) {
180+ const old = resolve ( from )
181+ expect ( old , from . join ( ' ' ) ) . toBeDefined ( )
182+ expect ( old && operationOf ( old ) , from . join ( ' ' ) ) . toBe ( operationOf ( resolve ( to ) ?? program ) )
183+ }
184+
185+ const current = await requests ( [ 'tables' , 'rows' , 'count' , 'table-1' ] )
186+ expect ( current . exitCode , current . stderr ) . toBe ( 0 )
187+ const retired = await requests ( [ 'tables' , 'count' , 'create' , 'table-1' ] )
188+ expect ( retired . exitCode , retired . stderr ) . toBe ( 0 )
189+ expect ( retired . seen ) . toEqual ( current . seen )
190+ } )
191+
121192 it ( 'the real CLI accepts both row bodies and prints their distinct 201 payloads' , async ( ) => {
122193 const identity = {
123194 endpoint : 'https://sim.internal.test' ,
0 commit comments