@lifestreamdynamics/vault-cli 1.0.0
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/LICENSE +21 -0
- package/README.md +759 -0
- package/dist/client.d.ts +12 -0
- package/dist/client.js +79 -0
- package/dist/commands/admin.d.ts +2 -0
- package/dist/commands/admin.js +263 -0
- package/dist/commands/audit.d.ts +2 -0
- package/dist/commands/audit.js +119 -0
- package/dist/commands/auth.d.ts +2 -0
- package/dist/commands/auth.js +256 -0
- package/dist/commands/config.d.ts +2 -0
- package/dist/commands/config.js +130 -0
- package/dist/commands/connectors.d.ts +2 -0
- package/dist/commands/connectors.js +224 -0
- package/dist/commands/docs.d.ts +2 -0
- package/dist/commands/docs.js +194 -0
- package/dist/commands/hooks.d.ts +2 -0
- package/dist/commands/hooks.js +159 -0
- package/dist/commands/keys.d.ts +2 -0
- package/dist/commands/keys.js +165 -0
- package/dist/commands/publish.d.ts +2 -0
- package/dist/commands/publish.js +138 -0
- package/dist/commands/search.d.ts +2 -0
- package/dist/commands/search.js +61 -0
- package/dist/commands/shares.d.ts +2 -0
- package/dist/commands/shares.js +121 -0
- package/dist/commands/subscription.d.ts +2 -0
- package/dist/commands/subscription.js +166 -0
- package/dist/commands/sync.d.ts +2 -0
- package/dist/commands/sync.js +565 -0
- package/dist/commands/teams.d.ts +2 -0
- package/dist/commands/teams.js +322 -0
- package/dist/commands/user.d.ts +2 -0
- package/dist/commands/user.js +48 -0
- package/dist/commands/vaults.d.ts +2 -0
- package/dist/commands/vaults.js +157 -0
- package/dist/commands/versions.d.ts +2 -0
- package/dist/commands/versions.js +219 -0
- package/dist/commands/webhooks.d.ts +2 -0
- package/dist/commands/webhooks.js +181 -0
- package/dist/config.d.ts +24 -0
- package/dist/config.js +88 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +63 -0
- package/dist/lib/credential-manager.d.ts +48 -0
- package/dist/lib/credential-manager.js +101 -0
- package/dist/lib/encrypted-config.d.ts +20 -0
- package/dist/lib/encrypted-config.js +102 -0
- package/dist/lib/keychain.d.ts +8 -0
- package/dist/lib/keychain.js +82 -0
- package/dist/lib/migration.d.ts +31 -0
- package/dist/lib/migration.js +92 -0
- package/dist/lib/profiles.d.ts +43 -0
- package/dist/lib/profiles.js +104 -0
- package/dist/sync/config.d.ts +32 -0
- package/dist/sync/config.js +100 -0
- package/dist/sync/conflict.d.ts +30 -0
- package/dist/sync/conflict.js +60 -0
- package/dist/sync/daemon-worker.d.ts +1 -0
- package/dist/sync/daemon-worker.js +128 -0
- package/dist/sync/daemon.d.ts +44 -0
- package/dist/sync/daemon.js +174 -0
- package/dist/sync/diff.d.ts +43 -0
- package/dist/sync/diff.js +166 -0
- package/dist/sync/engine.d.ts +41 -0
- package/dist/sync/engine.js +233 -0
- package/dist/sync/ignore.d.ts +16 -0
- package/dist/sync/ignore.js +72 -0
- package/dist/sync/remote-poller.d.ts +23 -0
- package/dist/sync/remote-poller.js +145 -0
- package/dist/sync/state.d.ts +32 -0
- package/dist/sync/state.js +98 -0
- package/dist/sync/types.d.ts +68 -0
- package/dist/sync/types.js +4 -0
- package/dist/sync/watcher.d.ts +23 -0
- package/dist/sync/watcher.js +207 -0
- package/dist/utils/flags.d.ts +18 -0
- package/dist/utils/flags.js +31 -0
- package/dist/utils/format.d.ts +2 -0
- package/dist/utils/format.js +22 -0
- package/dist/utils/output.d.ts +87 -0
- package/dist/utils/output.js +229 -0
- package/package.json +62 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { GlobalFlags } from './flags.js';
|
|
2
|
+
/**
|
|
3
|
+
* Column definition for table output.
|
|
4
|
+
*/
|
|
5
|
+
export interface TableColumn {
|
|
6
|
+
key: string;
|
|
7
|
+
header: string;
|
|
8
|
+
width?: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Output helper that centralizes formatting for text, json, and table modes.
|
|
12
|
+
* Status messages go to stderr so stdout stays clean for piping.
|
|
13
|
+
*/
|
|
14
|
+
export declare class Output {
|
|
15
|
+
private flags;
|
|
16
|
+
private spinner;
|
|
17
|
+
constructor(flags: GlobalFlags);
|
|
18
|
+
/**
|
|
19
|
+
* Start a spinner (only shown in text mode, non-quiet, TTY).
|
|
20
|
+
*/
|
|
21
|
+
startSpinner(message: string): void;
|
|
22
|
+
/**
|
|
23
|
+
* Stop the spinner without a status symbol.
|
|
24
|
+
*/
|
|
25
|
+
stopSpinner(): void;
|
|
26
|
+
/**
|
|
27
|
+
* Stop the spinner with a success message.
|
|
28
|
+
*/
|
|
29
|
+
succeedSpinner(message: string): void;
|
|
30
|
+
/**
|
|
31
|
+
* Stop the spinner with a failure message.
|
|
32
|
+
*/
|
|
33
|
+
failSpinner(message: string): void;
|
|
34
|
+
/**
|
|
35
|
+
* Print a status/info message to stderr (never captured by piping).
|
|
36
|
+
*/
|
|
37
|
+
status(message: string): void;
|
|
38
|
+
/**
|
|
39
|
+
* Print a verbose/debug message to stderr.
|
|
40
|
+
*/
|
|
41
|
+
debug(message: string): void;
|
|
42
|
+
/**
|
|
43
|
+
* Print an error message to stderr.
|
|
44
|
+
*/
|
|
45
|
+
error(message: string): void;
|
|
46
|
+
/**
|
|
47
|
+
* Print a warning message to stderr.
|
|
48
|
+
*/
|
|
49
|
+
warn(message: string): void;
|
|
50
|
+
/**
|
|
51
|
+
* Output a single record based on the format.
|
|
52
|
+
* - text: prints key-value lines
|
|
53
|
+
* - json: prints a single JSON object
|
|
54
|
+
* - table: prints a single-row table
|
|
55
|
+
*/
|
|
56
|
+
record(data: Record<string, unknown>, columns?: TableColumn[]): void;
|
|
57
|
+
/**
|
|
58
|
+
* Output a list of records based on the format.
|
|
59
|
+
* - text: prints each item using textFn, or key-value pairs
|
|
60
|
+
* - json: prints one JSON object per line (JSON Lines)
|
|
61
|
+
* - table: prints an ASCII table
|
|
62
|
+
*/
|
|
63
|
+
list(data: Record<string, unknown>[], options?: {
|
|
64
|
+
columns?: TableColumn[];
|
|
65
|
+
textFn?: (item: Record<string, unknown>) => string;
|
|
66
|
+
emptyMessage?: string;
|
|
67
|
+
}): void;
|
|
68
|
+
/**
|
|
69
|
+
* Output raw content to stdout (for piping document content, etc.).
|
|
70
|
+
*/
|
|
71
|
+
raw(content: string): void;
|
|
72
|
+
/**
|
|
73
|
+
* Print a success result (used for create/update/delete confirmations).
|
|
74
|
+
*/
|
|
75
|
+
success(message: string, data?: Record<string, unknown>): void;
|
|
76
|
+
private printKeyValue;
|
|
77
|
+
private table;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Create an Output instance from global flags.
|
|
81
|
+
*/
|
|
82
|
+
export declare function createOutput(flags: GlobalFlags): Output;
|
|
83
|
+
/**
|
|
84
|
+
* Standard error handler for commands.
|
|
85
|
+
* Prints error to stderr and sets exit code.
|
|
86
|
+
*/
|
|
87
|
+
export declare function handleError(out: Output, err: unknown, spinnerMessage?: string): void;
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
/**
|
|
4
|
+
* Output helper that centralizes formatting for text, json, and table modes.
|
|
5
|
+
* Status messages go to stderr so stdout stays clean for piping.
|
|
6
|
+
*/
|
|
7
|
+
export class Output {
|
|
8
|
+
flags;
|
|
9
|
+
spinner = null;
|
|
10
|
+
constructor(flags) {
|
|
11
|
+
this.flags = flags;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Start a spinner (only shown in text mode, non-quiet, TTY).
|
|
15
|
+
*/
|
|
16
|
+
startSpinner(message) {
|
|
17
|
+
if (this.flags.output === 'text' && !this.flags.quiet && process.stderr.isTTY) {
|
|
18
|
+
this.spinner = ora({ text: message, stream: process.stderr }).start();
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Stop the spinner without a status symbol.
|
|
23
|
+
*/
|
|
24
|
+
stopSpinner() {
|
|
25
|
+
this.spinner?.stop();
|
|
26
|
+
this.spinner = null;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Stop the spinner with a success message.
|
|
30
|
+
*/
|
|
31
|
+
succeedSpinner(message) {
|
|
32
|
+
if (this.spinner) {
|
|
33
|
+
this.spinner.succeed(message);
|
|
34
|
+
this.spinner = null;
|
|
35
|
+
}
|
|
36
|
+
else if (this.flags.output === 'text' && !this.flags.quiet) {
|
|
37
|
+
process.stderr.write(chalk.green('✓') + ' ' + message + '\n');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Stop the spinner with a failure message.
|
|
42
|
+
*/
|
|
43
|
+
failSpinner(message) {
|
|
44
|
+
if (this.spinner) {
|
|
45
|
+
this.spinner.fail(message);
|
|
46
|
+
this.spinner = null;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
process.stderr.write(chalk.red('✖') + ' ' + message + '\n');
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Print a status/info message to stderr (never captured by piping).
|
|
54
|
+
*/
|
|
55
|
+
status(message) {
|
|
56
|
+
if (!this.flags.quiet) {
|
|
57
|
+
process.stderr.write(message + '\n');
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Print a verbose/debug message to stderr.
|
|
62
|
+
*/
|
|
63
|
+
debug(message) {
|
|
64
|
+
if (this.flags.verbose) {
|
|
65
|
+
process.stderr.write(chalk.dim('[debug] ' + message) + '\n');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Print an error message to stderr.
|
|
70
|
+
*/
|
|
71
|
+
error(message) {
|
|
72
|
+
process.stderr.write(chalk.red(message) + '\n');
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Print a warning message to stderr.
|
|
76
|
+
*/
|
|
77
|
+
warn(message) {
|
|
78
|
+
if (!this.flags.quiet) {
|
|
79
|
+
process.stderr.write(chalk.yellow(message) + '\n');
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Output a single record based on the format.
|
|
84
|
+
* - text: prints key-value lines
|
|
85
|
+
* - json: prints a single JSON object
|
|
86
|
+
* - table: prints a single-row table
|
|
87
|
+
*/
|
|
88
|
+
record(data, columns) {
|
|
89
|
+
switch (this.flags.output) {
|
|
90
|
+
case 'json':
|
|
91
|
+
process.stdout.write(JSON.stringify(data) + '\n');
|
|
92
|
+
break;
|
|
93
|
+
case 'table':
|
|
94
|
+
this.table([data], columns);
|
|
95
|
+
break;
|
|
96
|
+
case 'text':
|
|
97
|
+
default:
|
|
98
|
+
this.printKeyValue(data);
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Output a list of records based on the format.
|
|
104
|
+
* - text: prints each item using textFn, or key-value pairs
|
|
105
|
+
* - json: prints one JSON object per line (JSON Lines)
|
|
106
|
+
* - table: prints an ASCII table
|
|
107
|
+
*/
|
|
108
|
+
list(data, options) {
|
|
109
|
+
if (data.length === 0) {
|
|
110
|
+
if (this.flags.output === 'json') {
|
|
111
|
+
// empty json array: no output
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
if (options?.emptyMessage && !this.flags.quiet) {
|
|
115
|
+
this.status(options.emptyMessage);
|
|
116
|
+
}
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
switch (this.flags.output) {
|
|
120
|
+
case 'json':
|
|
121
|
+
for (const item of data) {
|
|
122
|
+
process.stdout.write(JSON.stringify(item) + '\n');
|
|
123
|
+
}
|
|
124
|
+
break;
|
|
125
|
+
case 'table':
|
|
126
|
+
this.table(data, options?.columns);
|
|
127
|
+
break;
|
|
128
|
+
case 'text':
|
|
129
|
+
default:
|
|
130
|
+
if (options?.textFn) {
|
|
131
|
+
for (const item of data) {
|
|
132
|
+
process.stdout.write(options.textFn(item) + '\n');
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
for (const item of data) {
|
|
137
|
+
this.printKeyValue(item);
|
|
138
|
+
process.stdout.write('\n');
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Output raw content to stdout (for piping document content, etc.).
|
|
146
|
+
*/
|
|
147
|
+
raw(content) {
|
|
148
|
+
process.stdout.write(content);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Print a success result (used for create/update/delete confirmations).
|
|
152
|
+
*/
|
|
153
|
+
success(message, data) {
|
|
154
|
+
if (this.flags.output === 'json' && data) {
|
|
155
|
+
process.stdout.write(JSON.stringify(data) + '\n');
|
|
156
|
+
}
|
|
157
|
+
else if (!this.flags.quiet) {
|
|
158
|
+
this.succeedSpinner(message);
|
|
159
|
+
if (data && this.flags.output === 'text') {
|
|
160
|
+
this.printKeyValue(data);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
printKeyValue(data) {
|
|
165
|
+
const maxKeyLen = Math.max(...Object.keys(data).map(k => k.length));
|
|
166
|
+
for (const [key, value] of Object.entries(data)) {
|
|
167
|
+
const label = key.charAt(0).toUpperCase() + key.slice(1);
|
|
168
|
+
const padding = ' '.repeat(Math.max(0, maxKeyLen - key.length + 1));
|
|
169
|
+
const displayValue = value === null || value === undefined
|
|
170
|
+
? chalk.dim('none')
|
|
171
|
+
: String(value);
|
|
172
|
+
process.stdout.write(`${label}:${padding}${displayValue}\n`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
table(data, columns) {
|
|
176
|
+
if (data.length === 0)
|
|
177
|
+
return;
|
|
178
|
+
const cols = columns ?? Object.keys(data[0]).map(key => ({
|
|
179
|
+
key,
|
|
180
|
+
header: key.charAt(0).toUpperCase() + key.slice(1),
|
|
181
|
+
}));
|
|
182
|
+
// Calculate column widths
|
|
183
|
+
const widths = cols.map(col => {
|
|
184
|
+
const headerLen = col.header.length;
|
|
185
|
+
const maxDataLen = Math.max(...data.map(row => String(row[col.key] ?? '').length), 0);
|
|
186
|
+
return col.width ?? Math.max(headerLen, maxDataLen);
|
|
187
|
+
});
|
|
188
|
+
const separator = '─';
|
|
189
|
+
const corner = {
|
|
190
|
+
tl: '┌', tr: '┐', bl: '└', br: '┘',
|
|
191
|
+
ml: '├', mr: '┤', t: '┬', b: '┴', m: '┼',
|
|
192
|
+
};
|
|
193
|
+
// Top border
|
|
194
|
+
const topBorder = corner.tl + widths.map(w => separator.repeat(w + 2)).join(corner.t) + corner.tr;
|
|
195
|
+
process.stdout.write(topBorder + '\n');
|
|
196
|
+
// Header row
|
|
197
|
+
const headerRow = '│' + cols.map((col, i) => ' ' + col.header.padEnd(widths[i]) + ' ').join('│') + '│';
|
|
198
|
+
process.stdout.write(headerRow + '\n');
|
|
199
|
+
// Header separator
|
|
200
|
+
const headerSep = corner.ml + widths.map(w => separator.repeat(w + 2)).join(corner.m) + corner.mr;
|
|
201
|
+
process.stdout.write(headerSep + '\n');
|
|
202
|
+
// Data rows
|
|
203
|
+
for (const row of data) {
|
|
204
|
+
const dataRow = '│' + cols.map((col, i) => ' ' + String(row[col.key] ?? '').padEnd(widths[i]) + ' ').join('│') + '│';
|
|
205
|
+
process.stdout.write(dataRow + '\n');
|
|
206
|
+
}
|
|
207
|
+
// Bottom border
|
|
208
|
+
const bottomBorder = corner.bl + widths.map(w => separator.repeat(w + 2)).join(corner.b) + corner.br;
|
|
209
|
+
process.stdout.write(bottomBorder + '\n');
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Create an Output instance from global flags.
|
|
214
|
+
*/
|
|
215
|
+
export function createOutput(flags) {
|
|
216
|
+
return new Output(flags);
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Standard error handler for commands.
|
|
220
|
+
* Prints error to stderr and sets exit code.
|
|
221
|
+
*/
|
|
222
|
+
export function handleError(out, err, spinnerMessage) {
|
|
223
|
+
if (spinnerMessage) {
|
|
224
|
+
out.failSpinner(spinnerMessage);
|
|
225
|
+
}
|
|
226
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
227
|
+
out.error(message);
|
|
228
|
+
process.exitCode = 1;
|
|
229
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lifestreamdynamics/vault-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Command-line interface for Lifestream Vault",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"lsvault": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"!dist/**/*.test.*",
|
|
12
|
+
"!dist/**/__tests__",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
15
|
+
"keywords": [
|
|
16
|
+
"lifestream",
|
|
17
|
+
"vault",
|
|
18
|
+
"cli",
|
|
19
|
+
"markdown",
|
|
20
|
+
"webdav",
|
|
21
|
+
"sync",
|
|
22
|
+
"command-line"
|
|
23
|
+
],
|
|
24
|
+
"author": "Lifestream Dynamics <eric@lifestreamdynamics.com>",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/lifestreamdynamics/lifestream-vault-cli.git"
|
|
29
|
+
},
|
|
30
|
+
"bugs": {
|
|
31
|
+
"url": "https://github.com/lifestreamdynamics/lifestream-vault-cli/issues"
|
|
32
|
+
},
|
|
33
|
+
"homepage": "https://github.com/lifestreamdynamics/lifestream-vault-cli#readme",
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "tsc",
|
|
36
|
+
"dev": "tsx src/index.ts",
|
|
37
|
+
"test": "vitest run",
|
|
38
|
+
"test:watch": "vitest",
|
|
39
|
+
"test:coverage": "vitest run --coverage",
|
|
40
|
+
"test:ui": "vitest --ui",
|
|
41
|
+
"prepublishOnly": "npm run build && npm test"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@lifestreamdynamics/vault-sdk": "^1.0.0",
|
|
45
|
+
"chalk": "^5.4.0",
|
|
46
|
+
"chokidar": "^4.0.3",
|
|
47
|
+
"commander": "^13.0.0",
|
|
48
|
+
"minimatch": "^10.2.0",
|
|
49
|
+
"ora": "^8.0.0"
|
|
50
|
+
},
|
|
51
|
+
"optionalDependencies": {
|
|
52
|
+
"keytar": "^7.9.0"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@types/node": "^22.0.0",
|
|
56
|
+
"@vitest/coverage-v8": "^2.0.0",
|
|
57
|
+
"@vitest/ui": "^2.0.0",
|
|
58
|
+
"tsx": "^4.19.0",
|
|
59
|
+
"typescript": "^5.7.0",
|
|
60
|
+
"vitest": "^2.0.0"
|
|
61
|
+
}
|
|
62
|
+
}
|