@fgv/ks 5.1.0-25 → 5.1.0-27

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/lib/help.js ADDED
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getHelpText = getHelpText;
4
+ const ts_utils_1 = require("@fgv/ts-utils");
5
+ const AVAILABLE_HELP_TOPICS = ['overview', 'commands', 'password', 'template'];
6
+ const HELP_OVERVIEW = `ks manages ts-extras keystore files.
7
+
8
+ Start here:
9
+ ks help overview
10
+ ks help commands
11
+ ks help password
12
+ ks help template
13
+
14
+ Default keystore:
15
+ ~/.fgv-ks
16
+
17
+ Common commands:
18
+ ks init
19
+ ks put <name>
20
+ ks get <name>
21
+ ks list
22
+ ks remove <name>
23
+ ks export
24
+ ks session`;
25
+ const HELP_COMMANDS = `ks commands
26
+
27
+ ks init Create a new keystore
28
+ ks password Change the keystore password
29
+ ks put <name> Store a secret in the keystore
30
+ ks get <name> Read a secret from the keystore
31
+ ks list List stored secrets
32
+ ks remove <name> Remove a stored secret
33
+ ks export Render a shell template from keystore secrets
34
+ ks session Emit a shell export statement for the password
35
+ ks help [topic] Show self-contained help on a specific topic`;
36
+ const HELP_PASSWORD = `ks password sources
37
+
38
+ Passwords may be supplied with:
39
+ --password-file <path>
40
+ --password-stdin
41
+ --password-env <name>
42
+ FGV_KS_PASSWORD
43
+ KS_PASSWORD
44
+
45
+ If no password source is provided, ks prompts interactively.
46
+ The session command can emit a shell export statement:
47
+ ks session --var FGV_KS_PASSWORD`;
48
+ const HELP_TEMPLATE = `ks export template format
49
+
50
+ Templates use simple Mustache-style variables such as {{xai}}.
51
+ Only simple variables are supported; sections and other advanced Mustache features are rejected.
52
+
53
+ Options:
54
+ --template-file <path> Read the shell template from a file
55
+ --template-string <text> Use an inline shell template string
56
+ --persist-missing Save prompted secrets back to the keystore
57
+ --clipboard Copy the rendered output to the clipboard
58
+
59
+ Missing secrets referenced by the template are prompted interactively.
60
+ Rendered values are shell-quoted before output.
61
+
62
+ Example:
63
+ ks export --template-file ./keystore.template.sh
64
+ ks export --template-string 'export XAI_API_KEY={{xai}}'`;
65
+ const HELP_TEXTS = {
66
+ commands: HELP_COMMANDS,
67
+ overview: HELP_OVERVIEW,
68
+ password: HELP_PASSWORD,
69
+ template: HELP_TEMPLATE
70
+ };
71
+ function normalizeHelpTopic(topic) {
72
+ return topic.trim().toLowerCase();
73
+ }
74
+ function formatAvailableTopics() {
75
+ return AVAILABLE_HELP_TOPICS.join(', ');
76
+ }
77
+ function getHelpText(topic = undefined, program) {
78
+ if (topic === undefined || topic.trim().length === 0) {
79
+ return (0, ts_utils_1.succeed)(HELP_TEXTS.overview);
80
+ }
81
+ const normalized = normalizeHelpTopic(topic);
82
+ if (program !== undefined) {
83
+ const command = program.commands.find((candidate) => candidate.name() === normalized);
84
+ if (command !== undefined && command.name() !== 'help') {
85
+ return (0, ts_utils_1.succeed)(command.helpInformation());
86
+ }
87
+ }
88
+ switch (normalized) {
89
+ case 'help':
90
+ case 'overview':
91
+ return (0, ts_utils_1.succeed)(HELP_TEXTS.overview);
92
+ case 'commands':
93
+ case 'command':
94
+ return (0, ts_utils_1.succeed)(HELP_TEXTS.commands);
95
+ case 'password':
96
+ case 'passwords':
97
+ return (0, ts_utils_1.succeed)(HELP_TEXTS.password);
98
+ case 'template':
99
+ return (0, ts_utils_1.succeed)(HELP_TEXTS.template);
100
+ default:
101
+ return (0, ts_utils_1.fail)(`Unknown help topic '${topic}'. Available topics: ${formatAvailableTopics()}`);
102
+ }
103
+ }
104
+ //# sourceMappingURL=help.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"help.js","sourceRoot":"","sources":["../src/help.ts"],"names":[],"mappings":";;AAsFA,kCA6BC;AAjHD,4CAAsD;AAItD,MAAM,qBAAqB,GAAyB,CAAC,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;AAErG,MAAM,aAAa,GAAW;;;;;;;;;;;;;;;;;;aAkBjB,CAAC;AAEd,MAAM,aAAa,GAAW;;;;;;;;;;kEAUoC,CAAC;AAEnE,MAAM,aAAa,GAAW;;;;;;;;;;;mCAWK,CAAC;AAEpC,MAAM,aAAa,GAAW;;;;;;;;;;;;;;;;2DAgB6B,CAAC;AAE5D,MAAM,UAAU,GAAwC;IACtD,QAAQ,EAAE,aAAa;IACvB,QAAQ,EAAE,aAAa;IACvB,QAAQ,EAAE,aAAa;IACvB,QAAQ,EAAE,aAAa;CACxB,CAAC;AAEF,SAAS,kBAAkB,CAAC,KAAa;IACvC,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;AACpC,CAAC;AAED,SAAS,qBAAqB;IAC5B,OAAO,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1C,CAAC;AAED,SAAgB,WAAW,CAAC,QAA4B,SAAS,EAAE,OAAiB;IAClF,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrD,OAAO,IAAA,kBAAO,EAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IACtC,CAAC;IAED,MAAM,UAAU,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;IAE7C,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,MAAM,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,SAAkB,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,KAAK,UAAU,CAAC,CAAC;QAC/F,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,CAAC,IAAI,EAAE,KAAK,MAAM,EAAE,CAAC;YACvD,OAAO,IAAA,kBAAO,EAAC,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC;IAED,QAAQ,UAAU,EAAE,CAAC;QACnB,KAAK,MAAM,CAAC;QACZ,KAAK,UAAU;YACb,OAAO,IAAA,kBAAO,EAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QACtC,KAAK,UAAU,CAAC;QAChB,KAAK,SAAS;YACZ,OAAO,IAAA,kBAAO,EAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QACtC,KAAK,UAAU,CAAC;QAChB,KAAK,WAAW;YACd,OAAO,IAAA,kBAAO,EAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QACtC,KAAK,UAAU;YACb,OAAO,IAAA,kBAAO,EAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QACtC;YACE,OAAO,IAAA,eAAI,EAAC,uBAAuB,KAAK,wBAAwB,qBAAqB,EAAE,EAAE,CAAC,CAAC;IAC/F,CAAC;AACH,CAAC","sourcesContent":["import { Command } from 'commander';\n\nimport { fail, Result, succeed } from '@fgv/ts-utils';\n\nexport type HelpTopic = 'overview' | 'commands' | 'password' | 'template';\n\nconst AVAILABLE_HELP_TOPICS: readonly HelpTopic[] = ['overview', 'commands', 'password', 'template'];\n\nconst HELP_OVERVIEW: string = `ks manages ts-extras keystore files.\n\nStart here:\n ks help overview\n ks help commands\n ks help password\n ks help template\n\nDefault keystore:\n ~/.fgv-ks\n\nCommon commands:\n ks init\n ks put <name>\n ks get <name>\n ks list\n ks remove <name>\n ks export\n ks session`;\n\nconst HELP_COMMANDS: string = `ks commands\n\n ks init Create a new keystore\n ks password Change the keystore password\n ks put <name> Store a secret in the keystore\n ks get <name> Read a secret from the keystore\n ks list List stored secrets\n ks remove <name> Remove a stored secret\n ks export Render a shell template from keystore secrets\n ks session Emit a shell export statement for the password\n ks help [topic] Show self-contained help on a specific topic`;\n\nconst HELP_PASSWORD: string = `ks password sources\n\nPasswords may be supplied with:\n --password-file <path>\n --password-stdin\n --password-env <name>\n FGV_KS_PASSWORD\n KS_PASSWORD\n\nIf no password source is provided, ks prompts interactively.\nThe session command can emit a shell export statement:\n ks session --var FGV_KS_PASSWORD`;\n\nconst HELP_TEMPLATE: string = `ks export template format\n\nTemplates use simple Mustache-style variables such as {{xai}}.\nOnly simple variables are supported; sections and other advanced Mustache features are rejected.\n\nOptions:\n --template-file <path> Read the shell template from a file\n --template-string <text> Use an inline shell template string\n --persist-missing Save prompted secrets back to the keystore\n --clipboard Copy the rendered output to the clipboard\n\nMissing secrets referenced by the template are prompted interactively.\nRendered values are shell-quoted before output.\n\nExample:\n ks export --template-file ./keystore.template.sh\n ks export --template-string 'export XAI_API_KEY={{xai}}'`;\n\nconst HELP_TEXTS: Readonly<Record<HelpTopic, string>> = {\n commands: HELP_COMMANDS,\n overview: HELP_OVERVIEW,\n password: HELP_PASSWORD,\n template: HELP_TEMPLATE\n};\n\nfunction normalizeHelpTopic(topic: string): string {\n return topic.trim().toLowerCase();\n}\n\nfunction formatAvailableTopics(): string {\n return AVAILABLE_HELP_TOPICS.join(', ');\n}\n\nexport function getHelpText(topic: string | undefined = undefined, program?: Command): Result<string> {\n if (topic === undefined || topic.trim().length === 0) {\n return succeed(HELP_TEXTS.overview);\n }\n\n const normalized = normalizeHelpTopic(topic);\n\n if (program !== undefined) {\n const command = program.commands.find((candidate: Command) => candidate.name() === normalized);\n if (command !== undefined && command.name() !== 'help') {\n return succeed(command.helpInformation());\n }\n }\n\n switch (normalized) {\n case 'help':\n case 'overview':\n return succeed(HELP_TEXTS.overview);\n case 'commands':\n case 'command':\n return succeed(HELP_TEXTS.commands);\n case 'password':\n case 'passwords':\n return succeed(HELP_TEXTS.password);\n case 'template':\n return succeed(HELP_TEXTS.template);\n default:\n return fail(`Unknown help topic '${topic}'. Available topics: ${formatAvailableTopics()}`);\n }\n}\n"]}
package/lib/index.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ /**
2
+ * \@fgv/ks — CLI tool for managing ts-extras keystore files.
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ export { KsCli } from './app';
7
+ export { changeKeystorePassword, createKeystore, loadKeystoreFile, listSecrets, openKeystore, readSecret, removeSecret, resolveKeystorePath, saveKeystoreFile, storeSecret } from './keystore';
8
+ export { defaultKeystorePath, copyTextToClipboard, promptHidden, readAllFromStdin, readTextFile } from './io';
9
+ export { extractTemplateVariables, renderShellTemplate, shellQuote } from './template';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAC9B,OAAO,EACL,sBAAsB,EACtB,cAAc,EACd,gBAAgB,EAChB,WAAW,EACX,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,mBAAmB,EACnB,gBAAgB,EAChB,WAAW,EACZ,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,YAAY,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,MAAM,CAAC;AAC9G,OAAO,EAAE,wBAAwB,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
package/lib/index.js ADDED
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ /**
3
+ * \@fgv/ks — CLI tool for managing ts-extras keystore files.
4
+ *
5
+ * @packageDocumentation
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.shellQuote = exports.renderShellTemplate = exports.extractTemplateVariables = exports.readTextFile = exports.readAllFromStdin = exports.promptHidden = exports.copyTextToClipboard = exports.defaultKeystorePath = exports.storeSecret = exports.saveKeystoreFile = exports.resolveKeystorePath = exports.removeSecret = exports.readSecret = exports.openKeystore = exports.listSecrets = exports.loadKeystoreFile = exports.createKeystore = exports.changeKeystorePassword = exports.KsCli = void 0;
9
+ var app_1 = require("./app");
10
+ Object.defineProperty(exports, "KsCli", { enumerable: true, get: function () { return app_1.KsCli; } });
11
+ var keystore_1 = require("./keystore");
12
+ Object.defineProperty(exports, "changeKeystorePassword", { enumerable: true, get: function () { return keystore_1.changeKeystorePassword; } });
13
+ Object.defineProperty(exports, "createKeystore", { enumerable: true, get: function () { return keystore_1.createKeystore; } });
14
+ Object.defineProperty(exports, "loadKeystoreFile", { enumerable: true, get: function () { return keystore_1.loadKeystoreFile; } });
15
+ Object.defineProperty(exports, "listSecrets", { enumerable: true, get: function () { return keystore_1.listSecrets; } });
16
+ Object.defineProperty(exports, "openKeystore", { enumerable: true, get: function () { return keystore_1.openKeystore; } });
17
+ Object.defineProperty(exports, "readSecret", { enumerable: true, get: function () { return keystore_1.readSecret; } });
18
+ Object.defineProperty(exports, "removeSecret", { enumerable: true, get: function () { return keystore_1.removeSecret; } });
19
+ Object.defineProperty(exports, "resolveKeystorePath", { enumerable: true, get: function () { return keystore_1.resolveKeystorePath; } });
20
+ Object.defineProperty(exports, "saveKeystoreFile", { enumerable: true, get: function () { return keystore_1.saveKeystoreFile; } });
21
+ Object.defineProperty(exports, "storeSecret", { enumerable: true, get: function () { return keystore_1.storeSecret; } });
22
+ var io_1 = require("./io");
23
+ Object.defineProperty(exports, "defaultKeystorePath", { enumerable: true, get: function () { return io_1.defaultKeystorePath; } });
24
+ Object.defineProperty(exports, "copyTextToClipboard", { enumerable: true, get: function () { return io_1.copyTextToClipboard; } });
25
+ Object.defineProperty(exports, "promptHidden", { enumerable: true, get: function () { return io_1.promptHidden; } });
26
+ Object.defineProperty(exports, "readAllFromStdin", { enumerable: true, get: function () { return io_1.readAllFromStdin; } });
27
+ Object.defineProperty(exports, "readTextFile", { enumerable: true, get: function () { return io_1.readTextFile; } });
28
+ var template_1 = require("./template");
29
+ Object.defineProperty(exports, "extractTemplateVariables", { enumerable: true, get: function () { return template_1.extractTemplateVariables; } });
30
+ Object.defineProperty(exports, "renderShellTemplate", { enumerable: true, get: function () { return template_1.renderShellTemplate; } });
31
+ Object.defineProperty(exports, "shellQuote", { enumerable: true, get: function () { return template_1.shellQuote; } });
32
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;AAEH,6BAA8B;AAArB,4FAAA,KAAK,OAAA;AACd,uCAWoB;AAVlB,kHAAA,sBAAsB,OAAA;AACtB,0GAAA,cAAc,OAAA;AACd,4GAAA,gBAAgB,OAAA;AAChB,uGAAA,WAAW,OAAA;AACX,wGAAA,YAAY,OAAA;AACZ,sGAAA,UAAU,OAAA;AACV,wGAAA,YAAY,OAAA;AACZ,+GAAA,mBAAmB,OAAA;AACnB,4GAAA,gBAAgB,OAAA;AAChB,uGAAA,WAAW,OAAA;AAEb,2BAA8G;AAArG,yGAAA,mBAAmB,OAAA;AAAE,yGAAA,mBAAmB,OAAA;AAAE,kGAAA,YAAY,OAAA;AAAE,sGAAA,gBAAgB,OAAA;AAAE,kGAAA,YAAY,OAAA;AAC/F,uCAAuF;AAA9E,oHAAA,wBAAwB,OAAA;AAAE,+GAAA,mBAAmB,OAAA;AAAE,sGAAA,UAAU,OAAA","sourcesContent":["/**\n * \\@fgv/ks — CLI tool for managing ts-extras keystore files.\n *\n * @packageDocumentation\n */\n\nexport { KsCli } from './app';\nexport {\n changeKeystorePassword,\n createKeystore,\n loadKeystoreFile,\n listSecrets,\n openKeystore,\n readSecret,\n removeSecret,\n resolveKeystorePath,\n saveKeystoreFile,\n storeSecret\n} from './keystore';\nexport { defaultKeystorePath, copyTextToClipboard, promptHidden, readAllFromStdin, readTextFile } from './io';\nexport { extractTemplateVariables, renderShellTemplate, shellQuote } from './template';\n"]}
package/lib/io.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ import { Result } from '@fgv/ts-utils';
2
+ export declare function resolvePath(filePath: string): string;
3
+ export declare function defaultKeystorePath(): string;
4
+ export declare function readTextFile(filePath: string): Result<string>;
5
+ export declare function writeTextFile(filePath: string, contents: string): Result<string>;
6
+ export declare function readAllFromStdin(): Promise<Result<string>>;
7
+ export declare function promptHidden(prompt: string): Promise<Result<string>>;
8
+ export declare function promptVisible(prompt: string): Promise<Result<string>>;
9
+ export declare function copyTextToClipboard(text: string): Promise<Result<'copied'>>;
10
+ //# sourceMappingURL=io.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"io.d.ts","sourceRoot":"","sources":["../src/io.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,MAAM,EAAoD,MAAM,eAAe,CAAC;AAmBzF,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAK7D;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CA2BhF;AAED,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAehE;AAED,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAkD1E;AAED,wBAAsB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CA8B3E;AAED,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAOjF"}
package/lib/io.js ADDED
@@ -0,0 +1,211 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __asyncValues = (this && this.__asyncValues) || function (o) {
36
+ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
37
+ var m = o[Symbol.asyncIterator], i;
38
+ return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
39
+ function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
40
+ function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
41
+ };
42
+ var __importDefault = (this && this.__importDefault) || function (mod) {
43
+ return (mod && mod.__esModule) ? mod : { "default": mod };
44
+ };
45
+ Object.defineProperty(exports, "__esModule", { value: true });
46
+ exports.resolvePath = resolvePath;
47
+ exports.defaultKeystorePath = defaultKeystorePath;
48
+ exports.readTextFile = readTextFile;
49
+ exports.writeTextFile = writeTextFile;
50
+ exports.readAllFromStdin = readAllFromStdin;
51
+ exports.promptHidden = promptHidden;
52
+ exports.promptVisible = promptVisible;
53
+ exports.copyTextToClipboard = copyTextToClipboard;
54
+ const fs_1 = __importDefault(require("fs"));
55
+ const os_1 = __importDefault(require("os"));
56
+ const path_1 = __importDefault(require("path"));
57
+ const readline = __importStar(require("readline"));
58
+ const clipboardy_1 = __importDefault(require("clipboardy"));
59
+ const ts_json_base_1 = require("@fgv/ts-json-base");
60
+ const ts_utils_1 = require("@fgv/ts-utils");
61
+ function expandHome(filePath) {
62
+ if (!filePath.startsWith('~')) {
63
+ return filePath;
64
+ }
65
+ const home = os_1.default.homedir();
66
+ if (filePath === '~') {
67
+ return home;
68
+ }
69
+ if (filePath.startsWith('~/')) {
70
+ return path_1.default.join(home, filePath.substring(2));
71
+ }
72
+ return filePath;
73
+ }
74
+ function resolvePath(filePath) {
75
+ return path_1.default.resolve(expandHome(filePath));
76
+ }
77
+ function defaultKeystorePath() {
78
+ return path_1.default.join(os_1.default.homedir(), '.fgv-ks');
79
+ }
80
+ function readTextFile(filePath) {
81
+ const resolvedPath = resolvePath(filePath);
82
+ return ts_json_base_1.FileTree.forFilesystem().onSuccess((tree) => tree.getFile(resolvedPath).onSuccess((file) => file.getRawContents()));
83
+ }
84
+ function writeTextFile(filePath, contents) {
85
+ const resolvedPath = resolvePath(filePath);
86
+ const directoryPath = path_1.default.dirname(resolvedPath);
87
+ const fileName = path_1.default.basename(resolvedPath);
88
+ return ts_json_base_1.FileTree.forFilesystem({ mutable: true }).onSuccess((tree) => {
89
+ const accessors = tree.hal;
90
+ if (!ts_json_base_1.FileTree.isMutableAccessors(accessors)) {
91
+ return (0, ts_utils_1.fail)(`Unable to write '${resolvedPath}': filesystem access is read-only`);
92
+ }
93
+ return accessors.createDirectory(directoryPath).onSuccess(() => tree.getDirectory(directoryPath).onSuccess((directory) => {
94
+ if (!ts_json_base_1.FileTree.isMutableDirectoryItem(directory)) {
95
+ return (0, ts_utils_1.fail)(`Unable to write '${resolvedPath}': directory is not mutable`);
96
+ }
97
+ const tmpFileName = `${fileName}.tmp`;
98
+ return directory.createChildFile(tmpFileName, contents).onSuccess(() => (0, ts_utils_1.captureResult)(() => {
99
+ fs_1.default.renameSync(path_1.default.join(directoryPath, tmpFileName), resolvedPath);
100
+ return resolvedPath;
101
+ }));
102
+ }));
103
+ });
104
+ }
105
+ async function readAllFromStdin() {
106
+ const result = await (0, ts_utils_1.captureAsyncResult)(async () => {
107
+ var _a, e_1, _b, _c;
108
+ const chunks = [];
109
+ const input = process.stdin;
110
+ input.setEncoding('utf8');
111
+ input.resume();
112
+ try {
113
+ for (var _d = true, input_1 = __asyncValues(input), input_1_1; input_1_1 = await input_1.next(), _a = input_1_1.done, !_a; _d = true) {
114
+ _c = input_1_1.value;
115
+ _d = false;
116
+ const chunk = _c;
117
+ chunks.push(String(chunk));
118
+ }
119
+ }
120
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
121
+ finally {
122
+ try {
123
+ if (!_d && !_a && (_b = input_1.return)) await _b.call(input_1);
124
+ }
125
+ finally { if (e_1) throw e_1.error; }
126
+ }
127
+ return chunks.join('');
128
+ });
129
+ return result.withErrorFormat((message) => `Failed to read from stdin: ${message}`);
130
+ }
131
+ async function promptHidden(prompt) {
132
+ if (!process.stdin.isTTY) {
133
+ return (0, ts_utils_1.fail)(`Interactive prompt requires a TTY (prompt: ${prompt})`);
134
+ }
135
+ return new Promise((resolve) => {
136
+ const input = process.stdin;
137
+ const output = process.stderr;
138
+ const previousRawMode = input.isRaw;
139
+ let value = '';
140
+ let finished = false;
141
+ function onData(chunk) {
142
+ for (const char of chunk) {
143
+ if (char === '\r' || char === '\n') {
144
+ finish((0, ts_utils_1.succeed)(value));
145
+ return;
146
+ }
147
+ if (char === '\u0003') {
148
+ finish((0, ts_utils_1.fail)('Prompt cancelled'));
149
+ return;
150
+ }
151
+ if (char === '\u007f' || char === '\b') {
152
+ value = value.slice(0, -1);
153
+ continue;
154
+ }
155
+ value += char;
156
+ }
157
+ }
158
+ function finish(result) {
159
+ if (finished) {
160
+ return;
161
+ }
162
+ finished = true;
163
+ input.off('data', onData);
164
+ if (input.isTTY && input.isRaw && !previousRawMode) {
165
+ input.setRawMode(false);
166
+ }
167
+ input.pause();
168
+ output.write('\n');
169
+ resolve(result);
170
+ }
171
+ output.write(prompt);
172
+ input.setRawMode(true);
173
+ input.setEncoding('utf8');
174
+ input.resume();
175
+ input.on('data', onData);
176
+ });
177
+ }
178
+ async function promptVisible(prompt) {
179
+ if (!process.stdin.isTTY) {
180
+ return (0, ts_utils_1.fail)(`Interactive prompt requires a TTY (prompt: ${prompt})`);
181
+ }
182
+ return new Promise((resolve) => {
183
+ const input = process.stdin;
184
+ const output = process.stderr;
185
+ const interfaceInstance = readline.createInterface({ input, output, terminal: true });
186
+ let finished = false;
187
+ function finish(result) {
188
+ if (finished) {
189
+ return;
190
+ }
191
+ finished = true;
192
+ interfaceInstance.close();
193
+ output.write('\n');
194
+ resolve(result);
195
+ }
196
+ interfaceInstance.on('SIGINT', () => {
197
+ finish((0, ts_utils_1.fail)('Prompt cancelled'));
198
+ });
199
+ interfaceInstance.question(prompt, (answer) => {
200
+ finish((0, ts_utils_1.succeed)(answer));
201
+ });
202
+ });
203
+ }
204
+ async function copyTextToClipboard(text) {
205
+ const result = await (0, ts_utils_1.captureAsyncResult)(async () => {
206
+ await clipboardy_1.default.write(text);
207
+ return 'copied';
208
+ });
209
+ return result.withErrorFormat((message) => `Failed to copy to clipboard: ${message}`);
210
+ }
211
+ //# sourceMappingURL=io.js.map
package/lib/io.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"io.js","sourceRoot":"","sources":["../src/io.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'fs';\nimport os from 'os';\nimport path from 'path';\nimport * as readline from 'readline';\n\nimport clipboardy from 'clipboardy';\nimport { FileTree, FileTree as FileTreeNamespace } from '@fgv/ts-json-base';\nimport { Result, captureAsyncResult, captureResult, fail, succeed } from '@fgv/ts-utils';\n\nfunction expandHome(filePath: string): string {\n if (!filePath.startsWith('~')) {\n return filePath;\n }\n\n const home = os.homedir();\n if (filePath === '~') {\n return home;\n }\n\n if (filePath.startsWith('~/')) {\n return path.join(home, filePath.substring(2));\n }\n\n return filePath;\n}\n\nexport function resolvePath(filePath: string): string {\n return path.resolve(expandHome(filePath));\n}\n\nexport function defaultKeystorePath(): string {\n return path.join(os.homedir(), '.fgv-ks');\n}\n\nexport function readTextFile(filePath: string): Result<string> {\n const resolvedPath = resolvePath(filePath);\n return FileTree.forFilesystem().onSuccess((tree) =>\n tree.getFile(resolvedPath).onSuccess((file) => file.getRawContents())\n );\n}\n\nexport function writeTextFile(filePath: string, contents: string): Result<string> {\n const resolvedPath = resolvePath(filePath);\n const directoryPath = path.dirname(resolvedPath);\n const fileName = path.basename(resolvedPath);\n\n return FileTree.forFilesystem({ mutable: true }).onSuccess((tree) => {\n const accessors = tree.hal;\n if (!FileTreeNamespace.isMutableAccessors(accessors)) {\n return fail(`Unable to write '${resolvedPath}': filesystem access is read-only`);\n }\n\n return accessors.createDirectory(directoryPath).onSuccess(() =>\n tree.getDirectory(directoryPath).onSuccess((directory) => {\n if (!FileTreeNamespace.isMutableDirectoryItem(directory)) {\n return fail(`Unable to write '${resolvedPath}': directory is not mutable`);\n }\n\n const tmpFileName = `${fileName}.tmp`;\n return directory.createChildFile(tmpFileName, contents).onSuccess(() =>\n captureResult(() => {\n fs.renameSync(path.join(directoryPath, tmpFileName), resolvedPath);\n return resolvedPath;\n })\n );\n })\n );\n });\n}\n\nexport async function readAllFromStdin(): Promise<Result<string>> {\n const result = await captureAsyncResult(async () => {\n const chunks: string[] = [];\n const input = process.stdin;\n input.setEncoding('utf8');\n input.resume();\n\n for await (const chunk of input) {\n chunks.push(String(chunk));\n }\n\n return chunks.join('');\n });\n\n return result.withErrorFormat((message: string) => `Failed to read from stdin: ${message}`);\n}\n\nexport async function promptHidden(prompt: string): Promise<Result<string>> {\n if (!process.stdin.isTTY) {\n return fail(`Interactive prompt requires a TTY (prompt: ${prompt})`);\n }\n\n return new Promise<Result<string>>((resolve) => {\n const input = process.stdin;\n const output = process.stderr;\n const previousRawMode = input.isRaw;\n let value = '';\n let finished = false;\n\n function onData(chunk: string): void {\n for (const char of chunk) {\n if (char === '\\r' || char === '\\n') {\n finish(succeed(value));\n return;\n }\n if (char === '\\u0003') {\n finish(fail('Prompt cancelled'));\n return;\n }\n if (char === '\\u007f' || char === '\\b') {\n value = value.slice(0, -1);\n continue;\n }\n value += char;\n }\n }\n\n function finish(result: Result<string>): void {\n if (finished) {\n return;\n }\n finished = true;\n input.off('data', onData);\n if (input.isTTY && input.isRaw && !previousRawMode) {\n input.setRawMode(false);\n }\n input.pause();\n output.write('\\n');\n resolve(result);\n }\n\n output.write(prompt);\n input.setRawMode(true);\n input.setEncoding('utf8');\n input.resume();\n input.on('data', onData);\n });\n}\n\nexport async function promptVisible(prompt: string): Promise<Result<string>> {\n if (!process.stdin.isTTY) {\n return fail(`Interactive prompt requires a TTY (prompt: ${prompt})`);\n }\n\n return new Promise<Result<string>>((resolve) => {\n const input = process.stdin;\n const output = process.stderr;\n const interfaceInstance = readline.createInterface({ input, output, terminal: true });\n let finished = false;\n\n function finish(result: Result<string>): void {\n if (finished) {\n return;\n }\n\n finished = true;\n interfaceInstance.close();\n output.write('\\n');\n resolve(result);\n }\n\n interfaceInstance.on('SIGINT', () => {\n finish(fail('Prompt cancelled'));\n });\n\n interfaceInstance.question(prompt, (answer: string) => {\n finish(succeed(answer));\n });\n });\n}\n\nexport async function copyTextToClipboard(text: string): Promise<Result<'copied'>> {\n const result = await captureAsyncResult(async () => {\n await clipboardy.write(text);\n return 'copied' as const;\n });\n\n return result.withErrorFormat((message: string) => `Failed to copy to clipboard: ${message}`);\n}\n"]}
@@ -0,0 +1,21 @@
1
+ import { CryptoUtils } from '@fgv/ts-extras';
2
+ import { Result } from '@fgv/ts-utils';
3
+ export interface IKeystoreOpenResult {
4
+ readonly path: string;
5
+ readonly keystore: CryptoUtils.KeyStore.KeyStore;
6
+ }
7
+ export interface ISecretWriteOptions {
8
+ readonly description?: string;
9
+ readonly replace?: boolean;
10
+ }
11
+ export declare function resolveKeystorePath(filePath?: string): string;
12
+ export declare function loadKeystoreFile(resolvedPath: string): Result<CryptoUtils.KeyStore.IKeyStoreFile>;
13
+ export declare function saveKeystoreFile(filePath: string | undefined, keystoreFile: CryptoUtils.KeyStore.IKeyStoreFile): Result<string>;
14
+ export declare function openKeystore(filePath: string | undefined, password: string): Promise<Result<IKeystoreOpenResult>>;
15
+ export declare function createKeystore(filePath: string | undefined, password: string): Promise<Result<IKeystoreOpenResult>>;
16
+ export declare function changeKeystorePassword(filePath: string | undefined, currentPassword: string, nextPassword: string): Promise<Result<IKeystoreOpenResult>>;
17
+ export declare function storeSecret(filePath: string | undefined, password: string, name: string, value: string, options?: ISecretWriteOptions): Promise<Result<string>>;
18
+ export declare function readSecret(filePath: string | undefined, password: string, name: string): Promise<Result<string>>;
19
+ export declare function listSecrets(filePath: string | undefined, password: string): Promise<Result<readonly string[]>>;
20
+ export declare function removeSecret(filePath: string | undefined, password: string, name: string): Promise<Result<string>>;
21
+ //# sourceMappingURL=keystore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keystore.d.ts","sourceRoot":"","sources":["../src/keystore.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAgC,MAAM,eAAe,CAAC;AAIrE,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC,QAAQ,CAAC,QAAQ,CAAC;CAClD;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,aAAa,CAAC,CAYjG;AAED,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,YAAY,EAAE,WAAW,CAAC,QAAQ,CAAC,aAAa,GAC/C,MAAM,CAAC,MAAM,CAAC,CAIhB;AAED,wBAAsB,YAAY,CAChC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC,CAWtC;AAED,wBAAsB,cAAc,CAClC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC,CAkCtC;AAED,wBAAsB,sBAAsB,CAC1C,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,eAAe,EAAE,MAAM,EACvB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC,CAsBtC;AAED,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,mBAAmB,GAC5B,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAsBzB;AAED,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAEzB;AAED,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,CAAC,SAAS,MAAM,EAAE,CAAC,CAAC,CAEpC;AAED,wBAAsB,YAAY,CAChC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAmBzB"}
@@ -0,0 +1,133 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.resolveKeystorePath = resolveKeystorePath;
7
+ exports.loadKeystoreFile = loadKeystoreFile;
8
+ exports.saveKeystoreFile = saveKeystoreFile;
9
+ exports.openKeystore = openKeystore;
10
+ exports.createKeystore = createKeystore;
11
+ exports.changeKeystorePassword = changeKeystorePassword;
12
+ exports.storeSecret = storeSecret;
13
+ exports.readSecret = readSecret;
14
+ exports.listSecrets = listSecrets;
15
+ exports.removeSecret = removeSecret;
16
+ const fs_1 = __importDefault(require("fs"));
17
+ const ts_extras_1 = require("@fgv/ts-extras");
18
+ const ts_utils_1 = require("@fgv/ts-utils");
19
+ const io_1 = require("./io");
20
+ function resolveKeystorePath(filePath) {
21
+ return (0, io_1.resolvePath)(filePath !== null && filePath !== void 0 ? filePath : (0, io_1.defaultKeystorePath)());
22
+ }
23
+ function loadKeystoreFile(resolvedPath) {
24
+ return (0, io_1.readTextFile)(resolvedPath)
25
+ .onSuccess((contents) => (0, ts_utils_1.captureResult)(() => JSON.parse(contents)).withErrorFormat((msg) => `Invalid keystore file '${resolvedPath}': ${msg}`))
26
+ .onSuccess((json) => ts_extras_1.CryptoUtils.KeyStore.Converters.keystoreFile
27
+ .convert(json)
28
+ .withErrorFormat((msg) => `Invalid keystore file '${resolvedPath}': ${msg}`));
29
+ }
30
+ function saveKeystoreFile(filePath, keystoreFile) {
31
+ const resolvedPath = resolveKeystorePath(filePath);
32
+ const serialized = `${JSON.stringify(keystoreFile, null, 2)}\n`;
33
+ return (0, io_1.writeTextFile)(resolvedPath, serialized);
34
+ }
35
+ async function openKeystore(filePath, password) {
36
+ const resolvedPath = resolveKeystorePath(filePath);
37
+ return loadKeystoreFile(resolvedPath)
38
+ .onSuccess((file) => ts_extras_1.CryptoUtils.KeyStore.KeyStore.open({
39
+ cryptoProvider: ts_extras_1.CryptoUtils.nodeCryptoProvider,
40
+ keystoreFile: file
41
+ }))
42
+ .thenOnSuccess((store) => store.unlock(password))
43
+ .onSuccess((keystore) => (0, ts_utils_1.succeed)({ path: resolvedPath, keystore }));
44
+ }
45
+ async function createKeystore(filePath, password) {
46
+ const resolvedPath = resolveKeystorePath(filePath);
47
+ if (fs_1.default.existsSync(resolvedPath)) {
48
+ const readable = (0, io_1.readTextFile)(resolvedPath);
49
+ if (readable.isFailure()) {
50
+ return (0, ts_utils_1.fail)(`Keystore file at '${resolvedPath}' exists but cannot be read: ${readable.message}`);
51
+ }
52
+ return (0, ts_utils_1.fail)(`Keystore already exists at '${resolvedPath}'`);
53
+ }
54
+ const created = ts_extras_1.CryptoUtils.KeyStore.KeyStore.create({
55
+ cryptoProvider: ts_extras_1.CryptoUtils.nodeCryptoProvider
56
+ });
57
+ if (created.isFailure()) {
58
+ return (0, ts_utils_1.fail)(`Failed to create keystore: ${created.message}`);
59
+ }
60
+ const keystore = created.value;
61
+ const initialized = await keystore.initialize(password);
62
+ if (initialized.isFailure()) {
63
+ return (0, ts_utils_1.fail)(`Failed to initialize keystore: ${initialized.message}`);
64
+ }
65
+ const saved = await keystore.save(password);
66
+ if (saved.isFailure()) {
67
+ return (0, ts_utils_1.fail)(`Failed to save keystore: ${saved.message}`);
68
+ }
69
+ const savedFile = saveKeystoreFile(resolvedPath, saved.value);
70
+ if (savedFile.isFailure()) {
71
+ return (0, ts_utils_1.fail)(`Failed to write keystore file: ${savedFile.message}`);
72
+ }
73
+ return (0, ts_utils_1.succeed)({ path: resolvedPath, keystore });
74
+ }
75
+ async function changeKeystorePassword(filePath, currentPassword, nextPassword) {
76
+ const opened = await openKeystore(filePath, currentPassword);
77
+ if (opened.isFailure()) {
78
+ return (0, ts_utils_1.fail)(opened.message);
79
+ }
80
+ const changed = await opened.value.keystore.changePassword(currentPassword, nextPassword);
81
+ if (changed.isFailure()) {
82
+ return (0, ts_utils_1.fail)(`Failed to change password: ${changed.message}`);
83
+ }
84
+ const saved = await opened.value.keystore.save(nextPassword);
85
+ if (saved.isFailure()) {
86
+ return (0, ts_utils_1.fail)(`Failed to save keystore: ${saved.message}`);
87
+ }
88
+ const persisted = saveKeystoreFile(opened.value.path, saved.value);
89
+ if (persisted.isFailure()) {
90
+ return (0, ts_utils_1.fail)(`Failed to write keystore file: ${persisted.message}`);
91
+ }
92
+ return (0, ts_utils_1.succeed)(opened.value);
93
+ }
94
+ async function storeSecret(filePath, password, name, value, options) {
95
+ const opened = await openKeystore(filePath, password);
96
+ if (opened.isFailure()) {
97
+ return (0, ts_utils_1.fail)(opened.message);
98
+ }
99
+ const writeResult = await opened.value.keystore.importApiKey(name, value, {
100
+ description: options === null || options === void 0 ? void 0 : options.description,
101
+ replace: options === null || options === void 0 ? void 0 : options.replace
102
+ });
103
+ if (writeResult.isFailure()) {
104
+ return (0, ts_utils_1.fail)(`Failed to store secret '${name}': ${writeResult.message}`);
105
+ }
106
+ const saved = await opened.value.keystore.save(password);
107
+ if (saved.isFailure()) {
108
+ return (0, ts_utils_1.fail)(`Failed to save keystore: ${saved.message}`);
109
+ }
110
+ return saveKeystoreFile(opened.value.path, saved.value).withErrorFormat((msg) => `Failed to write keystore file: ${msg}`);
111
+ }
112
+ async function readSecret(filePath, password, name) {
113
+ return (await openKeystore(filePath, password)).onSuccess((opened) => opened.keystore.getApiKey(name));
114
+ }
115
+ async function listSecrets(filePath, password) {
116
+ return (await openKeystore(filePath, password)).onSuccess((opened) => opened.keystore.listSecrets());
117
+ }
118
+ async function removeSecret(filePath, password, name) {
119
+ const opened = await openKeystore(filePath, password);
120
+ if (opened.isFailure()) {
121
+ return (0, ts_utils_1.fail)(opened.message);
122
+ }
123
+ const removed = await opened.value.keystore.removeSecret(name);
124
+ if (removed.isFailure()) {
125
+ return (0, ts_utils_1.fail)(`Failed to remove secret '${name}': ${removed.message}`);
126
+ }
127
+ const saved = await opened.value.keystore.save(password);
128
+ if (saved.isFailure()) {
129
+ return (0, ts_utils_1.fail)(`Failed to save keystore: ${saved.message}`);
130
+ }
131
+ return saveKeystoreFile(opened.value.path, saved.value).withErrorFormat((msg) => `Failed to write keystore file: ${msg}`);
132
+ }
133
+ //# sourceMappingURL=keystore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keystore.js","sourceRoot":"","sources":["../src/keystore.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'fs';\n\nimport { CryptoUtils } from '@fgv/ts-extras';\nimport { Result, captureResult, fail, succeed } from '@fgv/ts-utils';\n\nimport { defaultKeystorePath, readTextFile, resolvePath, writeTextFile } from './io';\n\nexport interface IKeystoreOpenResult {\n readonly path: string;\n readonly keystore: CryptoUtils.KeyStore.KeyStore;\n}\n\nexport interface ISecretWriteOptions {\n readonly description?: string;\n readonly replace?: boolean;\n}\n\nexport function resolveKeystorePath(filePath?: string): string {\n return resolvePath(filePath ?? defaultKeystorePath());\n}\n\nexport function loadKeystoreFile(resolvedPath: string): Result<CryptoUtils.KeyStore.IKeyStoreFile> {\n return readTextFile(resolvedPath)\n .onSuccess((contents) =>\n captureResult(() => JSON.parse(contents)).withErrorFormat(\n (msg) => `Invalid keystore file '${resolvedPath}': ${msg}`\n )\n )\n .onSuccess((json) =>\n CryptoUtils.KeyStore.Converters.keystoreFile\n .convert(json)\n .withErrorFormat((msg) => `Invalid keystore file '${resolvedPath}': ${msg}`)\n );\n}\n\nexport function saveKeystoreFile(\n filePath: string | undefined,\n keystoreFile: CryptoUtils.KeyStore.IKeyStoreFile\n): Result<string> {\n const resolvedPath = resolveKeystorePath(filePath);\n const serialized = `${JSON.stringify(keystoreFile, null, 2)}\\n`;\n return writeTextFile(resolvedPath, serialized);\n}\n\nexport async function openKeystore(\n filePath: string | undefined,\n password: string\n): Promise<Result<IKeystoreOpenResult>> {\n const resolvedPath = resolveKeystorePath(filePath);\n return loadKeystoreFile(resolvedPath)\n .onSuccess((file) =>\n CryptoUtils.KeyStore.KeyStore.open({\n cryptoProvider: CryptoUtils.nodeCryptoProvider,\n keystoreFile: file\n })\n )\n .thenOnSuccess((store) => store.unlock(password))\n .onSuccess((keystore) => succeed({ path: resolvedPath, keystore }));\n}\n\nexport async function createKeystore(\n filePath: string | undefined,\n password: string\n): Promise<Result<IKeystoreOpenResult>> {\n const resolvedPath = resolveKeystorePath(filePath);\n if (fs.existsSync(resolvedPath)) {\n const readable = readTextFile(resolvedPath);\n if (readable.isFailure()) {\n return fail(`Keystore file at '${resolvedPath}' exists but cannot be read: ${readable.message}`);\n }\n return fail(`Keystore already exists at '${resolvedPath}'`);\n }\n\n const created = CryptoUtils.KeyStore.KeyStore.create({\n cryptoProvider: CryptoUtils.nodeCryptoProvider\n });\n if (created.isFailure()) {\n return fail(`Failed to create keystore: ${created.message}`);\n }\n\n const keystore = created.value;\n const initialized = await keystore.initialize(password);\n if (initialized.isFailure()) {\n return fail(`Failed to initialize keystore: ${initialized.message}`);\n }\n\n const saved = await keystore.save(password);\n if (saved.isFailure()) {\n return fail(`Failed to save keystore: ${saved.message}`);\n }\n\n const savedFile = saveKeystoreFile(resolvedPath, saved.value);\n if (savedFile.isFailure()) {\n return fail(`Failed to write keystore file: ${savedFile.message}`);\n }\n\n return succeed({ path: resolvedPath, keystore });\n}\n\nexport async function changeKeystorePassword(\n filePath: string | undefined,\n currentPassword: string,\n nextPassword: string\n): Promise<Result<IKeystoreOpenResult>> {\n const opened = await openKeystore(filePath, currentPassword);\n if (opened.isFailure()) {\n return fail(opened.message);\n }\n\n const changed = await opened.value.keystore.changePassword(currentPassword, nextPassword);\n if (changed.isFailure()) {\n return fail(`Failed to change password: ${changed.message}`);\n }\n\n const saved = await opened.value.keystore.save(nextPassword);\n if (saved.isFailure()) {\n return fail(`Failed to save keystore: ${saved.message}`);\n }\n\n const persisted = saveKeystoreFile(opened.value.path, saved.value);\n if (persisted.isFailure()) {\n return fail(`Failed to write keystore file: ${persisted.message}`);\n }\n\n return succeed(opened.value);\n}\n\nexport async function storeSecret(\n filePath: string | undefined,\n password: string,\n name: string,\n value: string,\n options?: ISecretWriteOptions\n): Promise<Result<string>> {\n const opened = await openKeystore(filePath, password);\n if (opened.isFailure()) {\n return fail(opened.message);\n }\n\n const writeResult = await opened.value.keystore.importApiKey(name, value, {\n description: options?.description,\n replace: options?.replace\n });\n if (writeResult.isFailure()) {\n return fail(`Failed to store secret '${name}': ${writeResult.message}`);\n }\n\n const saved = await opened.value.keystore.save(password);\n if (saved.isFailure()) {\n return fail(`Failed to save keystore: ${saved.message}`);\n }\n\n return saveKeystoreFile(opened.value.path, saved.value).withErrorFormat(\n (msg) => `Failed to write keystore file: ${msg}`\n );\n}\n\nexport async function readSecret(\n filePath: string | undefined,\n password: string,\n name: string\n): Promise<Result<string>> {\n return (await openKeystore(filePath, password)).onSuccess((opened) => opened.keystore.getApiKey(name));\n}\n\nexport async function listSecrets(\n filePath: string | undefined,\n password: string\n): Promise<Result<readonly string[]>> {\n return (await openKeystore(filePath, password)).onSuccess((opened) => opened.keystore.listSecrets());\n}\n\nexport async function removeSecret(\n filePath: string | undefined,\n password: string,\n name: string\n): Promise<Result<string>> {\n const opened = await openKeystore(filePath, password);\n if (opened.isFailure()) {\n return fail(opened.message);\n }\n\n const removed = await opened.value.keystore.removeSecret(name);\n if (removed.isFailure()) {\n return fail(`Failed to remove secret '${name}': ${removed.message}`);\n }\n\n const saved = await opened.value.keystore.save(password);\n if (saved.isFailure()) {\n return fail(`Failed to save keystore: ${saved.message}`);\n }\n\n return saveKeystoreFile(opened.value.path, saved.value).withErrorFormat(\n (msg) => `Failed to write keystore file: ${msg}`\n );\n}\n"]}
@@ -0,0 +1,5 @@
1
+ import { Result } from '@fgv/ts-utils';
2
+ export declare function extractTemplateVariables(template: string): Result<readonly string[]>;
3
+ export declare function renderShellTemplate(template: string, context: Readonly<Record<string, string>>): Result<string>;
4
+ export declare function shellQuote(value: string): string;
5
+ //# sourceMappingURL=template.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../src/template.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAiB,MAAM,eAAe,CAAC;AAkBtD,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,SAAS,MAAM,EAAE,CAAC,CAapF;AAED,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GACxC,MAAM,CAAC,MAAM,CAAC,CAmBhB;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhD"}