bitty-tui 0.0.10 → 0.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
File without changes
@@ -139,6 +139,7 @@ export declare class Client {
139
139
  syncCache: SyncResponse | null;
140
140
  constructor(uri?: ClientConfig);
141
141
  private patchObject;
142
+ isVaultWarden(): boolean;
142
143
  setUrls(uri: ClientConfig): void;
143
144
  /**
144
145
  * Authenticates a user with the Bitwarden server using their email and password.
@@ -375,6 +375,10 @@ export class Client {
375
375
  }
376
376
  return result;
377
377
  }
378
+ isVaultWarden() {
379
+ return (!this.apiUrl.endsWith("bitwarden.eu/api") &&
380
+ !this.apiUrl.endsWith("bitwarden.com/api"));
381
+ }
378
382
  setUrls(uri) {
379
383
  if (uri.baseUrl) {
380
384
  this.apiUrl = uri.baseUrl + "/api";
@@ -695,7 +699,12 @@ export class Client {
695
699
  decrypt(value, key) {
696
700
  if (!value)
697
701
  return value;
698
- return mcbw.decrypt(value, key ?? this.keys.userKey);
702
+ try {
703
+ return mcbw.decrypt(value, key ?? this.keys.userKey);
704
+ }
705
+ catch (error) {
706
+ return value;
707
+ }
699
708
  }
700
709
  encryptCipher(obj, key) {
701
710
  const { ...ret } = obj;
@@ -5,7 +5,7 @@ import { CipherType } from "../../clients/bw.js";
5
5
  import { Button } from "../../components/Button.js";
6
6
  import { useState } from "react";
7
7
  import { MoreInfoTab } from "./MoreInfoTab.js";
8
- import { MainTab } from "./MainInfoTab.js";
8
+ import { MainTab } from "./MainTab.js";
9
9
  export function CipherDetail({ selectedCipher, isFocused, mode, onChange, onSave, }) {
10
10
  const [isMoreInfoTab, setIsMoreInfoTab] = useState(false);
11
11
  return (_jsx(Box, { flexDirection: "column", width: "60%", flexGrow: 1, paddingX: 1, borderStyle: "round", borderColor: isFocused ? primaryLight : "gray", borderLeftColor: "gray", children: selectedCipher && (_jsxs(Box, { flexDirection: "column", justifyContent: "space-between", flexGrow: 1, children: [isMoreInfoTab ? (_jsx(MoreInfoTab, { isFocused: isFocused, selectedCipher: selectedCipher, onChange: onChange })) : (_jsx(MainTab, { isFocused: isFocused, selectedCipher: selectedCipher, onChange: onChange })), _jsxs(Box, { marginTop: 1, flexShrink: 0, gap: 1, children: [mode !== "new" && (_jsx(Button, { width: "50%", isActive: isFocused, onClick: () => setIsMoreInfoTab(!isMoreInfoTab), children: "More" })), selectedCipher.type !== CipherType.SSHKey && (_jsx(Button, { doubleConfirm: true, width: "50%", isActive: isFocused, onClick: () => onSave(selectedCipher), children: "Save" }))] })] })) }));
@@ -14,9 +14,7 @@ export function MainTab({ isFocused, selectedCipher, onChange, }) {
14
14
  interval = setInterval(() => {
15
15
  setOtpTimeout((t) => {
16
16
  if (t <= 1) {
17
- const totp = authenticator.generate(selectedCipher.login.totp);
18
- selectedCipher.login.currentTotp = totp;
19
- setOtpCode(totp);
17
+ setOtpCode(authenticator.generate(selectedCipher.login.totp));
20
18
  return 30;
21
19
  }
22
20
  return t - 1;
@@ -0,0 +1,6 @@
1
+ import { Cipher } from "../../clients/bw.js";
2
+ export declare function MainTab({ isFocused, selectedCipher, onChange, }: {
3
+ isFocused: boolean;
4
+ selectedCipher: Cipher;
5
+ onChange: (cipher: Cipher) => void;
6
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,93 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Text } from "ink";
3
+ import { CipherType } from "../../clients/bw.js";
4
+ import { primaryLight } from "../../theme/style.js";
5
+ import { TextInput } from "../../components/TextInput.js";
6
+ import { useEffect, useState } from "react";
7
+ import { authenticator } from "otplib";
8
+ const OTP_INTERVAL = 30;
9
+ export function MainTab({ isFocused, selectedCipher, onChange, }) {
10
+ const [otpCode, setOtpCode] = useState("");
11
+ const [otpTimeout, setOtpTimeout] = useState(0);
12
+ const genOtp = () => {
13
+ if (selectedCipher.login?.totp) {
14
+ const totp = authenticator.generate(selectedCipher.login.totp);
15
+ selectedCipher.login.currentTotp = totp;
16
+ setOtpCode(totp);
17
+ }
18
+ };
19
+ useEffect(() => {
20
+ let interval = null;
21
+ if (selectedCipher?.login?.totp) {
22
+ genOtp();
23
+ setOtpTimeout(OTP_INTERVAL);
24
+ interval = setInterval(() => {
25
+ setOtpTimeout((t) => {
26
+ if (t <= 1) {
27
+ genOtp();
28
+ return OTP_INTERVAL;
29
+ }
30
+ return t - 1;
31
+ });
32
+ }, 1000);
33
+ }
34
+ else {
35
+ setOtpCode("");
36
+ }
37
+ return () => {
38
+ if (interval)
39
+ clearInterval(interval);
40
+ };
41
+ }, [selectedCipher?.login?.totp]);
42
+ return (_jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsxs(Box, { flexDirection: "row", children: [_jsx(Box, { width: 12, marginRight: 2, flexShrink: 0, children: _jsx(Text, { bold: true, color: isFocused ? primaryLight : "gray", children: "Name:" }) }), _jsx(Box, { flexGrow: 1, paddingLeft: 1, children: _jsx(TextInput, { inline: true, isActive: isFocused, value: selectedCipher.name, onChange: (value) => onChange({ ...selectedCipher, name: value }) }) })] }), selectedCipher.type === CipherType.Login && (_jsxs(Box, { flexDirection: "row", children: [_jsx(Box, { width: 12, marginRight: 2, flexShrink: 0, children: _jsx(Text, { bold: true, color: isFocused ? primaryLight : "gray", children: "Username:" }) }), _jsx(Box, { flexGrow: 1, paddingLeft: 1, children: _jsx(TextInput, { inline: true, isActive: isFocused, value: selectedCipher.login?.username ?? "", onChange: (value) => onChange({
43
+ ...selectedCipher,
44
+ login: { ...selectedCipher.login, username: value },
45
+ }) }) })] })), selectedCipher.type === CipherType.Login && (_jsxs(Box, { flexDirection: "row", children: [_jsxs(Box, { flexDirection: "row", flexGrow: 1, children: [_jsx(Box, { width: 12, marginRight: 2, flexShrink: 0, children: _jsx(Text, { bold: true, color: isFocused ? primaryLight : "gray", children: "Password:" }) }), _jsx(Box, { flexGrow: 1, paddingLeft: 1, children: _jsx(TextInput, { inline: true, isPassword: true, showPasswordOnFocus: true, isActive: isFocused, value: selectedCipher.login?.password ?? "", onChange: (value) => onChange({
46
+ ...selectedCipher,
47
+ login: { ...selectedCipher.login, password: value },
48
+ }) }) })] }), selectedCipher.login?.totp && (_jsxs(Box, { flexDirection: "row", flexGrow: 1, children: [_jsx(Box, { marginRight: 2, flexShrink: 0, children: _jsxs(Text, { bold: true, color: isFocused ? primaryLight : "gray", children: ["OTP (", otpTimeout.toString().padStart(2, "0"), "s):"] }) }), _jsx(Box, { flexGrow: 1, paddingLeft: 1, children: _jsx(TextInput, { inline: true, isActive: isFocused, value: otpCode }) })] }))] })), selectedCipher.type === CipherType.Login && (_jsxs(Box, { flexDirection: "row", children: [_jsx(Box, { width: 12, marginRight: 2, flexShrink: 0, children: _jsx(Text, { bold: true, color: isFocused ? primaryLight : "gray", children: "URL:" }) }), _jsx(Box, { flexGrow: 1, paddingLeft: 1, children: _jsx(TextInput, { inline: true, isActive: isFocused, value: selectedCipher.login?.uris?.[0]?.uri ?? "", onChange: (value) => onChange({
49
+ ...selectedCipher,
50
+ login: {
51
+ ...selectedCipher.login,
52
+ uris: [
53
+ { uri: value },
54
+ ...selectedCipher.login.uris.slice(1),
55
+ ],
56
+ },
57
+ }) }) })] })), selectedCipher.type === CipherType.SSHKey && (_jsxs(Box, { flexDirection: "row", children: [_jsx(Box, { width: 12, marginRight: 2, flexShrink: 0, children: _jsx(Text, { bold: true, color: isFocused ? primaryLight : "gray", children: "Private Key:" }) }), _jsx(Box, { flexGrow: 1, paddingLeft: 1, children: _jsx(TextInput, { inline: true, isActive: isFocused, value: selectedCipher.sshKey?.privateKey ?? "" }) })] })), selectedCipher.type === CipherType.SSHKey && (_jsxs(Box, { flexDirection: "row", children: [_jsx(Box, { width: 12, marginRight: 2, flexShrink: 0, children: _jsx(Text, { bold: true, color: isFocused ? primaryLight : "gray", children: "Public Key:" }) }), _jsx(Box, { flexGrow: 1, paddingLeft: 1, children: _jsx(TextInput, { inline: true, isActive: isFocused, value: selectedCipher.sshKey?.publicKey ?? "" }) })] })), selectedCipher.type === CipherType.Identity &&
58
+ selectedCipher.identity?.firstName && (_jsxs(Box, { flexDirection: "row", children: [_jsx(Box, { width: 12, marginRight: 2, flexShrink: 0, children: _jsx(Text, { bold: true, color: isFocused ? primaryLight : "gray", children: "First Name:" }) }), _jsx(Box, { flexGrow: 1, paddingLeft: 1, children: _jsx(TextInput, { inline: true, isActive: isFocused, value: selectedCipher.identity?.firstName ?? "", onChange: (value) => onChange({
59
+ ...selectedCipher,
60
+ identity: { ...selectedCipher.identity, firstName: value },
61
+ }) }) })] })), selectedCipher.type === CipherType.Identity &&
62
+ selectedCipher.identity?.lastName && (_jsxs(Box, { flexDirection: "row", children: [_jsx(Box, { width: 12, marginRight: 2, flexShrink: 0, children: _jsx(Text, { bold: true, color: isFocused ? primaryLight : "gray", children: "Last Name:" }) }), _jsx(Box, { flexGrow: 1, paddingLeft: 1, children: _jsx(TextInput, { inline: true, isActive: isFocused, value: selectedCipher.identity?.lastName ?? "", onChange: (value) => onChange({
63
+ ...selectedCipher,
64
+ identity: { ...selectedCipher.identity, lastName: value },
65
+ }) }) })] })), selectedCipher.type === CipherType.Identity &&
66
+ selectedCipher.identity?.username && (_jsxs(Box, { flexDirection: "row", children: [_jsx(Box, { width: 12, marginRight: 2, flexShrink: 0, children: _jsx(Text, { bold: true, color: isFocused ? primaryLight : "gray", children: "Username:" }) }), _jsx(Box, { flexGrow: 1, paddingLeft: 1, children: _jsx(TextInput, { inline: true, isActive: isFocused, value: selectedCipher.identity?.username ?? "", onChange: (value) => onChange({
67
+ ...selectedCipher,
68
+ identity: { ...selectedCipher.identity, username: value },
69
+ }) }) })] })), selectedCipher.type === CipherType.Identity &&
70
+ selectedCipher.identity?.city && (_jsxs(Box, { flexDirection: "row", children: [_jsx(Box, { width: 12, marginRight: 2, flexShrink: 0, children: _jsx(Text, { bold: true, color: isFocused ? primaryLight : "gray", children: "City:" }) }), _jsx(Box, { flexGrow: 1, paddingLeft: 1, children: _jsx(TextInput, { inline: true, isActive: isFocused, value: selectedCipher.identity?.city ?? "", onChange: (value) => onChange({
71
+ ...selectedCipher,
72
+ identity: { ...selectedCipher.identity, city: value },
73
+ }) }) })] })), selectedCipher.type === CipherType.Identity &&
74
+ selectedCipher.identity?.address1 && (_jsxs(Box, { flexDirection: "row", children: [_jsx(Box, { width: 12, marginRight: 2, flexShrink: 0, children: _jsx(Text, { bold: true, color: isFocused ? primaryLight : "gray", children: "Address:" }) }), _jsx(Box, { flexGrow: 1, paddingLeft: 1, children: _jsx(TextInput, { inline: true, isActive: isFocused, value: selectedCipher.identity?.address1 ?? "", onChange: (value) => onChange({
75
+ ...selectedCipher,
76
+ identity: { ...selectedCipher.identity, address1: value },
77
+ }) }) })] })), selectedCipher.type === CipherType.Identity &&
78
+ selectedCipher.identity?.country && (_jsxs(Box, { flexDirection: "row", children: [_jsx(Box, { width: 12, marginRight: 2, flexShrink: 0, children: _jsx(Text, { bold: true, color: isFocused ? primaryLight : "gray", children: "Country:" }) }), _jsx(Box, { flexGrow: 1, paddingLeft: 1, children: _jsx(TextInput, { inline: true, isActive: isFocused, value: selectedCipher.identity?.country ?? "", onChange: (value) => onChange({
79
+ ...selectedCipher,
80
+ identity: { ...selectedCipher.identity, country: value },
81
+ }) }) })] })), selectedCipher.type === CipherType.Identity &&
82
+ selectedCipher.identity?.email && (_jsxs(Box, { flexDirection: "row", children: [_jsx(Box, { width: 12, marginRight: 2, flexShrink: 0, children: _jsx(Text, { bold: true, color: isFocused ? primaryLight : "gray", children: "Email:" }) }), _jsx(Box, { flexGrow: 1, paddingLeft: 1, children: _jsx(TextInput, { inline: true, isActive: isFocused, value: selectedCipher.identity?.email ?? "", onChange: (value) => onChange({
83
+ ...selectedCipher,
84
+ identity: { ...selectedCipher.identity, email: value },
85
+ }) }) })] })), selectedCipher.type === CipherType.Identity &&
86
+ selectedCipher.identity?.phone && (_jsxs(Box, { flexDirection: "row", children: [_jsx(Box, { width: 12, marginRight: 2, flexShrink: 0, children: _jsx(Text, { bold: true, color: isFocused ? primaryLight : "gray", children: "Phone:" }) }), _jsx(Box, { flexGrow: 1, paddingLeft: 1, children: _jsx(TextInput, { inline: true, isActive: isFocused, value: selectedCipher.identity?.phone ?? "", onChange: (value) => onChange({
87
+ ...selectedCipher,
88
+ identity: { ...selectedCipher.identity, phone: value },
89
+ }) }) })] })), _jsxs(Box, { flexDirection: "row", children: [_jsx(Box, { width: 12, flexShrink: 0, marginRight: 2, children: _jsx(Text, { bold: true, color: isFocused ? primaryLight : "gray", children: "Notes:" }) }), _jsx(Box, { flexGrow: 1, minHeight: 7, children: _jsx(TextInput, { multiline: true, maxLines: 5, isActive: isFocused, value: selectedCipher.notes ?? "", onChange: (value) => onChange({
90
+ ...selectedCipher,
91
+ notes: value,
92
+ }) }) })] })] }));
93
+ }
@@ -103,7 +103,9 @@ export function LoginView({ onLogin }) {
103
103
  })();
104
104
  }, []);
105
105
  return (_jsxs(Box, { flexDirection: "column", alignItems: "center", padding: 1, flexGrow: 1, minHeight: stdout.rows - 2, children: [_jsx(Box, { marginBottom: 2, children: _jsx(Text, { color: primary, children: art }) }), loading ? (_jsx(Text, { children: "Loading..." })) : askMfa ? (_jsx(Box, { flexDirection: "column", width: "50%", children: Object.values(askMfa).map((provider) => (_jsx(Button, { autoFocus: true, onClick: () => {
106
- if (provider === "1") {
106
+ if (provider === "1" &&
107
+ (Object.values(askMfa).length > 1 ||
108
+ !bwClient.isVaultWarden())) {
107
109
  bwClient.sendEmailMfaCode(email);
108
110
  }
109
111
  setMfaParams((p) => ({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bitty-tui",
3
- "version": "0.0.10",
3
+ "version": "0.0.12",
4
4
  "license": "MIT",
5
5
  "repository": "https://github.com/mceck/bitty",
6
6
  "keywords": [