Skip to content

feat(wizard): add password and number option types#9

Merged
svyatov merged 1 commit into
mainfrom
feat/password-number-option-types
Jul 1, 2026
Merged

feat(wizard): add password and number option types#9
svyatov merged 1 commit into
mainfrom
feat/password-number-option-types

Conversation

@svyatov

@svyatov svyatov commented Jul 1, 2026

Copy link
Copy Markdown
Owner

Summary

Wizards can now collect two kinds of input they couldn't before: secrets and bounded numbers.

A password option masks input as it's typed, renders as **** everywhere oz prints a command or answer (dry-run, confirmation prompt, oz run show, and the pins/presets editors), and is never written to last-used state, presets, or pins — every run re-prompts. With secret_env: NAME, oz hands the value to the wrapped command through its environment instead of argv, keeping it off the process list on Linux.

A number option accepts integer or float input with optional inclusive min/max bounds, rejecting non-numeric, out-of-range, and non-finite (NaN/Inf) values, and omits its flag when left blank.

What changed

  • Two new types wired end to endOptionPassword and OptionNumber run through the enum, validator, field engine, and command builder, each added to the exhaustive switches so no render or build path silently falls through to a default.
  • Redaction lives at two surfaces, not sprinkled per call site. Part.Secret masks at the command renderer and FormatAnswer masks at the answer renderer, while PlainParts/FormatCommand keep the true value for exec and hermetic goldens. Persistence and display paths route through stripSecrets, which drops password-typed keys on every save and load — so a secret that predates the feature, or a value retyped into a field that became a password, is dropped rather than restored or shown.
  • Off-argv secret delivery. RunWithEnv appends NAME=value to the child environment; when secret_env is set the option emits nothing into argv. If both secret_env and flag are present, env delivery wins and oz prints a warning.
  • Config guardrails. secret_env requires a valid env-var name and the password type; min/max require number with min <= max; a positional password is rejected outright, since a positional secret can be neither masked nor kept off argv.

Design decisions

  • Redact at the boundaries, keep fidelity at the core. The one invariant is that the executed command and the test goldens carry the real value while every human-facing surface carries ****. Separating Part.Secret (renderer) from PlainParts (exec) is what lets both hold at once.
  • number reuses input. Only entry validation differs, so NumberField embeds InputField and injects its numeric validator through a validateFn hook instead of duplicating the update loop.
  • "Off the process list" is a Linux-only guarantee, documented as such. secret_env is strictly more private than a flag on Linux and no less private elsewhere; the README states the platform scope and the argv-visibility limitation of the flag fallback plainly, rather than implying a universal guarantee.

Validation

task lint -> 0 issues. task test -> all packages pass, including new coverage for: secret masking across equals-style, space-style, and space-style-with-=-in-value flags; raw-value fidelity on the exec/golden path; NaN/Inf rejection and min/max bounds; stripSecrets on the preset/pins/save paths; secret_env env delivery, name validation, and the both-set warning; and positional-password rejection.

No live TUI recording is attached — the behavior is covered by the test suite above; the interactive prompt itself is a standard Bubbletea field.

Two new option types for wizard configs:

- password: masked entry that renders as **** everywhere oz prints a command
  or answer (dry-run, confirmation prompt, oz run show, pins/presets editors)
  and is never written to persisted state (last-used, presets, pins) — every
  run re-prompts. An optional `secret_env: NAME` delivers the value through the
  child process environment instead of argv, keeping it off the process list
  on Linux; env wins over flag when both are set.
- number: integer/float entry with optional inclusive min/max bounds; rejects
  non-numeric, out-of-range, and non-finite (NaN/Inf) input, and omits its flag
  when left blank.

Redaction is enforced at the render surfaces (Part.Secret + FormatAnswer) and
at every persistence/display boundary (stripSecrets, applied on save and load),
while PlainParts/FormatCommand keep the real value for exec and hermetic
goldens. Config guardrails reject secret_env on non-password types, invalid env
var names, min/max on non-number types, min > max, and positional passwords.
@svyatov
svyatov merged commit 5d0afa5 into main Jul 1, 2026
5 checks passed
@svyatov
svyatov deleted the feat/password-number-option-types branch July 1, 2026 14:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant