favacli 0.0.2 → 0.0.4

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.
Files changed (38) hide show
  1. package/build/BaseCommand.d.mts +16 -0
  2. package/build/BaseCommand.mjs +40 -0
  3. package/build/commands/entries/add.d.mts +15 -0
  4. package/build/commands/entries/add.mjs +31 -0
  5. package/build/commands/entries/list.d.mts +8 -0
  6. package/build/commands/entries/list.mjs +29 -0
  7. package/build/commands/entries/search.d.mts +10 -0
  8. package/build/commands/entries/search.mjs +31 -0
  9. package/build/commands/sync/connect.d.mts +9 -0
  10. package/build/commands/sync/connect.mjs +27 -0
  11. package/build/commands/sync/setServerUrl.d.mts +10 -0
  12. package/build/commands/sync/setServerUrl.mjs +15 -0
  13. package/build/commands/vault/create.d.mts +9 -0
  14. package/build/commands/vault/create.mjs +31 -0
  15. package/build/main.d.mts +2 -0
  16. package/build/main.mjs +21 -0
  17. package/build/utils/generateEntriesTable.d.mts +3 -0
  18. package/build/utils/generateEntriesTable.mjs +35 -0
  19. package/build/utils/init.d.mts +8 -0
  20. package/build/utils/init.mjs +30 -0
  21. package/build/utils/loadVault.d.mts +3 -0
  22. package/build/utils/loadVault.mjs +21 -0
  23. package/package.json +4 -5
  24. package/dist/007c5b826a6cdb53f76ac788701bcc44.node +0 -0
  25. package/dist/173.bundle.js +0 -1
  26. package/dist/206.bundle.js +0 -1
  27. package/dist/260.bundle.js +0 -2
  28. package/dist/260.bundle.js.LICENSE.txt +0 -16
  29. package/dist/304.bundle.js +0 -1
  30. package/dist/411.bundle.js +0 -1
  31. package/dist/477.bundle.js +0 -1
  32. package/dist/479.bundle.js +0 -1
  33. package/dist/622.bundle.js +0 -2
  34. package/dist/622.bundle.js.LICENSE.txt +0 -41
  35. package/dist/770.bundle.js +0 -1
  36. package/dist/87bec8279bd43a4f86aa53c27736e54f.node +0 -0
  37. package/dist/bundle.js +0 -2
  38. package/dist/bundle.js.LICENSE.txt +0 -15
@@ -0,0 +1,16 @@
1
+ import { Command } from 'clipanion';
2
+ import type { Jsonifiable } from 'type-fest';
3
+ import type { LockedRepresentationString, TwoFaLib } from 'favalib';
4
+ import { Settings } from './utils/init.mjs';
5
+ declare abstract class BaseCommand extends Command {
6
+ abstract exec(): Promise<Jsonifiable>;
7
+ abstract requireTwoFaLib: boolean;
8
+ json: boolean | undefined;
9
+ verbose: boolean | undefined;
10
+ lockedRepresentationString: LockedRepresentationString;
11
+ settings: Settings;
12
+ twoFaLib: TwoFaLib;
13
+ output(string: string): void;
14
+ execute(): Promise<number>;
15
+ }
16
+ export default BaseCommand;
@@ -0,0 +1,40 @@
1
+ import { Command, Option } from 'clipanion';
2
+ import loadVault from './utils/loadVault.mjs';
3
+ import init from './utils/init.mjs';
4
+ class BaseCommand extends Command {
5
+ constructor() {
6
+ super(...arguments);
7
+ this.json = Option.Boolean('--json', {
8
+ description: 'output as json',
9
+ });
10
+ this.verbose = Option.Boolean('--verbose', {
11
+ description: 'verbose output',
12
+ });
13
+ }
14
+ output(string) {
15
+ if (!this.json) {
16
+ this.context.stdout.write(string);
17
+ }
18
+ }
19
+ async execute() {
20
+ const { lockedRepresentationString, settings } = await init();
21
+ this.settings = settings;
22
+ if (lockedRepresentationString) {
23
+ this.twoFaLib = await loadVault(lockedRepresentationString, this.verbose);
24
+ }
25
+ else {
26
+ if (this.requireTwoFaLib) {
27
+ throw new Error('TwoFaLib is required');
28
+ }
29
+ }
30
+ const json = await this.exec();
31
+ if (this.json) {
32
+ this.context.stdout.write(JSON.stringify(json, null, 2) + '\n');
33
+ }
34
+ if (this.twoFaLib?.sync) {
35
+ this.twoFaLib.sync.closeServerConnection();
36
+ }
37
+ return 0;
38
+ }
39
+ }
40
+ export default BaseCommand;
@@ -0,0 +1,15 @@
1
+ import BaseCommand from '../../BaseCommand.mjs';
2
+ declare class EntriesAddCommand extends BaseCommand {
3
+ static paths: string[][];
4
+ requireTwoFaLib: boolean;
5
+ name: string;
6
+ issuer: string;
7
+ secret: string;
8
+ period: string;
9
+ digits: string;
10
+ algorithm: string;
11
+ exec(): Promise<{
12
+ success: boolean;
13
+ }>;
14
+ }
15
+ export default EntriesAddCommand;
@@ -0,0 +1,31 @@
1
+ import { Option } from 'clipanion';
2
+ import BaseCommand from '../../BaseCommand.mjs';
3
+ class EntriesAddCommand extends BaseCommand {
4
+ constructor() {
5
+ super(...arguments);
6
+ this.requireTwoFaLib = true;
7
+ this.name = Option.String('--name', { required: true });
8
+ this.issuer = Option.String('--issuer', { required: true });
9
+ this.secret = Option.String('--secret', { required: true });
10
+ this.period = Option.String('--period', '30');
11
+ this.digits = Option.String('--digits', '6');
12
+ this.algorithm = Option.String('--algorithm', 'SHA-1');
13
+ }
14
+ static { this.paths = [['entries', 'add']]; }
15
+ async exec() {
16
+ await this.twoFaLib.vault.addEntry({
17
+ name: this.name,
18
+ issuer: this.issuer,
19
+ type: 'TOTP',
20
+ payload: {
21
+ secret: this.secret,
22
+ period: Number.parseInt(this.period, 10),
23
+ digits: Number.parseInt(this.digits, 10),
24
+ algorithm: this.algorithm,
25
+ },
26
+ });
27
+ this.output('Entry added!');
28
+ return { success: true };
29
+ }
30
+ }
31
+ export default EntriesAddCommand;
@@ -0,0 +1,8 @@
1
+ import BaseCommand from '../../BaseCommand.mjs';
2
+ declare class EntriesListCommand extends BaseCommand {
3
+ static paths: string[][];
4
+ requireTwoFaLib: boolean;
5
+ withTokens: boolean | undefined;
6
+ exec(): Promise<0 | readonly import("type-fest").JsonValue[]>;
7
+ }
8
+ export default EntriesListCommand;
@@ -0,0 +1,29 @@
1
+ import { Option } from 'clipanion';
2
+ import BaseCommand from '../../BaseCommand.mjs';
3
+ import generateEntriesTable from '../../utils/generateEntriesTable.mjs';
4
+ class EntriesListCommand extends BaseCommand {
5
+ constructor() {
6
+ super(...arguments);
7
+ this.requireTwoFaLib = true;
8
+ this.withTokens = Option.Boolean('--withTokens', {
9
+ description: 'Include current TOTP tokens in the output',
10
+ });
11
+ }
12
+ static { this.paths = [['entries', 'list']]; }
13
+ async exec() {
14
+ let entries;
15
+ if (this.withTokens) {
16
+ entries = this.twoFaLib.vault.listEntriesMetas(true);
17
+ }
18
+ else {
19
+ entries = this.twoFaLib.vault.listEntriesMetas(false);
20
+ }
21
+ if (entries.length === 0) {
22
+ this.context.stdout.write('No entries\n');
23
+ return 0;
24
+ }
25
+ this.output(generateEntriesTable(entries));
26
+ return Promise.resolve(entries);
27
+ }
28
+ }
29
+ export default EntriesListCommand;
@@ -0,0 +1,10 @@
1
+ import type { JsonArray } from 'type-fest';
2
+ import BaseCommand from '../../BaseCommand.mjs';
3
+ declare class EntriesSearchCommand extends BaseCommand {
4
+ static paths: string[][];
5
+ requireTwoFaLib: boolean;
6
+ withTokens: boolean | undefined;
7
+ query: string;
8
+ exec(): Promise<JsonArray>;
9
+ }
10
+ export default EntriesSearchCommand;
@@ -0,0 +1,31 @@
1
+ import { Option } from 'clipanion';
2
+ import BaseCommand from '../../BaseCommand.mjs';
3
+ import generateEntriesTable from '../../utils/generateEntriesTable.mjs';
4
+ class EntriesSearchCommand extends BaseCommand {
5
+ constructor() {
6
+ super(...arguments);
7
+ this.requireTwoFaLib = true;
8
+ this.withTokens = Option.Boolean('--withTokens', {
9
+ description: 'Include current TOTP tokens in the output',
10
+ });
11
+ this.query = Option.String({ required: true });
12
+ }
13
+ static { this.paths = [['entries', 'search']]; }
14
+ async exec() {
15
+ let filteredEntries;
16
+ if (this.withTokens) {
17
+ filteredEntries = this.twoFaLib.vault.searchEntriesMetas(this.query, true);
18
+ }
19
+ else {
20
+ filteredEntries = this.twoFaLib.vault.searchEntriesMetas(this.query, false);
21
+ }
22
+ if (filteredEntries.length === 0) {
23
+ this.output('No matching entries found.\n');
24
+ return [];
25
+ }
26
+ const out = generateEntriesTable(filteredEntries);
27
+ this.output(out);
28
+ return Promise.resolve(filteredEntries);
29
+ }
30
+ }
31
+ export default EntriesSearchCommand;
@@ -0,0 +1,9 @@
1
+ import BaseCommand from '../../BaseCommand.mjs';
2
+ declare class ConnectCommand extends BaseCommand {
3
+ static paths: string[][];
4
+ requireTwoFaLib: boolean;
5
+ exec(): Promise<{
6
+ success: boolean;
7
+ }>;
8
+ }
9
+ export default ConnectCommand;
@@ -0,0 +1,27 @@
1
+ import { input } from '@inquirer/prompts';
2
+ import BaseCommand from '../../BaseCommand.mjs';
3
+ import { TwoFaLibEvent } from 'favalib';
4
+ class ConnectCommand extends BaseCommand {
5
+ constructor() {
6
+ super(...arguments);
7
+ this.requireTwoFaLib = true;
8
+ }
9
+ static { this.paths = [['sync', 'connect']]; }
10
+ async exec() {
11
+ if (!this.twoFaLib.sync) {
12
+ throw new Error('No server url set');
13
+ }
14
+ const connectionString = await input({
15
+ message: 'Enter connection string:',
16
+ });
17
+ const connectFinished = new Promise((resolve) => {
18
+ this.twoFaLib.addEventListener(TwoFaLibEvent.ConnectToExistingVaultFinished, () => {
19
+ resolve();
20
+ });
21
+ });
22
+ await this.twoFaLib.sync.respondToAddDeviceFlow(connectionString, 'text');
23
+ await connectFinished;
24
+ return { success: true };
25
+ }
26
+ }
27
+ export default ConnectCommand;
@@ -0,0 +1,10 @@
1
+ import BaseCommand from '../../BaseCommand.mjs';
2
+ declare class SetServerUrlCommand extends BaseCommand {
3
+ static paths: string[][];
4
+ requireTwoFaLib: boolean;
5
+ serverUrl: string;
6
+ exec(): Promise<{
7
+ success: boolean;
8
+ }>;
9
+ }
10
+ export default SetServerUrlCommand;
@@ -0,0 +1,15 @@
1
+ import { Option } from 'clipanion';
2
+ import BaseCommand from '../../BaseCommand.mjs';
3
+ class SetServerUrlCommand extends BaseCommand {
4
+ constructor() {
5
+ super(...arguments);
6
+ this.requireTwoFaLib = true;
7
+ this.serverUrl = Option.String({ required: true });
8
+ }
9
+ static { this.paths = [['sync', 'setServerUrl']]; }
10
+ async exec() {
11
+ await this.twoFaLib.setSyncServerUrl(this.serverUrl);
12
+ return { success: true };
13
+ }
14
+ }
15
+ export default SetServerUrlCommand;
@@ -0,0 +1,9 @@
1
+ import BaseCommand from '../../BaseCommand.mjs';
2
+ declare class VaultCreateCommand extends BaseCommand {
3
+ static paths: string[][];
4
+ requireTwoFaLib: boolean;
5
+ exec(): Promise<{
6
+ success: boolean;
7
+ }>;
8
+ }
9
+ export default VaultCreateCommand;
@@ -0,0 +1,31 @@
1
+ import fs from 'node:fs/promises';
2
+ import keytar from 'keytar';
3
+ import BaseCommand from '../../BaseCommand.mjs';
4
+ import { getTwoFaLibVaultCreationUtils, TwoFaLibEvent, } from 'favalib';
5
+ import NodeCryptoProvider from 'favalib/cryptoProviders/node';
6
+ import { password } from '@inquirer/prompts';
7
+ const cryptoLib = new NodeCryptoProvider();
8
+ const twoFaLibVaultCreationUtils = getTwoFaLibVaultCreationUtils(cryptoLib, 'cli', ['cli']);
9
+ class VaultCreateCommand extends BaseCommand {
10
+ constructor() {
11
+ super(...arguments);
12
+ this.requireTwoFaLib = false;
13
+ }
14
+ static { this.paths = [['vault', 'create']]; }
15
+ async exec() {
16
+ const passphrase = (await password({
17
+ message: 'Enter your vault passphrase:',
18
+ mask: '*',
19
+ }));
20
+ const { twoFaLib } = await twoFaLibVaultCreationUtils.createNewTwoFaLibVault(passphrase);
21
+ twoFaLib.addEventListener(TwoFaLibEvent.Changed, (ev) => {
22
+ return fs.writeFile('vault.json', ev.detail.newLockedRepresentationString);
23
+ });
24
+ await Promise.all([
25
+ twoFaLib.forceSave(),
26
+ keytar.setPassword('2falib', 'vault-passphrase', passphrase),
27
+ ]);
28
+ return { success: true };
29
+ }
30
+ }
31
+ export default VaultCreateCommand;
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/build/main.mjs ADDED
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env node
2
+ import { Cli } from 'clipanion';
3
+ import VaultCreateCommand from './commands/vault/create.mjs';
4
+ import EntriesAddCommand from './commands/entries/add.mjs';
5
+ import EntriesListCommand from './commands/entries/list.mjs';
6
+ import EntriesSearchCommand from './commands/entries/search.mjs';
7
+ import SyncSetServerUrlCommand from './commands/sync/setServerUrl.mjs';
8
+ import SyncConnect from './commands/sync/connect.mjs';
9
+ const [node, app, ...args] = process.argv;
10
+ const cli = new Cli({
11
+ binaryLabel: `My Application`,
12
+ binaryName: `${node} ${app}`,
13
+ binaryVersion: `1.0.0`,
14
+ });
15
+ cli.register(VaultCreateCommand);
16
+ cli.register(EntriesAddCommand);
17
+ cli.register(EntriesListCommand);
18
+ cli.register(EntriesSearchCommand);
19
+ cli.register(SyncSetServerUrlCommand);
20
+ cli.register(SyncConnect);
21
+ void cli.runExit(args);
@@ -0,0 +1,3 @@
1
+ import type { EntryMeta, EntryMetaWithToken } from 'favalib';
2
+ declare const generateEntriesTable: (entries: EntryMeta[] | EntryMetaWithToken[]) => string;
3
+ export default generateEntriesTable;
@@ -0,0 +1,35 @@
1
+ import Table from 'tty-table';
2
+ const generateEntriesTable = (entries) => {
3
+ const headers = [
4
+ { value: 'Id', align: 'left', width: 30 },
5
+ { value: 'Name', align: 'left', width: 30 },
6
+ { value: 'Issuer', align: 'left', width: 20 },
7
+ { value: 'Added at', align: 'left', width: 36 },
8
+ { value: 'Updated at', align: 'left', width: 36 },
9
+ ];
10
+ const withTokens = entries[0].token;
11
+ if (withTokens) {
12
+ headers.push({ value: 'Token', align: 'left', width: 10 });
13
+ }
14
+ const rows = entries.map((entry) => {
15
+ const row = [
16
+ entry.id,
17
+ entry.name || '-',
18
+ entry.issuer || '-',
19
+ new Date(entry.addedAt).toLocaleString(),
20
+ entry.updatedAt ? new Date(entry.updatedAt).toLocaleString() : '-',
21
+ ];
22
+ if (withTokens) {
23
+ row.push(entry.token.otp);
24
+ }
25
+ return row;
26
+ });
27
+ const tableOptions = {
28
+ borderStyle: 'solid',
29
+ paddingLeft: 1,
30
+ paddingRight: 1,
31
+ headerAlign: 'left',
32
+ };
33
+ return Table(headers, rows, [], tableOptions).render() + '\n';
34
+ };
35
+ export default generateEntriesTable;
@@ -0,0 +1,8 @@
1
+ import type { LockedRepresentationString } from 'favalib';
2
+ import type { EmptyObject } from 'type-fest';
3
+ export type Settings = EmptyObject;
4
+ declare const init: () => Promise<{
5
+ settings: EmptyObject;
6
+ lockedRepresentationString: LockedRepresentationString;
7
+ }>;
8
+ export default init;
@@ -0,0 +1,30 @@
1
+ import fs from 'node:fs/promises';
2
+ const defaultSettings = {};
3
+ const readFile = async (path) => {
4
+ try {
5
+ return (await fs.readFile(path)).toString();
6
+ }
7
+ catch (err) {
8
+ if (err.code === 'ENOENT') {
9
+ return null;
10
+ }
11
+ throw err;
12
+ }
13
+ };
14
+ const readJSONFile = async (path) => {
15
+ const contents = await readFile(path);
16
+ if (!contents) {
17
+ return null;
18
+ }
19
+ return JSON.parse(contents);
20
+ };
21
+ const init = async () => {
22
+ let settings = await readJSONFile('settings.json');
23
+ if (!settings) {
24
+ settings = defaultSettings;
25
+ await fs.writeFile('settings.json', JSON.stringify(settings, null, 2));
26
+ }
27
+ const lockedRepresentationString = (await readFile('vault.json'));
28
+ return { settings, lockedRepresentationString };
29
+ };
30
+ export default init;
@@ -0,0 +1,3 @@
1
+ import { type LockedRepresentationString } from 'favalib';
2
+ declare const loadVault: (vaultData: LockedRepresentationString, verbose?: boolean) => Promise<import("favalib").TwoFaLib>;
3
+ export default loadVault;
@@ -0,0 +1,21 @@
1
+ import fs from 'node:fs/promises';
2
+ import keytar from 'keytar';
3
+ import { getTwoFaLibVaultCreationUtils, TwoFaLibEvent, } from 'favalib';
4
+ import NodeCryptoProvider from 'favalib/cryptoProviders/node';
5
+ const cryptoLib = new NodeCryptoProvider();
6
+ const twoFaLibVaultCreationUtils = getTwoFaLibVaultCreationUtils(cryptoLib, 'cli', ['cli']);
7
+ const loadVault = async (vaultData, verbose = false) => {
8
+ const passphrase = (await keytar.getPassword('2falib', 'vault-passphrase'));
9
+ const twoFaLib = await twoFaLibVaultCreationUtils.loadTwoFaLibFromLockedRepesentation(vaultData, passphrase);
10
+ twoFaLib.addEventListener(TwoFaLibEvent.Changed, (ev) => {
11
+ return fs.writeFile('vault.json', ev.detail.newLockedRepresentationString);
12
+ });
13
+ twoFaLib.addEventListener(TwoFaLibEvent.Log, (ev) => {
14
+ if (ev.detail.severity !== 'info' || verbose) {
15
+ console.log(ev.detail);
16
+ }
17
+ });
18
+ await twoFaLib.ready;
19
+ return twoFaLib;
20
+ };
21
+ export default loadVault;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "favacli",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "author": "",
@@ -15,17 +15,16 @@
15
15
  "ts-loader": "^9.5.1",
16
16
  "tty-table": "^4.2.3",
17
17
  "typanion": "^3.14.0",
18
- "utf-8-validate": "^6.0.5",
19
- "webpack": "^5.96.1"
18
+ "utf-8-validate": "^6.0.5"
20
19
  },
21
20
  "devDependencies": {
22
21
  "type-fest": "^4.26.1",
23
22
  "webpack-cli": "^5.1.4"
24
23
  },
25
24
  "bin": {
26
- "favacli": "bundle.js"
25
+ "favacli": "main.mjs"
27
26
  },
28
27
  "files": [
29
- "dist/**"
28
+ "build/**"
30
29
  ]
31
30
  }