mirror of
https://github.com/PR0M3TH3AN/SeedPass.git
synced 2026-11-03 11:22:03 +00:00
feat: interactive mode, so the bare command opens the vault
Python's `seedpass` launches its TUI when invoked with no subcommand. The port printed a help dump instead, which is why running the bare command looked broken: nothing was wrong with it, there was simply no way in. This adds the interactive mode that was missing. Scope is the daily path -- browse, search, copy, reveal, add a password / TOTP / key-value entry, archive, switch profile -- not a port of Python's v2 and v3 TUIs, which are around 20k lines of Textual between them. Piped or redirected, the bare command still prints the help: there is no terminal to drive. No new runtime dependencies. Node's tty and ANSI directly, rather than a TUI framework, because everything bundled here shares a process with unlocked seed phrases and the CLI's runtime surface is currently commander plus the noble/scure crypto libraries. The agent-blind rule from plan section 9.3 carries over intact, and matters more here than in the CLI because this screen renders the whole vault at once: list and detail views show metadata only, `c` copies without ever drawing the value, and `r` is the single explicit reveal -- on screen until the next keypress, then gone from the following frame. Everything is drawn on the alternate screen buffer, so a revealed secret cannot be recovered by scrolling back after quitting, and never lands in a terminal log that captures the main buffer. The key-value field is masked while being typed for the same reason. terminal.ts owes the user one thing above all: their shell back. Raw mode with a hidden cursor is an unusable terminal, so restoration is idempotent and wired to exit, SIGINT, SIGTERM, SIGHUP, and the uncaught-exception paths -- verified through a pty for each. Holding raw mode across the whole unlock sequence rather than toggling it per prompt also fixes a narrow leak: echo came back on between password attempts, so a password retyped after a typo was echoed in clear text. Tests drive the real screens through an injected terminal, including the redaction properties above. Two harness bugs worth recording, because both produced confident nonsense: sampling the frame count after dispatching a key made a synchronous redraw look like no redraw at all, and waiting only for "the screen stopped changing" returned during the second-long PBKDF2 a mutation performs, so the next keystroke landed on the previous screen and typed "q" into a form field instead of quitting. 122 CLI tests, 220 core tests, 36/36 cross-impl checks. Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
1 parent
b0e2a309a4
commit
1eab3bf137
8 files changed
+1699
-11
No files matched your search
@@ -154,6 +154,8 @@ import { AppDir, resolveAppDir, INDEX_FILENAME } from "./appDir.js";
|
||||
import { loadConfig, saveConfig, mutateConfig } from "./configFile.js";
|
||||
import { AgentClient, AgentDaemon, agentSocketPath, DEFAULT_TTL_SECONDS } from "./agent.js";
|
||||
import { AuditLog } from "./audit.js";
|
||||
import { runTui } from "./tui/app.js";
|
||||
import { Terminal } from "./tui/terminal.js";
|
||||
|
||||
export interface ProgramIo {
|
||||
out(line: string): void;
|
||||
@@ -392,6 +394,25 @@ export function buildProgram(io: ProgramIo = defaultIo): Command {
|
||||
})
|
||||
.exitOverride();
|
||||
|
||||
// No subcommand: launch the interactive mode, matching Python, whose bare
|
||||
// `seedpass` opens its TUI. Without this the first thing a new user saw
|
||||
// was a help dump — the command they were told to run appeared to do
|
||||
// nothing. Piped or redirected, there is no terminal to drive, so the help
|
||||
// remains the sensible answer.
|
||||
program.action(async () => {
|
||||
const opts = program.opts() as GlobalOpts;
|
||||
if (!Terminal.isInteractive()) {
|
||||
program.outputHelp();
|
||||
process.exitCode = 1;
|
||||
return;
|
||||
}
|
||||
const code = await runTui({
|
||||
...(opts.appDir !== undefined && { appDir: opts.appDir }),
|
||||
...(opts.fingerprint !== undefined && { fingerprint: opts.fingerprint }),
|
||||
});
|
||||
if (code !== 0) process.exitCode = code;
|
||||
});
|
||||
|
||||
program
|
||||
.command("capabilities")
|
||||
.description("machine-readable capability map")
|
||||
|
||||
Reference in new issue
Block a user