Check arguments before looking for the mnemonic (closes #79)
check / check (push) Waiting to run

The key index, --words, the --to recipients and the input file of age encrypt and age decrypt were checked only after the mnemonic had been obtained, so a run that could not succeed still prompted for it or ran the mnemonic command. options.Index now refuses an index above derive.MaxIndex, which covers every command group; the mnemonic command calls the new childmnemonic.CheckWords first; age encrypt reads its recipients with the new agekey.Recipients, and both age commands open their input, before the key is derived. Encrypt now takes the parsed recipients. The README says which checks come first.

Model: opus-5-5
This commit is contained in:
2026-10-14 18:26:16 +00:00
parent efca0ebb39
commit 1a9cc39dd2
9 changed files with 162 additions and 62 deletions
+47 -31
View File
@@ -108,47 +108,25 @@ func decrypt() *cobra.Command {
}
// runEncrypt encrypts to the derived recipient and any others given.
// The recipients are read and the input opened before the key is
// derived, so that a run refused for either never asks for the
// mnemonic.
func runEncrypt(cmd *cobra.Command, args []string) error {
key, err := derived(cmd)
if err != nil {
return err
}
to, err := cmd.Flags().GetStringArray("to")
names, err := cmd.Flags().GetStringArray("to")
if err != nil {
return fmt.Errorf("reading the recipients: %w", err)
}
to, err := agekey.Recipients(names)
if err != nil {
return err
}
armored, err := cmd.Flags().GetBool("armor")
if err != nil {
return fmt.Errorf("reading the armor flag: %w", err)
}
return through(cmd, args, func(dst io.Writer, src io.Reader) error {
return key.Encrypt(dst, src, to, armored)
})
}
// runDecrypt decrypts with the derived identity.
func runDecrypt(cmd *cobra.Command, args []string) error {
key, err := derived(cmd)
if err != nil {
return err
}
return through(cmd, args, key.Decrypt)
}
// through opens the input the arguments ask for and hands it to the
// work, with the file --output names to write to, or the command's own
// output when it names none or names the same file as that output, and
// the command's own error output when it names the same file as that.
// Those two are the streams the tool already has, so whatever they are
// redirected to is written as the redirect says, never replaced.
func through(
cmd *cobra.Command, args []string,
work func(io.Writer, io.Reader) error,
) error {
src, closeSrc, err := input(cmd, args)
if err != nil {
return err
@@ -156,6 +134,44 @@ func through(
defer closeSrc()
key, err := derived(cmd)
if err != nil {
return err
}
return through(cmd, src, func(dst io.Writer, plain io.Reader) error {
return key.Encrypt(dst, plain, to, armored)
})
}
// runDecrypt decrypts with the derived identity. The input is opened
// before the key is derived, as for runEncrypt.
func runDecrypt(cmd *cobra.Command, args []string) error {
src, closeSrc, err := input(cmd, args)
if err != nil {
return err
}
defer closeSrc()
key, err := derived(cmd)
if err != nil {
return err
}
return through(cmd, src, key.Decrypt)
}
// through hands the input to the work, with the file --output names to
// write to, or the command's own output when it names none or names the
// same file as that output, and the command's own error output when it
// names the same file as that. Those two are the streams the tool
// already has, so whatever they are redirected to is written as the
// redirect says, never replaced.
func through(
cmd *cobra.Command, src io.Reader,
work func(io.Writer, io.Reader) error,
) error {
name, err := cmd.Flags().GetString("output")
if err != nil {
return fmt.Errorf("reading the output file: %w", err)