@lifestreamdynamics/vault-cli 1.3.4 → 1.3.6
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/commands/analytics.js +10 -3
- package/dist/commands/audit.js +56 -2
- package/dist/commands/auth.js +9 -1
- package/dist/commands/booking.js +39 -23
- package/dist/commands/calendar.js +107 -52
- package/dist/commands/connectors.js +21 -1
- package/dist/commands/docs.js +65 -23
- package/dist/commands/hooks.js +46 -8
- package/dist/commands/keys.js +9 -2
- package/dist/commands/links.js +13 -4
- package/dist/commands/mfa.js +5 -1
- package/dist/commands/plugins.js +4 -3
- package/dist/commands/publish.js +25 -8
- package/dist/commands/search.js +7 -0
- package/dist/commands/shares.js +12 -3
- package/dist/commands/sync.js +37 -5
- package/dist/commands/user.js +13 -3
- package/dist/commands/vaults.js +6 -3
- package/dist/commands/versions.js +27 -9
- package/dist/commands/webhooks.js +39 -7
- package/dist/lib/credential-manager.js +8 -2
- package/dist/lib/keychain.js +13 -1
- package/dist/utils/flags.js +1 -1
- package/dist/utils/output.d.ts +4 -1
- package/dist/utils/output.js +15 -3
- package/dist/utils/resolve-vault.d.ts +8 -0
- package/dist/utils/resolve-vault.js +19 -0
- package/package.json +1 -1
package/dist/commands/docs.js
CHANGED
|
@@ -4,32 +4,45 @@ import { addGlobalFlags, resolveFlags } from '../utils/flags.js';
|
|
|
4
4
|
import { createOutput, handleError } from '../utils/output.js';
|
|
5
5
|
import { getCredentialManager } from '../config.js';
|
|
6
6
|
import { confirmAction } from '../utils/confirm.js';
|
|
7
|
+
import { resolveVaultId } from '../utils/resolve-vault.js';
|
|
7
8
|
export function registerDocCommands(program) {
|
|
8
9
|
const docs = program.command('docs').description('Read, write, move, and delete documents in a vault');
|
|
9
10
|
addGlobalFlags(docs.command('list')
|
|
10
11
|
.description('List documents in a vault, optionally filtered by directory')
|
|
11
|
-
.argument('<vaultId>', 'Vault ID')
|
|
12
|
+
.argument('<vaultId>', 'Vault ID or slug')
|
|
12
13
|
.option('--dir <path>', 'Filter by directory path')
|
|
14
|
+
.option('--limit <n>', 'Maximum number of documents to return')
|
|
15
|
+
.option('--offset <n>', 'Number of documents to skip')
|
|
16
|
+
.option('--tag <tags>', 'Filter by tags (comma-separated)')
|
|
13
17
|
.addHelpText('after', `
|
|
14
18
|
EXAMPLES
|
|
15
19
|
lsvault docs list abc123
|
|
16
|
-
lsvault docs list abc123 --dir notes/meetings
|
|
20
|
+
lsvault docs list abc123 --dir notes/meetings
|
|
21
|
+
lsvault docs list abc123 --tag work,project --limit 20
|
|
22
|
+
lsvault docs list abc123 --limit 50 --offset 100`))
|
|
17
23
|
.action(async (vaultId, _opts) => {
|
|
18
24
|
const flags = resolveFlags(_opts);
|
|
19
25
|
const out = createOutput(flags);
|
|
20
26
|
out.startSpinner('Fetching documents...');
|
|
21
27
|
try {
|
|
28
|
+
vaultId = await resolveVaultId(vaultId);
|
|
22
29
|
const client = await getClientAsync();
|
|
23
|
-
const
|
|
30
|
+
const dirPath = _opts.dir ? String(_opts.dir).replace(/\/+$/, '') : undefined;
|
|
31
|
+
const limit = _opts.limit !== undefined ? Number(_opts.limit) : undefined;
|
|
32
|
+
const offset = _opts.offset !== undefined ? Number(_opts.offset) : undefined;
|
|
33
|
+
const tags = _opts.tag ? String(_opts.tag).split(',').map((t) => t.trim()).filter(Boolean) : undefined;
|
|
34
|
+
const documents = await client.documents.list(vaultId, dirPath, { limit, offset, tags });
|
|
24
35
|
out.stopSpinner();
|
|
25
36
|
out.list(documents.map(doc => ({
|
|
37
|
+
id: doc.id,
|
|
26
38
|
path: doc.path,
|
|
27
|
-
title: doc.title
|
|
39
|
+
title: doc.title ?? null,
|
|
28
40
|
tags: Array.isArray(doc.tags) ? doc.tags.join(', ') : '',
|
|
29
41
|
sizeBytes: doc.sizeBytes,
|
|
30
42
|
})), {
|
|
31
43
|
emptyMessage: 'No documents found.',
|
|
32
44
|
columns: [
|
|
45
|
+
{ key: 'id', header: 'ID' },
|
|
33
46
|
{ key: 'path', header: 'Path' },
|
|
34
47
|
{ key: 'title', header: 'Title' },
|
|
35
48
|
{ key: 'tags', header: 'Tags' },
|
|
@@ -38,7 +51,7 @@ EXAMPLES
|
|
|
38
51
|
textFn: (doc) => {
|
|
39
52
|
const title = doc.title ? chalk.dim(` -- ${String(doc.title)}`) : '';
|
|
40
53
|
const tags = doc.tags ? chalk.blue(` [${String(doc.tags)}]`) : '';
|
|
41
|
-
return `${chalk.cyan(String(doc.path))}${title}${tags}`;
|
|
54
|
+
return `${chalk.cyan(String(doc.path))}${title}${tags} ${chalk.dim(String(doc.id))}`;
|
|
42
55
|
},
|
|
43
56
|
});
|
|
44
57
|
if (flags.output === 'text' && documents.length > 0) {
|
|
@@ -51,7 +64,7 @@ EXAMPLES
|
|
|
51
64
|
});
|
|
52
65
|
addGlobalFlags(docs.command('get')
|
|
53
66
|
.description('Print document content to stdout, or show metadata with --meta')
|
|
54
|
-
.argument('<vaultId>', 'Vault ID')
|
|
67
|
+
.argument('<vaultId>', 'Vault ID or slug')
|
|
55
68
|
.argument('<path>', 'Document path (e.g., notes/todo.md)')
|
|
56
69
|
.option('--meta', 'Show metadata instead of content')
|
|
57
70
|
.addHelpText('after', `
|
|
@@ -63,6 +76,7 @@ EXAMPLES
|
|
|
63
76
|
const flags = resolveFlags(_opts);
|
|
64
77
|
const out = createOutput(flags);
|
|
65
78
|
try {
|
|
79
|
+
vaultId = await resolveVaultId(vaultId);
|
|
66
80
|
const client = await getClientAsync();
|
|
67
81
|
const result = await client.documents.get(vaultId, docPath);
|
|
68
82
|
// Auto-decrypt if the document is encrypted
|
|
@@ -82,6 +96,7 @@ EXAMPLES
|
|
|
82
96
|
if (_opts.meta) {
|
|
83
97
|
const doc = result.document;
|
|
84
98
|
out.record({
|
|
99
|
+
id: doc.id,
|
|
85
100
|
path: doc.path,
|
|
86
101
|
title: doc.title,
|
|
87
102
|
sizeBytes: doc.sizeBytes,
|
|
@@ -93,6 +108,9 @@ EXAMPLES
|
|
|
93
108
|
updatedAt: doc.updatedAt,
|
|
94
109
|
});
|
|
95
110
|
}
|
|
111
|
+
else if (flags.output === 'json') {
|
|
112
|
+
out.raw(JSON.stringify({ path: result.document.path, content: result.content }) + '\n');
|
|
113
|
+
}
|
|
96
114
|
else {
|
|
97
115
|
out.raw(result.content);
|
|
98
116
|
}
|
|
@@ -103,7 +121,7 @@ EXAMPLES
|
|
|
103
121
|
});
|
|
104
122
|
addGlobalFlags(docs.command('put')
|
|
105
123
|
.description('Create or update a document by reading content from stdin')
|
|
106
|
-
.argument('<vaultId>', 'Vault ID')
|
|
124
|
+
.argument('<vaultId>', 'Vault ID or slug')
|
|
107
125
|
.argument('<path>', 'Document path (must end with .md)')
|
|
108
126
|
.addHelpText('after', `
|
|
109
127
|
EXAMPLES
|
|
@@ -114,11 +132,19 @@ EXAMPLES
|
|
|
114
132
|
const out = createOutput(flags);
|
|
115
133
|
out.startSpinner('Reading stdin...');
|
|
116
134
|
try {
|
|
135
|
+
vaultId = await resolveVaultId(vaultId);
|
|
117
136
|
const content = await new Promise((resolve) => {
|
|
118
137
|
let data = '';
|
|
119
138
|
process.stdin.on('data', (chunk) => data += chunk);
|
|
120
139
|
process.stdin.on('end', () => resolve(data));
|
|
121
140
|
});
|
|
141
|
+
if (!content.trim()) {
|
|
142
|
+
out.failSpinner('No content received');
|
|
143
|
+
out.error('No content received on stdin. Pipe content to this command:');
|
|
144
|
+
out.status(chalk.dim(` echo "# Hello" | lsvault docs put ${vaultId} ${docPath}`));
|
|
145
|
+
process.exitCode = 1;
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
122
148
|
out.startSpinner('Uploading document...');
|
|
123
149
|
const client = await getClientAsync();
|
|
124
150
|
// Check if vault is encrypted and auto-encrypt
|
|
@@ -139,10 +165,10 @@ EXAMPLES
|
|
|
139
165
|
else {
|
|
140
166
|
doc = await client.documents.put(vaultId, docPath, content);
|
|
141
167
|
}
|
|
142
|
-
out.success(`Document saved: ${chalk.cyan(doc.path)} (${doc.sizeBytes} bytes)`, {
|
|
168
|
+
out.success(`Document saved: ${chalk.cyan(doc.path)} (${doc.sizeBytes ?? 0} bytes)`, {
|
|
143
169
|
path: doc.path,
|
|
144
|
-
sizeBytes: doc.sizeBytes,
|
|
145
|
-
encrypted: doc.encrypted,
|
|
170
|
+
sizeBytes: doc.sizeBytes ?? 0,
|
|
171
|
+
encrypted: doc.encrypted ?? false,
|
|
146
172
|
});
|
|
147
173
|
}
|
|
148
174
|
catch (err) {
|
|
@@ -151,13 +177,14 @@ EXAMPLES
|
|
|
151
177
|
});
|
|
152
178
|
addGlobalFlags(docs.command('delete')
|
|
153
179
|
.description('Permanently delete a document from a vault')
|
|
154
|
-
.argument('<vaultId>', 'Vault ID')
|
|
180
|
+
.argument('<vaultId>', 'Vault ID or slug')
|
|
155
181
|
.argument('<path>', 'Document path to delete')
|
|
156
182
|
.option('-y, --yes', 'Skip confirmation prompt'))
|
|
157
183
|
.action(async (vaultId, docPath, _opts) => {
|
|
158
184
|
const flags = resolveFlags(_opts);
|
|
159
185
|
const out = createOutput(flags);
|
|
160
186
|
try {
|
|
187
|
+
vaultId = await resolveVaultId(vaultId);
|
|
161
188
|
const confirmed = await confirmAction(`Permanently delete "${docPath}" from vault ${vaultId}?`, { yes: _opts.yes });
|
|
162
189
|
if (!confirmed) {
|
|
163
190
|
out.status('Deletion cancelled.');
|
|
@@ -174,7 +201,7 @@ EXAMPLES
|
|
|
174
201
|
});
|
|
175
202
|
addGlobalFlags(docs.command('move')
|
|
176
203
|
.description('Move or rename a document within a vault')
|
|
177
|
-
.argument('<vaultId>', 'Vault ID')
|
|
204
|
+
.argument('<vaultId>', 'Vault ID or slug')
|
|
178
205
|
.argument('<source>', 'Current document path')
|
|
179
206
|
.argument('<dest>', 'New document path')
|
|
180
207
|
.option('--overwrite', 'Overwrite if destination already exists')
|
|
@@ -185,8 +212,17 @@ EXAMPLES
|
|
|
185
212
|
.action(async (vaultId, source, dest, _opts) => {
|
|
186
213
|
const flags = resolveFlags(_opts);
|
|
187
214
|
const out = createOutput(flags);
|
|
215
|
+
if (flags.dryRun) {
|
|
216
|
+
out.success(`[dry-run] Would move: ${source} → ${dest}`, {
|
|
217
|
+
dryRun: true,
|
|
218
|
+
source,
|
|
219
|
+
destination: dest,
|
|
220
|
+
});
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
188
223
|
out.startSpinner('Moving document...');
|
|
189
224
|
try {
|
|
225
|
+
vaultId = await resolveVaultId(vaultId);
|
|
190
226
|
const client = await getClientAsync();
|
|
191
227
|
const result = await client.documents.move(vaultId, source, dest, _opts.overwrite);
|
|
192
228
|
out.success(`Moved: ${chalk.cyan(result.source)} -> ${chalk.cyan(result.destination)}`, {
|
|
@@ -200,7 +236,7 @@ EXAMPLES
|
|
|
200
236
|
});
|
|
201
237
|
addGlobalFlags(docs.command('bulk-move')
|
|
202
238
|
.description('Move multiple documents to a target directory')
|
|
203
|
-
.argument('<vaultId>', 'Vault ID')
|
|
239
|
+
.argument('<vaultId>', 'Vault ID or slug')
|
|
204
240
|
.requiredOption('--paths <csv>', 'Comma-separated list of document paths')
|
|
205
241
|
.requiredOption('--target <dir>', 'Target directory'))
|
|
206
242
|
.action(async (vaultId, _opts) => {
|
|
@@ -208,9 +244,10 @@ EXAMPLES
|
|
|
208
244
|
const out = createOutput(flags);
|
|
209
245
|
out.startSpinner('Moving documents...');
|
|
210
246
|
try {
|
|
247
|
+
vaultId = await resolveVaultId(vaultId);
|
|
211
248
|
const client = await getClientAsync();
|
|
212
249
|
const paths = (String(_opts.paths)).split(',').map(p => p.trim()).filter(Boolean);
|
|
213
|
-
const result = await client.documents.bulkMove(vaultId, { paths,
|
|
250
|
+
const result = await client.documents.bulkMove(vaultId, { items: paths, destination: _opts.target });
|
|
214
251
|
out.stopSpinner();
|
|
215
252
|
if (flags.output === 'json') {
|
|
216
253
|
out.raw(JSON.stringify(result, null, 2) + '\n');
|
|
@@ -229,7 +266,7 @@ EXAMPLES
|
|
|
229
266
|
});
|
|
230
267
|
addGlobalFlags(docs.command('bulk-copy')
|
|
231
268
|
.description('Copy multiple documents to a target directory')
|
|
232
|
-
.argument('<vaultId>', 'Vault ID')
|
|
269
|
+
.argument('<vaultId>', 'Vault ID or slug')
|
|
233
270
|
.requiredOption('--paths <csv>', 'Comma-separated list of document paths')
|
|
234
271
|
.requiredOption('--target <dir>', 'Target directory'))
|
|
235
272
|
.action(async (vaultId, _opts) => {
|
|
@@ -237,9 +274,10 @@ EXAMPLES
|
|
|
237
274
|
const out = createOutput(flags);
|
|
238
275
|
out.startSpinner('Copying documents...');
|
|
239
276
|
try {
|
|
277
|
+
vaultId = await resolveVaultId(vaultId);
|
|
240
278
|
const client = await getClientAsync();
|
|
241
279
|
const paths = (String(_opts.paths)).split(',').map(p => p.trim()).filter(Boolean);
|
|
242
|
-
const result = await client.documents.bulkCopy(vaultId, { paths,
|
|
280
|
+
const result = await client.documents.bulkCopy(vaultId, { items: paths, destination: _opts.target });
|
|
243
281
|
out.stopSpinner();
|
|
244
282
|
if (flags.output === 'json') {
|
|
245
283
|
out.raw(JSON.stringify(result, null, 2) + '\n');
|
|
@@ -258,16 +296,17 @@ EXAMPLES
|
|
|
258
296
|
});
|
|
259
297
|
addGlobalFlags(docs.command('bulk-delete')
|
|
260
298
|
.description('Delete multiple documents')
|
|
261
|
-
.argument('<vaultId>', 'Vault ID')
|
|
299
|
+
.argument('<vaultId>', 'Vault ID or slug')
|
|
262
300
|
.requiredOption('--paths <csv>', 'Comma-separated list of document paths'))
|
|
263
301
|
.action(async (vaultId, _opts) => {
|
|
264
302
|
const flags = resolveFlags(_opts);
|
|
265
303
|
const out = createOutput(flags);
|
|
266
304
|
out.startSpinner('Deleting documents...');
|
|
267
305
|
try {
|
|
306
|
+
vaultId = await resolveVaultId(vaultId);
|
|
268
307
|
const client = await getClientAsync();
|
|
269
308
|
const paths = (String(_opts.paths)).split(',').map(p => p.trim()).filter(Boolean);
|
|
270
|
-
const result = await client.documents.bulkDelete(vaultId, { paths });
|
|
309
|
+
const result = await client.documents.bulkDelete(vaultId, { items: paths });
|
|
271
310
|
out.stopSpinner();
|
|
272
311
|
if (flags.output === 'json') {
|
|
273
312
|
out.raw(JSON.stringify(result, null, 2) + '\n');
|
|
@@ -286,7 +325,7 @@ EXAMPLES
|
|
|
286
325
|
});
|
|
287
326
|
addGlobalFlags(docs.command('bulk-tag')
|
|
288
327
|
.description('Add or remove tags from multiple documents')
|
|
289
|
-
.argument('<vaultId>', 'Vault ID')
|
|
328
|
+
.argument('<vaultId>', 'Vault ID or slug')
|
|
290
329
|
.requiredOption('--paths <csv>', 'Comma-separated list of document paths')
|
|
291
330
|
.option('--add <csv>', 'Tags to add (comma-separated)')
|
|
292
331
|
.option('--remove <csv>', 'Tags to remove (comma-separated)'))
|
|
@@ -300,11 +339,12 @@ EXAMPLES
|
|
|
300
339
|
}
|
|
301
340
|
out.startSpinner('Tagging documents...');
|
|
302
341
|
try {
|
|
342
|
+
vaultId = await resolveVaultId(vaultId);
|
|
303
343
|
const client = await getClientAsync();
|
|
304
344
|
const paths = (String(_opts.paths)).split(',').map(p => p.trim()).filter(Boolean);
|
|
305
345
|
const addTags = _opts.add ? (String(_opts.add)).split(',').map(t => t.trim()).filter(Boolean) : undefined;
|
|
306
346
|
const removeTags = _opts.remove ? (String(_opts.remove)).split(',').map(t => t.trim()).filter(Boolean) : undefined;
|
|
307
|
-
const result = await client.documents.bulkTag(vaultId, { paths, addTags, removeTags });
|
|
347
|
+
const result = await client.documents.bulkTag(vaultId, { items: paths, addTags, removeTags });
|
|
308
348
|
out.stopSpinner();
|
|
309
349
|
if (flags.output === 'json') {
|
|
310
350
|
out.raw(JSON.stringify(result, null, 2) + '\n');
|
|
@@ -323,16 +363,18 @@ EXAMPLES
|
|
|
323
363
|
});
|
|
324
364
|
addGlobalFlags(docs.command('mkdir')
|
|
325
365
|
.description('Create a directory in a vault')
|
|
326
|
-
.argument('<vaultId>', 'Vault ID')
|
|
366
|
+
.argument('<vaultId>', 'Vault ID or slug')
|
|
327
367
|
.argument('<path>', 'Directory path to create'))
|
|
328
368
|
.action(async (vaultId, path, _opts) => {
|
|
329
369
|
const flags = resolveFlags(_opts);
|
|
330
370
|
const out = createOutput(flags);
|
|
331
371
|
out.startSpinner('Creating directory...');
|
|
332
372
|
try {
|
|
373
|
+
vaultId = await resolveVaultId(vaultId);
|
|
333
374
|
const client = await getClientAsync();
|
|
334
|
-
const
|
|
335
|
-
|
|
375
|
+
const normalizedPath = path.replace(/\/+$/, '');
|
|
376
|
+
const result = await client.documents.createDirectory(vaultId, normalizedPath);
|
|
377
|
+
out.success(`Directory ${result.created !== false ? 'created' : 'already exists'}: ${result.path}`, { path: result.path, created: result.created ?? true });
|
|
336
378
|
}
|
|
337
379
|
catch (err) {
|
|
338
380
|
handleError(out, err, 'Failed to create directory');
|
package/dist/commands/hooks.js
CHANGED
|
@@ -2,16 +2,18 @@ import chalk from 'chalk';
|
|
|
2
2
|
import { getClientAsync } from '../client.js';
|
|
3
3
|
import { addGlobalFlags, resolveFlags } from '../utils/flags.js';
|
|
4
4
|
import { createOutput, handleError } from '../utils/output.js';
|
|
5
|
+
import { resolveVaultId } from '../utils/resolve-vault.js';
|
|
5
6
|
export function registerHookCommands(program) {
|
|
6
|
-
const hooks = program.command('hooks').description('Manage vault event hooks
|
|
7
|
+
const hooks = program.command('hooks').description('Manage vault event hooks');
|
|
7
8
|
addGlobalFlags(hooks.command('list')
|
|
8
9
|
.description('List all hooks for a vault')
|
|
9
|
-
.argument('<vaultId>', 'Vault ID'))
|
|
10
|
+
.argument('<vaultId>', 'Vault ID or slug'))
|
|
10
11
|
.action(async (vaultId, _opts) => {
|
|
11
12
|
const flags = resolveFlags(_opts);
|
|
12
13
|
const out = createOutput(flags);
|
|
13
14
|
out.startSpinner('Fetching hooks...');
|
|
14
15
|
try {
|
|
16
|
+
vaultId = await resolveVaultId(vaultId);
|
|
15
17
|
const client = await getClientAsync();
|
|
16
18
|
const hookList = await client.hooks.list(vaultId);
|
|
17
19
|
out.stopSpinner();
|
|
@@ -45,12 +47,31 @@ export function registerHookCommands(program) {
|
|
|
45
47
|
});
|
|
46
48
|
addGlobalFlags(hooks.command('create')
|
|
47
49
|
.description('Create a new hook')
|
|
48
|
-
.argument('<vaultId>', 'Vault ID')
|
|
50
|
+
.argument('<vaultId>', 'Vault ID or slug')
|
|
49
51
|
.argument('<name>', 'Hook name')
|
|
50
|
-
.requiredOption('--trigger <event>', 'Trigger event (
|
|
51
|
-
.requiredOption('--action <type>', 'Action type (
|
|
52
|
+
.requiredOption('--trigger <event>', 'Trigger event (document.created, document.updated, document.deleted, document.moved, document.copied)')
|
|
53
|
+
.requiredOption('--action <type>', 'Action type (webhook, ai_prompt, document_operation, auto_calendar_event, auto_booking_process)')
|
|
52
54
|
.requiredOption('--config <json>', 'Action configuration as JSON')
|
|
53
|
-
.option('--filter <json>', 'Trigger filter as JSON')
|
|
55
|
+
.option('--filter <json>', 'Trigger filter as JSON')
|
|
56
|
+
.addHelpText('after', `
|
|
57
|
+
VALID TRIGGER EVENTS
|
|
58
|
+
document.created Document was created
|
|
59
|
+
document.updated Document content was updated
|
|
60
|
+
document.deleted Document was deleted
|
|
61
|
+
document.moved Document was moved or renamed
|
|
62
|
+
document.copied Document was copied
|
|
63
|
+
|
|
64
|
+
VALID ACTION TYPES
|
|
65
|
+
webhook Send an HTTP notification to a URL
|
|
66
|
+
ai_prompt Run an AI prompt on the document
|
|
67
|
+
document_operation Perform a document operation (move, copy, tag)
|
|
68
|
+
auto_calendar_event Automatically create a calendar event
|
|
69
|
+
auto_booking_process Automatically process a booking
|
|
70
|
+
|
|
71
|
+
EXAMPLES
|
|
72
|
+
lsvault hooks create <vaultId> my-hook --trigger document.created --action webhook --config '{"url":"https://example.com/hook"}'
|
|
73
|
+
lsvault hooks create <vaultId> ai-tag --trigger document.created --action ai_prompt --config '{"prompt":"Suggest tags"}'
|
|
74
|
+
lsvault hooks create <vaultId> move-docs --trigger document.created --action document_operation --config '{"operation":"move","targetPath":"inbox/"}'`))
|
|
54
75
|
.action(async (vaultId, name, _opts) => {
|
|
55
76
|
const flags = resolveFlags(_opts);
|
|
56
77
|
const out = createOutput(flags);
|
|
@@ -74,8 +95,23 @@ export function registerHookCommands(program) {
|
|
|
74
95
|
return;
|
|
75
96
|
}
|
|
76
97
|
}
|
|
98
|
+
const VALID_TRIGGERS = ['document.created', 'document.updated', 'document.deleted', 'document.moved', 'document.copied'];
|
|
99
|
+
const VALID_ACTIONS = ['webhook', 'ai_prompt', 'document_operation', 'auto_calendar_event', 'auto_booking_process'];
|
|
100
|
+
const trigger = String(_opts.trigger);
|
|
101
|
+
const action = String(_opts.action);
|
|
102
|
+
if (!VALID_TRIGGERS.includes(trigger)) {
|
|
103
|
+
out.error(`Invalid trigger "${trigger}". Valid values: document.created, document.updated, document.deleted, document.moved, document.copied`);
|
|
104
|
+
process.exitCode = 1;
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (!VALID_ACTIONS.includes(action)) {
|
|
108
|
+
out.error(`Invalid action "${action}". Valid values: webhook, ai_prompt, document_operation, auto_calendar_event, auto_booking_process`);
|
|
109
|
+
process.exitCode = 1;
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
77
112
|
out.startSpinner('Creating hook...');
|
|
78
113
|
try {
|
|
114
|
+
vaultId = await resolveVaultId(vaultId);
|
|
79
115
|
const client = await getClientAsync();
|
|
80
116
|
const params = {
|
|
81
117
|
name,
|
|
@@ -99,7 +135,7 @@ export function registerHookCommands(program) {
|
|
|
99
135
|
});
|
|
100
136
|
addGlobalFlags(hooks.command('delete')
|
|
101
137
|
.description('Delete a hook')
|
|
102
|
-
.argument('<vaultId>', 'Vault ID')
|
|
138
|
+
.argument('<vaultId>', 'Vault ID or slug')
|
|
103
139
|
.argument('<hookId>', 'Hook ID')
|
|
104
140
|
.option('-y, --yes', 'Skip confirmation prompt'))
|
|
105
141
|
.action(async (vaultId, hookId, _opts) => {
|
|
@@ -111,6 +147,7 @@ export function registerHookCommands(program) {
|
|
|
111
147
|
}
|
|
112
148
|
out.startSpinner('Deleting hook...');
|
|
113
149
|
try {
|
|
150
|
+
vaultId = await resolveVaultId(vaultId);
|
|
114
151
|
const client = await getClientAsync();
|
|
115
152
|
await client.hooks.delete(vaultId, hookId);
|
|
116
153
|
out.success('Hook deleted successfully', { id: hookId, deleted: true });
|
|
@@ -121,13 +158,14 @@ export function registerHookCommands(program) {
|
|
|
121
158
|
});
|
|
122
159
|
addGlobalFlags(hooks.command('executions')
|
|
123
160
|
.description('List recent executions for a hook')
|
|
124
|
-
.argument('<vaultId>', 'Vault ID')
|
|
161
|
+
.argument('<vaultId>', 'Vault ID or slug')
|
|
125
162
|
.argument('<hookId>', 'Hook ID'))
|
|
126
163
|
.action(async (vaultId, hookId, _opts) => {
|
|
127
164
|
const flags = resolveFlags(_opts);
|
|
128
165
|
const out = createOutput(flags);
|
|
129
166
|
out.startSpinner('Fetching executions...');
|
|
130
167
|
try {
|
|
168
|
+
vaultId = await resolveVaultId(vaultId);
|
|
131
169
|
const client = await getClientAsync();
|
|
132
170
|
const executions = await client.hooks.listExecutions(vaultId, hookId);
|
|
133
171
|
out.stopSpinner();
|
package/dist/commands/keys.js
CHANGED
|
@@ -3,7 +3,9 @@ import { getClientAsync } from '../client.js';
|
|
|
3
3
|
import { addGlobalFlags, resolveFlags } from '../utils/flags.js';
|
|
4
4
|
import { createOutput, handleError } from '../utils/output.js';
|
|
5
5
|
export function registerKeyCommands(program) {
|
|
6
|
-
const keys = program.command('keys')
|
|
6
|
+
const keys = program.command('keys')
|
|
7
|
+
.description('Create, list, update, and revoke API keys (requires JWT auth — use "lsvault auth login" first)')
|
|
8
|
+
.addHelpText('after', '\nNOTE: API key management requires JWT authentication. API key auth is not sufficient.\nRun "lsvault auth login" to authenticate with email/password first.');
|
|
7
9
|
addGlobalFlags(keys.command('list')
|
|
8
10
|
.description('List all API keys for the current user'))
|
|
9
11
|
.action(async (_opts) => {
|
|
@@ -148,10 +150,15 @@ EXAMPLES
|
|
|
148
150
|
});
|
|
149
151
|
addGlobalFlags(keys.command('revoke')
|
|
150
152
|
.description('Permanently revoke and delete an API key')
|
|
151
|
-
.argument('<keyId>', 'API key ID')
|
|
153
|
+
.argument('<keyId>', 'API key ID')
|
|
154
|
+
.option('-y, --yes', 'Skip confirmation prompt'))
|
|
152
155
|
.action(async (keyId, _opts) => {
|
|
153
156
|
const flags = resolveFlags(_opts);
|
|
154
157
|
const out = createOutput(flags);
|
|
158
|
+
if (!_opts.yes) {
|
|
159
|
+
out.status(chalk.yellow(`Pass -y/--yes to permanently revoke key ${keyId}.`));
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
155
162
|
out.startSpinner('Revoking API key...');
|
|
156
163
|
try {
|
|
157
164
|
const client = await getClientAsync();
|
package/dist/commands/links.js
CHANGED
|
@@ -2,18 +2,20 @@ import chalk from 'chalk';
|
|
|
2
2
|
import { getClientAsync } from '../client.js';
|
|
3
3
|
import { addGlobalFlags, resolveFlags } from '../utils/flags.js';
|
|
4
4
|
import { createOutput, handleError } from '../utils/output.js';
|
|
5
|
+
import { resolveVaultId } from '../utils/resolve-vault.js';
|
|
5
6
|
export function registerLinkCommands(program) {
|
|
6
7
|
const links = program.command('links').description('Manage document links and backlinks');
|
|
7
8
|
// lsvault links list <vaultId> <path> — forward links
|
|
8
9
|
addGlobalFlags(links.command('list')
|
|
9
10
|
.description('List forward links from a document')
|
|
10
|
-
.argument('<vaultId>', 'Vault ID')
|
|
11
|
+
.argument('<vaultId>', 'Vault ID or slug')
|
|
11
12
|
.argument('<path>', 'Document path'))
|
|
12
13
|
.action(async (vaultId, docPath, _opts) => {
|
|
13
14
|
const flags = resolveFlags(_opts);
|
|
14
15
|
const out = createOutput(flags);
|
|
15
16
|
out.startSpinner('Fetching links...');
|
|
16
17
|
try {
|
|
18
|
+
vaultId = await resolveVaultId(vaultId);
|
|
17
19
|
const client = await getClientAsync();
|
|
18
20
|
const linkList = await client.documents.getLinks(vaultId, docPath);
|
|
19
21
|
out.stopSpinner();
|
|
@@ -41,13 +43,14 @@ export function registerLinkCommands(program) {
|
|
|
41
43
|
// lsvault links backlinks <vaultId> <path>
|
|
42
44
|
addGlobalFlags(links.command('backlinks')
|
|
43
45
|
.description('List backlinks pointing to a document')
|
|
44
|
-
.argument('<vaultId>', 'Vault ID')
|
|
46
|
+
.argument('<vaultId>', 'Vault ID or slug')
|
|
45
47
|
.argument('<path>', 'Document path'))
|
|
46
48
|
.action(async (vaultId, docPath, _opts) => {
|
|
47
49
|
const flags = resolveFlags(_opts);
|
|
48
50
|
const out = createOutput(flags);
|
|
49
51
|
out.startSpinner('Fetching backlinks...');
|
|
50
52
|
try {
|
|
53
|
+
vaultId = await resolveVaultId(vaultId);
|
|
51
54
|
const client = await getClientAsync();
|
|
52
55
|
const backlinks = await client.documents.getBacklinks(vaultId, docPath);
|
|
53
56
|
out.stopSpinner();
|
|
@@ -78,12 +81,13 @@ export function registerLinkCommands(program) {
|
|
|
78
81
|
// lsvault links graph <vaultId>
|
|
79
82
|
addGlobalFlags(links.command('graph')
|
|
80
83
|
.description('Get the link graph for a vault')
|
|
81
|
-
.argument('<vaultId>', 'Vault ID'))
|
|
84
|
+
.argument('<vaultId>', 'Vault ID or slug'))
|
|
82
85
|
.action(async (vaultId, _opts) => {
|
|
83
86
|
const flags = resolveFlags(_opts);
|
|
84
87
|
const out = createOutput(flags);
|
|
85
88
|
out.startSpinner('Fetching link graph...');
|
|
86
89
|
try {
|
|
90
|
+
vaultId = await resolveVaultId(vaultId);
|
|
87
91
|
const client = await getClientAsync();
|
|
88
92
|
const graph = await client.vaults.getGraph(vaultId);
|
|
89
93
|
out.stopSpinner();
|
|
@@ -104,15 +108,20 @@ export function registerLinkCommands(program) {
|
|
|
104
108
|
// lsvault links broken <vaultId>
|
|
105
109
|
addGlobalFlags(links.command('broken')
|
|
106
110
|
.description('List unresolved (broken) links in a vault')
|
|
107
|
-
.argument('<vaultId>', 'Vault ID'))
|
|
111
|
+
.argument('<vaultId>', 'Vault ID or slug'))
|
|
108
112
|
.action(async (vaultId, _opts) => {
|
|
109
113
|
const flags = resolveFlags(_opts);
|
|
110
114
|
const out = createOutput(flags);
|
|
111
115
|
out.startSpinner('Fetching unresolved links...');
|
|
112
116
|
try {
|
|
117
|
+
vaultId = await resolveVaultId(vaultId);
|
|
113
118
|
const client = await getClientAsync();
|
|
114
119
|
const unresolved = await client.vaults.getUnresolvedLinks(vaultId);
|
|
115
120
|
out.stopSpinner();
|
|
121
|
+
if (flags.output === 'json') {
|
|
122
|
+
process.stdout.write(JSON.stringify(unresolved) + '\n');
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
116
125
|
if (unresolved.length === 0) {
|
|
117
126
|
out.success('No broken links found!');
|
|
118
127
|
return;
|
package/dist/commands/mfa.js
CHANGED
|
@@ -3,7 +3,9 @@ import ora from 'ora';
|
|
|
3
3
|
import { getClientAsync } from '../client.js';
|
|
4
4
|
import { promptPassword, promptMfaCode } from '../utils/prompt.js';
|
|
5
5
|
export function registerMfaCommands(program) {
|
|
6
|
-
const mfa = program.command('mfa')
|
|
6
|
+
const mfa = program.command('mfa')
|
|
7
|
+
.description('Multi-factor authentication management (requires JWT auth — use "lsvault auth login" first)')
|
|
8
|
+
.addHelpText('after', '\nNOTE: MFA management requires JWT authentication. API key auth is not sufficient.\nRun "lsvault auth login" to authenticate with email/password first.');
|
|
7
9
|
mfa.command('status')
|
|
8
10
|
.description('Show MFA status and configured methods')
|
|
9
11
|
.action(async () => {
|
|
@@ -129,6 +131,7 @@ export function registerMfaCommands(program) {
|
|
|
129
131
|
catch (err) {
|
|
130
132
|
spinner.fail('Failed to regenerate backup codes');
|
|
131
133
|
console.error(err instanceof Error ? err.message : String(err));
|
|
134
|
+
process.exitCode = 1;
|
|
132
135
|
}
|
|
133
136
|
}
|
|
134
137
|
else {
|
|
@@ -149,6 +152,7 @@ export function registerMfaCommands(program) {
|
|
|
149
152
|
catch (err) {
|
|
150
153
|
spinner.fail('Failed to fetch backup code count');
|
|
151
154
|
console.error(err instanceof Error ? err.message : String(err));
|
|
155
|
+
process.exitCode = 1;
|
|
152
156
|
}
|
|
153
157
|
}
|
|
154
158
|
});
|
package/dist/commands/plugins.js
CHANGED
|
@@ -71,12 +71,13 @@ export function registerPluginCommands(program) {
|
|
|
71
71
|
addGlobalFlags(plugins.command('uninstall')
|
|
72
72
|
.description('Uninstall a plugin')
|
|
73
73
|
.argument('<pluginId>', 'Plugin marketplace identifier')
|
|
74
|
-
.option('--
|
|
74
|
+
.option('-y, --yes', 'Skip confirmation prompt')
|
|
75
|
+
.option('--confirm', 'Alias for --yes (deprecated)'))
|
|
75
76
|
.action(async (pluginId, _opts) => {
|
|
76
77
|
const flags = resolveFlags(_opts);
|
|
77
78
|
const out = createOutput(flags);
|
|
78
|
-
if (!_opts.confirm) {
|
|
79
|
-
out.raw(chalk.yellow(`Pass --confirm to uninstall plugin ${pluginId}.`) + '\n');
|
|
79
|
+
if (!_opts.yes && !_opts.confirm) {
|
|
80
|
+
out.raw(chalk.yellow(`Pass -y/--yes or --confirm to uninstall plugin ${pluginId}.`) + '\n');
|
|
80
81
|
return;
|
|
81
82
|
}
|
|
82
83
|
out.startSpinner('Uninstalling plugin...');
|