@@ -677,54 +677,129 @@ export function parseVolumeMounts(mounts: string[]): ParseVolumeMountsResult | P
677677 return { success : true , mounts : result } ;
678678}
679679
680+ function formatItem (
681+ term : string ,
682+ description : string ,
683+ termWidth : number ,
684+ indent : number ,
685+ sep : number ,
686+ _helpWidth : number
687+ ) : string {
688+ const indentStr = ' '. repeat ( indent ) ;
689+ const fullWidth = termWidth + sep ;
690+ if ( description ) {
691+ if ( term . length < fullWidth - sep ) {
692+ return `${indentStr } ${term . padEnd ( fullWidth ) } ${description } `;
693+ }
694+ return `${indentStr } ${term } \n${' '. repeat ( indent + fullWidth ) } ${description } `;
695+ }
696+ return `${indentStr } ${term } `;
697+ }
698+
680699const program = new Command ( ) ;
681700
701+ // Option group markers used by the custom help formatter to insert section headers.
702+ // Each key is the long flag name of the first option in a group.
703+ const optionGroupHeaders : Record < string , string > = {
704+ 'allow - domains ': 'Domain Filtering :',
705+ 'build - local ': 'Image Management :',
706+ 'env ': 'Container Configuration :',
707+ 'dns - servers ': 'Network & Security :',
708+ 'enable - api - proxy ': 'API Proxy :',
709+ 'log - level ': 'Logging & Debug :',
710+ } ;
711+
682712program
683713 . name ( 'awf ')
684714 . description ( 'Network firewall for agentic workflows with domain whitelisting ')
685715 . version ( version )
716+ . configureHelp ( {
717+ formatHelp ( cmd , helper ) : string {
718+ const termWidth = helper . padWidth ( cmd , helper ) ;
719+ const helpWidth = ( helper as unknown as { helpWidth ?: number } ) . helpWidth ?? 80 ;
720+ const itemIndent = 2 ;
721+ const itemSep = 2 ;
722+
723+ const output : string [ ] = [ ] ;
724+
725+ // Usage line
726+ const usage = helper . commandUsage ( cmd ) ;
727+ output . push ( `Usage: ${ usage } ` ) ;
728+
729+ const desc = helper . commandDescription ( cmd ) ;
730+ if ( desc ) {
731+ output . push ( '' ) ;
732+ output . push ( desc ) ;
733+ }
734+
735+ // Arguments
736+ const args = helper . visibleArguments ( cmd ) ;
737+ if ( args . length > 0 ) {
738+ output . push ( '' ) ;
739+ output . push ( 'Arguments:' ) ;
740+ for ( const arg of args ) {
741+ const term = helper . argumentTerm ( arg ) ;
742+ const argDesc = helper . argumentDescription ( arg ) ;
743+ output . push ( formatItem ( term , argDesc , termWidth , itemIndent , itemSep , helpWidth ) ) ;
744+ }
745+ }
746+
747+ // Options with group headers
748+ const options = helper . visibleOptions ( cmd ) ;
749+ if ( options . length > 0 ) {
750+ output . push ( '' ) ;
751+ output . push ( 'Options:' ) ;
752+ for ( const opt of options ) {
753+ const flags = helper . optionTerm ( opt ) ;
754+ const optDesc = helper . optionDescription ( opt ) ;
755+ const longFlag = opt . long ?. replace ( / ^ - - / , '' ) ;
756+ if ( longFlag && optionGroupHeaders [ longFlag ] ) {
757+ output . push ( '' ) ;
758+ output . push ( ` ${ optionGroupHeaders [ longFlag ] } ` ) ;
759+ }
760+ output . push ( formatItem ( flags , optDesc , termWidth , itemIndent + 2 , itemSep , helpWidth ) ) ;
761+ }
762+ }
763+
764+ return output . join ( '\n' ) + '\n' ;
765+ }
766+ } )
767+
768+ // -- Domain Filtering --
686769 . option (
687770 '--allow-domains <domains>' ,
688771 'Comma-separated list of allowed domains. Supports wildcards and protocol prefixes:\n' +
689- ' github.com - exact domain + subdomains (HTTP & HTTPS)\n' +
690- ' *.github.com - any subdomain of github.com\n' +
691- ' api-*.example.com - api-* subdomains\n' +
692- ' https://secure.com - HTTPS only\n' +
693- ' http://legacy.com - HTTP only\n' +
694- ' localhost - auto-configure for local testing (Playwright, etc.)'
772+ ' github.com - exact domain + subdomains (HTTP & HTTPS)\n' +
773+ ' *.github.com - any subdomain of github.com\n' +
774+ ' api-*.example.com - api-* subdomains\n' +
775+ ' https://secure.com - HTTPS only\n' +
776+ ' http://legacy.com - HTTP only\n' +
777+ ' localhost - auto-configure for local testing (Playwright, etc.)'
695778 )
696779 . option (
697780 '--allow-domains-file <path>' ,
698- 'Path to file containing allowed domains (one per line or comma-separated , supports # comments)'
781+ 'Path to file with allowed domains (one per line, supports # comments)'
699782 )
700783 . option (
701784 '--block-domains <domains>' ,
702- 'Comma-separated list of blocked domains (takes precedence over allowed domains ). Supports wildcards.'
785+ 'Comma-separated blocked domains (overrides allow list ). Supports wildcards.'
703786 )
704787 . option (
705788 '--block-domains-file <path>' ,
706- 'Path to file containing blocked domains (one per line or comma-separated , supports # comments)'
789+ 'Path to file with blocked domains (one per line, supports # comments)'
707790 )
708791 . option (
709- '--log-level <level>' ,
710- 'Log level: debug, info, warn, error' ,
711- 'info'
712- )
713- . option (
714- '--keep-containers' ,
715- 'Keep containers running after command exits' ,
716- false
717- )
718- . option (
719- '--tty' ,
720- 'Allocate a pseudo-TTY for the container (required for interactive tools like Claude Code)' ,
792+ '--ssl-bump' ,
793+ 'Enable SSL Bump for HTTPS content inspection (allows URL path filtering)' ,
721794 false
722795 )
723796 . option (
724- '--work-dir <dir >' ,
725- 'Working directory for temporary files' ,
726- path . join ( os . tmpdir ( ) , `awf- ${ Date . now ( ) } ` )
797+ '--allow-urls <urls >' ,
798+ 'Comma-separated allowed URL patterns for HTTPS (requires --ssl-bump).\n' +
799+ ' Supports wildcards: https://github.com/myorg/*'
727800 )
801+
802+ // -- Image Management --
728803 . option (
729804 '--build-local' ,
730805 'Build containers locally instead of using GHCR images' ,
@@ -733,13 +808,13 @@ program
733808 . option (
734809 '--agent-image <value>' ,
735810 'Agent container image (default: "default")\n' +
736- ' Presets (pre-built, fast):\n' +
737- ' default - Minimal ubuntu:22.04 (~200MB)\n' +
738- ' act - GitHub Actions parity (~2GB)\n' +
739- ' Custom base images (requires --build-local):\n' +
740- ' ubuntu:XX.XX\n' +
741- ' ghcr.io/catthehacker/ubuntu:runner-XX.XX\n' +
742- ' ghcr.io/catthehacker/ubuntu:full-XX.XX'
811+ ' Presets (pre-built, fast):\n' +
812+ ' default - Minimal ubuntu:22.04 (~200MB)\n' +
813+ ' act - GitHub Actions parity (~2GB)\n' +
814+ ' Custom base images (requires --build-local):\n' +
815+ ' ubuntu:XX.XX\n' +
816+ ' ghcr.io/catthehacker/ubuntu:runner-XX.XX\n' +
817+ ' ghcr.io/catthehacker/ubuntu:full-XX.XX'
743818 )
744819 . option (
745820 '--image-registry <registry>' ,
@@ -748,18 +823,20 @@ program
748823 )
749824 . option (
750825 '--image-tag <tag>' ,
751- 'Container image tag' ,
826+ 'Container image tag (applied to --agent-image preset name, e.g. agent:<tag>) ' ,
752827 'latest'
753828 )
754829 . option (
755830 '--skip-pull' ,
756- 'Use local images without pulling from registry (requires images to be pre-downloaded)' ,
831+ 'Use local images without pulling from registry (requires pre-downloaded images )' ,
757832 false
758833 )
834+
835+ // -- Container Configuration --
759836 . option (
760837 '-e, --env <KEY=VALUE>' ,
761- 'Additional environment variables to pass to container (can be specified multiple times )' ,
762- ( value , previous : string [ ] = [ ] ) => [ ...previous , value ] ,
838+ 'Environment variable for the container (repeatable )' ,
839+ ( value : string , previous : string [ ] = [ ] ) => [ ...previous , value ] ,
763840 [ ]
764841 )
765842 . option (
@@ -769,74 +846,84 @@ program
769846 )
770847 . option (
771848 '-v, --mount <host_path:container_path[:mode]>' ,
772- 'Volume mount (can be specified multiple times ). Format: host_path:container_path[:ro|rw]' ,
773- ( value , previous : string [ ] = [ ] ) => [ ...previous , value ] ,
849+ 'Volume mount (repeatable ). Format: host_path:container_path[:ro|rw]' ,
850+ ( value : string , previous : string [ ] = [ ] ) => [ ...previous , value ] ,
774851 [ ]
775852 )
776853 . option (
777854 '--container-workdir <dir>' ,
778- 'Working directory inside the container (should match GITHUB_WORKSPACE for path consistency) '
855+ 'Working directory inside the container'
779856 )
780857 . option (
781- '--dns-servers <servers> ' ,
782- 'Comma-separated list of trusted DNS servers. DNS traffic is ONLY allowed to these servers (default: 8.8.8.8,8.8.4.4 )' ,
783- '8.8.8.8,8.8.4.4'
858+ '--tty ' ,
859+ 'Allocate a pseudo-TTY (required for interactive tools like Claude Code )' ,
860+ false
784861 )
862+
863+ // -- Network & Security --
785864 . option (
786- '--proxy-logs-dir <path>' ,
787- 'Directory to save Squid proxy logs to (writes access.log directly to this directory)'
865+ '--dns-servers <servers>' ,
866+ 'Comma-separated trusted DNS servers' ,
867+ '8.8.8.8,8.8.4.4'
788868 )
789869 . option (
790870 '--enable-host-access' ,
791- 'Enable access to host services via host.docker.internal. ' +
792- 'Security warning: When combined with --allow-domains host.docker.internal, ' +
793- 'containers can access ANY service on the host machine.' ,
871+ 'Enable access to host services via host.docker.internal' ,
794872 false
795873 )
796874 . option (
797875 '--allow-host-ports <ports>' ,
798- 'Comma-separated list of ports or port ranges to allow when using --enable-host-access. ' +
799- 'By default, only ports 80 and 443 are allowed. ' +
800- 'Example: --allow-host-ports 3000 or --allow-host-ports 3000,8080 or --allow-host-ports 3000-3010,8000-8090'
801- )
802- . option (
803- '--ssl-bump' ,
804- 'Enable SSL Bump for HTTPS content inspection (allows URL path filtering for HTTPS)' ,
805- false
806- )
807- . option (
808- '--allow-urls <urls>' ,
809- 'Comma-separated list of allowed URL patterns for HTTPS (requires --ssl-bump).\n' +
810- ' Supports wildcards: https://github.com/myorg/*'
876+ 'Ports/ranges to allow with --enable-host-access (default: 80,443).\n' +
877+ ' Example: 3000,8080 or 3000-3010,8000-8090'
811878 )
879+
880+ // -- API Proxy --
812881 . option (
813882 '--enable-api-proxy' ,
814- 'Enable API proxy sidecar for holding authentication credentials.\n' +
815- ' Deploys a Node.js proxy that injects API keys securely.\n' +
816- ' Supports OpenAI (Codex) and Anthropic (Claude) APIs.' ,
883+ 'Enable API proxy sidecar for secure credential injection.\n' +
884+ ' Supports OpenAI (Codex) and Anthropic (Claude) APIs.' ,
817885 false
818886 )
819887 . option (
820888 '--copilot-api-target <host>' ,
821- 'Target hostname for GitHub Copilot API requests in the api-proxy sidecar.\n' +
822- ' Defaults to api.githubcopilot.com. Useful for GHES deployments.\n' +
823- ' Can also be set via COPILOT_API_TARGET env var.' ,
889+ 'Target hostname for Copilot API requests (default: api.githubcopilot.com)' ,
824890 )
825891 . option (
826892 '--rate-limit-rpm <n>' ,
827- 'Enable rate limiting: max requests per minute per provider (requires --enable-api-proxy)' ,
893+ 'Max requests per minute per provider (requires --enable-api-proxy)' ,
828894 )
829895 . option (
830896 '--rate-limit-rph <n>' ,
831- 'Enable rate limiting: max requests per hour per provider (requires --enable-api-proxy)' ,
897+ 'Max requests per hour per provider (requires --enable-api-proxy)' ,
832898 )
833899 . option (
834900 '--rate-limit-bytes-pm <n>' ,
835- 'Enable rate limiting: max request bytes per minute per provider (requires --enable-api-proxy)' ,
901+ 'Max request bytes per minute per provider (requires --enable-api-proxy)' ,
836902 )
837903 . option (
838904 '--no-rate-limit' ,
839- 'Explicitly disable rate limiting in the API proxy (requires --enable-api-proxy)' ,
905+ 'Disable rate limiting in the API proxy (requires --enable-api-proxy)' ,
906+ )
907+
908+ // -- Logging & Debug --
909+ . option (
910+ '--log-level <level>' ,
911+ 'Log level: debug, info, warn, error' ,
912+ 'info'
913+ )
914+ . option (
915+ '--keep-containers' ,
916+ 'Keep containers running after command exits' ,
917+ false
918+ )
919+ . option (
920+ '--work-dir <dir>' ,
921+ 'Working directory for temporary files' ,
922+ path . join ( os . tmpdir ( ) , `awf-${ Date . now ( ) } ` )
923+ )
924+ . option (
925+ '--proxy-logs-dir <path>' ,
926+ 'Directory to save Squid proxy access.log'
840927 )
841928 . argument ( '[args...]' , 'Command and arguments to execute (use -- to separate from options)' )
842929 . action ( async ( args : string [ ] , options ) => {
0 commit comments