It took me quite a while of Googling and trial-and-error but I finally figured out how to actually do this. Thanks to Gyan for pointing me in the right direction.
I tried to do more of the process as one-liners but it always turned into a non-functional, undebuggable mess. If you think you could script this, please do so, test it, and then post it as a new answer.
pkt_duration_time line and save it into a file:
ffprobe -show_frames input.gif | grep pkt_duration_time= >frames.txt
Modify the line to the format that FFmpeg's concat filter requires:
sed 's/pkt_duration_time=/duration /g' frametimes.txt >frametimes.txt
Print a list of your input files (frames) located in
folder/ and modify the list to be the format FFmpeg's concat filter requires:
ls --quoting-style=shell folder/ >filenames.txt && sed -i -e 's/^/file /' filenames.txt
Combine your frame-time file and filenames file to a concat file that FFmpeg accepts:
paste -d \\n filenames.txt frametimes.txt > folder/concatfile.txt
We saved the concat file to where the input files are located because otherwise FFmpeg won't accept it. One more thing before we can encode the video: the data for the last frame needs to be present in the concat file twice for some reason. I don't have a CLI for that so just copy-paste it manually.
Now we can use FFmpeg to turn the image files to an animated video with correct frame times:
ffmpeg -f concat -safe 0 -i concatfile.txt -pix_fmt yuv420p -c:v libvpx-vp9 -b:v 0 -cpu-used 0 -tile-columns 2 -row-mt 1 -lag-in-frames 25 -threads 16 -crf 35 ../output.webm
-safe 0 switch is required because otherwise FFmpeg complains about unsafe file names. At least it did in my case, possibly because the file names contained square brackets.
-pix_fmt yuv420p ensures the correct color space is used. If you omit this, a color space that isn't standard for web video will be used and in the case of VP9 the resulting file will use Profile 1 which doesn't have as wide hardware decoding support as Profile 0. You could use
-profile:v 0 to make sure that this doesn't happen without you noticing.
The rest of the parameters are quality and rate control features for libvpx-vp9. Remember to at least use
-b:v 0, otherwise CRF won't behave as you'd expect. Also note that setting
-threads manually is required, otherwise encoding is limited to a single thread.