@lifestreamdynamics/vault-cli 1.3.5 → 1.3.7
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 +4 -2
- 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/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 +2 -2
package/dist/commands/publish.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 registerPublishCommands(program) {
|
|
6
7
|
const publish = program.command('publish').description('Publish documents to public profile pages');
|
|
7
8
|
addGlobalFlags(publish.command('list')
|
|
8
9
|
.description('List your published documents')
|
|
9
|
-
.argument('<vaultId>', 'Vault ID (required by route)'))
|
|
10
|
+
.argument('<vaultId>', 'Vault ID or slug (required by route)'))
|
|
10
11
|
.action(async (vaultId, _opts) => {
|
|
11
12
|
const flags = resolveFlags(_opts);
|
|
12
13
|
const out = createOutput(flags);
|
|
13
14
|
out.startSpinner('Fetching published documents...');
|
|
14
15
|
try {
|
|
16
|
+
vaultId = await resolveVaultId(vaultId);
|
|
15
17
|
const client = await getClientAsync();
|
|
16
18
|
const docs = await client.publish.listMine(vaultId);
|
|
17
19
|
out.stopSpinner();
|
|
@@ -49,7 +51,7 @@ export function registerPublishCommands(program) {
|
|
|
49
51
|
});
|
|
50
52
|
addGlobalFlags(publish.command('create')
|
|
51
53
|
.description('Publish a document')
|
|
52
|
-
.argument('<vaultId>', 'Vault ID')
|
|
54
|
+
.argument('<vaultId>', 'Vault ID or slug')
|
|
53
55
|
.argument('<docPath>', 'Document path (e.g., blog/post.md)')
|
|
54
56
|
.requiredOption('--slug <slug>', 'URL-friendly slug for the published page')
|
|
55
57
|
.option('--title <title>', 'SEO title')
|
|
@@ -60,6 +62,7 @@ export function registerPublishCommands(program) {
|
|
|
60
62
|
const out = createOutput(flags);
|
|
61
63
|
out.startSpinner('Publishing document...');
|
|
62
64
|
try {
|
|
65
|
+
vaultId = await resolveVaultId(vaultId);
|
|
63
66
|
const client = await getClientAsync();
|
|
64
67
|
const params = {
|
|
65
68
|
slug: String(_opts.slug),
|
|
@@ -73,6 +76,7 @@ export function registerPublishCommands(program) {
|
|
|
73
76
|
const pub = await client.publish.create(vaultId, docPath, params);
|
|
74
77
|
out.success('Document published successfully!', {
|
|
75
78
|
slug: pub.slug,
|
|
79
|
+
url: `/${pub.publishedBy}/${pub.slug}`,
|
|
76
80
|
isPublished: pub.isPublished,
|
|
77
81
|
seoTitle: pub.seoTitle || null,
|
|
78
82
|
seoDescription: pub.seoDescription || null,
|
|
@@ -85,7 +89,7 @@ export function registerPublishCommands(program) {
|
|
|
85
89
|
});
|
|
86
90
|
addGlobalFlags(publish.command('update')
|
|
87
91
|
.description('Update a published document')
|
|
88
|
-
.argument('<vaultId>', 'Vault ID')
|
|
92
|
+
.argument('<vaultId>', 'Vault ID or slug')
|
|
89
93
|
.argument('<docPath>', 'Document path (e.g., blog/post.md)')
|
|
90
94
|
.requiredOption('--slug <slug>', 'URL-friendly slug (required for updates)')
|
|
91
95
|
.option('--title <title>', 'SEO title')
|
|
@@ -96,6 +100,7 @@ export function registerPublishCommands(program) {
|
|
|
96
100
|
const out = createOutput(flags);
|
|
97
101
|
out.startSpinner('Updating published document...');
|
|
98
102
|
try {
|
|
103
|
+
vaultId = await resolveVaultId(vaultId);
|
|
99
104
|
const client = await getClientAsync();
|
|
100
105
|
const params = {
|
|
101
106
|
slug: String(_opts.slug),
|
|
@@ -120,7 +125,7 @@ export function registerPublishCommands(program) {
|
|
|
120
125
|
});
|
|
121
126
|
addGlobalFlags(publish.command('delete')
|
|
122
127
|
.description('Unpublish a document')
|
|
123
|
-
.argument('<vaultId>', 'Vault ID')
|
|
128
|
+
.argument('<vaultId>', 'Vault ID or slug')
|
|
124
129
|
.argument('<docPath>', 'Document path (e.g., blog/post.md)')
|
|
125
130
|
.option('-y, --yes', 'Skip confirmation prompt'))
|
|
126
131
|
.action(async (vaultId, docPath, _opts) => {
|
|
@@ -132,6 +137,7 @@ export function registerPublishCommands(program) {
|
|
|
132
137
|
}
|
|
133
138
|
out.startSpinner('Unpublishing document...');
|
|
134
139
|
try {
|
|
140
|
+
vaultId = await resolveVaultId(vaultId);
|
|
135
141
|
const client = await getClientAsync();
|
|
136
142
|
await client.publish.delete(vaultId, docPath);
|
|
137
143
|
out.success('Document unpublished successfully', { path: docPath, unpublished: true });
|
|
@@ -143,16 +149,25 @@ export function registerPublishCommands(program) {
|
|
|
143
149
|
const subdomain = publish.command('subdomain').description('Subdomain management for published vaults');
|
|
144
150
|
addGlobalFlags(subdomain.command('get')
|
|
145
151
|
.description('Get the subdomain for a published vault')
|
|
146
|
-
.argument('<vaultId>', 'Vault ID'))
|
|
152
|
+
.argument('<vaultId>', 'Vault ID or slug'))
|
|
147
153
|
.action(async (vaultId, _opts) => {
|
|
148
154
|
const flags = resolveFlags(_opts);
|
|
149
155
|
const out = createOutput(flags);
|
|
150
156
|
out.startSpinner('Fetching subdomain...');
|
|
151
157
|
try {
|
|
158
|
+
vaultId = await resolveVaultId(vaultId);
|
|
152
159
|
const client = await getClientAsync();
|
|
153
160
|
const result = await client.publish.getSubdomain(vaultId);
|
|
154
161
|
out.stopSpinner();
|
|
155
|
-
|
|
162
|
+
if (flags.output === 'json') {
|
|
163
|
+
out.record({ subdomain: result.subdomain });
|
|
164
|
+
}
|
|
165
|
+
else if (result.subdomain == null) {
|
|
166
|
+
out.status('No subdomain configured.');
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
out.record({ subdomain: result.subdomain });
|
|
170
|
+
}
|
|
156
171
|
}
|
|
157
172
|
catch (err) {
|
|
158
173
|
handleError(out, err, 'Failed to fetch subdomain');
|
|
@@ -160,13 +175,14 @@ export function registerPublishCommands(program) {
|
|
|
160
175
|
});
|
|
161
176
|
addGlobalFlags(subdomain.command('set')
|
|
162
177
|
.description('Set a subdomain for a published vault')
|
|
163
|
-
.argument('<vaultId>', 'Vault ID')
|
|
178
|
+
.argument('<vaultId>', 'Vault ID or slug')
|
|
164
179
|
.argument('<subdomain>', 'Subdomain to assign'))
|
|
165
180
|
.action(async (vaultId, subdomainArg, _opts) => {
|
|
166
181
|
const flags = resolveFlags(_opts);
|
|
167
182
|
const out = createOutput(flags);
|
|
168
183
|
out.startSpinner('Setting subdomain...');
|
|
169
184
|
try {
|
|
185
|
+
vaultId = await resolveVaultId(vaultId);
|
|
170
186
|
const client = await getClientAsync();
|
|
171
187
|
const result = await client.publish.setSubdomain(vaultId, subdomainArg);
|
|
172
188
|
out.success(`Subdomain set: ${result.subdomain}`, { subdomain: result.subdomain });
|
|
@@ -177,7 +193,7 @@ export function registerPublishCommands(program) {
|
|
|
177
193
|
});
|
|
178
194
|
addGlobalFlags(subdomain.command('delete')
|
|
179
195
|
.description('Remove the subdomain for a published vault')
|
|
180
|
-
.argument('<vaultId>', 'Vault ID')
|
|
196
|
+
.argument('<vaultId>', 'Vault ID or slug')
|
|
181
197
|
.option('-y, --yes', 'Skip confirmation prompt'))
|
|
182
198
|
.action(async (vaultId, _opts) => {
|
|
183
199
|
const flags = resolveFlags(_opts);
|
|
@@ -188,6 +204,7 @@ export function registerPublishCommands(program) {
|
|
|
188
204
|
}
|
|
189
205
|
out.startSpinner('Removing subdomain...');
|
|
190
206
|
try {
|
|
207
|
+
vaultId = await resolveVaultId(vaultId);
|
|
191
208
|
const client = await getClientAsync();
|
|
192
209
|
const result = await client.publish.deleteSubdomain(vaultId);
|
|
193
210
|
out.success(result.message, { message: result.message });
|
package/dist/commands/search.js
CHANGED
|
@@ -2,6 +2,7 @@ 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 registerSearchCommands(program) {
|
|
6
7
|
addGlobalFlags(program.command('search')
|
|
7
8
|
.description('Full-text search across all documents')
|
|
@@ -21,6 +22,8 @@ EXAMPLES
|
|
|
21
22
|
const out = createOutput(flags);
|
|
22
23
|
out.startSpinner('Searching...');
|
|
23
24
|
try {
|
|
25
|
+
if (_opts.vault)
|
|
26
|
+
_opts.vault = await resolveVaultId(String(_opts.vault));
|
|
24
27
|
const client = await getClientAsync();
|
|
25
28
|
const mode = _opts.mode;
|
|
26
29
|
const response = await client.search.search({
|
|
@@ -31,6 +34,9 @@ EXAMPLES
|
|
|
31
34
|
mode: mode,
|
|
32
35
|
});
|
|
33
36
|
out.stopSpinner();
|
|
37
|
+
if (response.results.length === 0 && mode === 'semantic') {
|
|
38
|
+
out.warn('Semantic search returned no results. Ensure document embeddings have been generated (the embedding worker must be running).');
|
|
39
|
+
}
|
|
34
40
|
if (flags.output === 'text') {
|
|
35
41
|
const modeInfo = mode && mode !== 'text' ? `[${mode}] ` : '';
|
|
36
42
|
out.status(chalk.dim(`${modeInfo}${response.total} result(s) for "${response.query}":\n`));
|
|
@@ -39,6 +45,7 @@ EXAMPLES
|
|
|
39
45
|
title: r.title || r.path,
|
|
40
46
|
path: r.path,
|
|
41
47
|
vaultName: r.vaultName,
|
|
48
|
+
rank: r.rank,
|
|
42
49
|
tags: r.tags.join(', '),
|
|
43
50
|
snippet: r.snippet ? r.snippet.replace(/<[^>]+>/g, '') : '',
|
|
44
51
|
})), {
|
package/dist/commands/shares.js
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { getClientAsync } from '../client.js';
|
|
3
|
+
import { loadConfigAsync } from '../config.js';
|
|
3
4
|
import { addGlobalFlags, resolveFlags } from '../utils/flags.js';
|
|
4
5
|
import { createOutput, handleError } from '../utils/output.js';
|
|
5
6
|
import { promptPassword, readPasswordFromStdin } from '../utils/prompt.js';
|
|
7
|
+
import { resolveVaultId } from '../utils/resolve-vault.js';
|
|
6
8
|
export function registerShareCommands(program) {
|
|
7
9
|
const shares = program.command('shares').description('Create, list, and revoke document share links');
|
|
8
10
|
addGlobalFlags(shares.command('list')
|
|
9
11
|
.description('List share links for a document')
|
|
10
|
-
.argument('<vaultId>', 'Vault ID')
|
|
12
|
+
.argument('<vaultId>', 'Vault ID or slug')
|
|
11
13
|
.argument('<docPath>', 'Document path (e.g., notes/meeting.md)'))
|
|
12
14
|
.action(async (vaultId, docPath, _opts) => {
|
|
13
15
|
const flags = resolveFlags(_opts);
|
|
14
16
|
const out = createOutput(flags);
|
|
15
17
|
out.startSpinner('Fetching share links...');
|
|
16
18
|
try {
|
|
19
|
+
vaultId = await resolveVaultId(vaultId);
|
|
17
20
|
const client = await getClientAsync();
|
|
18
21
|
const links = await client.shares.list(vaultId, docPath);
|
|
19
22
|
out.stopSpinner();
|
|
@@ -53,7 +56,7 @@ export function registerShareCommands(program) {
|
|
|
53
56
|
});
|
|
54
57
|
addGlobalFlags(shares.command('create')
|
|
55
58
|
.description('Create a share link for a document')
|
|
56
|
-
.argument('<vaultId>', 'Vault ID')
|
|
59
|
+
.argument('<vaultId>', 'Vault ID or slug')
|
|
57
60
|
.argument('<docPath>', 'Document path (e.g., notes/meeting.md)')
|
|
58
61
|
.option('--permission <perm>', 'Permission level: view or edit', 'view')
|
|
59
62
|
.option('--protect-with-password', 'Prompt for a password to protect the link (interactive TTY only)')
|
|
@@ -85,6 +88,7 @@ export function registerShareCommands(program) {
|
|
|
85
88
|
}
|
|
86
89
|
out.startSpinner('Creating share link...');
|
|
87
90
|
try {
|
|
91
|
+
vaultId = await resolveVaultId(vaultId);
|
|
88
92
|
const client = await getClientAsync();
|
|
89
93
|
const params = {};
|
|
90
94
|
if (_opts.permission)
|
|
@@ -97,9 +101,12 @@ export function registerShareCommands(program) {
|
|
|
97
101
|
params.maxViews = parseInt(String(_opts.maxViews), 10);
|
|
98
102
|
const result = await client.shares.create(vaultId, docPath, params);
|
|
99
103
|
out.stopSpinner();
|
|
104
|
+
const config = await loadConfigAsync();
|
|
105
|
+
const shareUrl = `${config.apiUrl}/share/${result.fullToken}`;
|
|
100
106
|
if (flags.output === 'json') {
|
|
101
107
|
out.record({
|
|
102
108
|
token: result.fullToken,
|
|
109
|
+
url: shareUrl,
|
|
103
110
|
id: result.shareLink.id,
|
|
104
111
|
permission: result.shareLink.permission,
|
|
105
112
|
expiresAt: result.shareLink.expiresAt || null,
|
|
@@ -109,6 +116,7 @@ export function registerShareCommands(program) {
|
|
|
109
116
|
else {
|
|
110
117
|
out.warn('\nIMPORTANT: Save this token. It cannot be retrieved later.\n');
|
|
111
118
|
process.stdout.write(chalk.green.bold(`Token: ${result.fullToken}\n`));
|
|
119
|
+
process.stdout.write(chalk.green(`URL: ${shareUrl}\n`));
|
|
112
120
|
process.stdout.write(`\nID: ${result.shareLink.id}\n`);
|
|
113
121
|
process.stdout.write(`Permission: ${result.shareLink.permission}\n`);
|
|
114
122
|
if (result.shareLink.expiresAt) {
|
|
@@ -125,13 +133,14 @@ export function registerShareCommands(program) {
|
|
|
125
133
|
});
|
|
126
134
|
addGlobalFlags(shares.command('revoke')
|
|
127
135
|
.description('Revoke a share link')
|
|
128
|
-
.argument('<vaultId>', 'Vault ID')
|
|
136
|
+
.argument('<vaultId>', 'Vault ID or slug')
|
|
129
137
|
.argument('<shareId>', 'Share link ID'))
|
|
130
138
|
.action(async (vaultId, shareId, _opts) => {
|
|
131
139
|
const flags = resolveFlags(_opts);
|
|
132
140
|
const out = createOutput(flags);
|
|
133
141
|
out.startSpinner('Revoking share link...');
|
|
134
142
|
try {
|
|
143
|
+
vaultId = await resolveVaultId(vaultId);
|
|
135
144
|
const client = await getClientAsync();
|
|
136
145
|
await client.shares.revoke(vaultId, shareId);
|
|
137
146
|
out.success('Share link revoked successfully', { id: shareId, revoked: true });
|
package/dist/commands/sync.js
CHANGED
|
@@ -186,8 +186,6 @@ Sync modes:
|
|
|
186
186
|
}
|
|
187
187
|
out.stopSpinner();
|
|
188
188
|
if (flags.dryRun) {
|
|
189
|
-
out.status(chalk.yellow('Dry run — no changes will be made:'));
|
|
190
|
-
out.status(formatDiff(diff));
|
|
191
189
|
if (flags.output === 'json') {
|
|
192
190
|
out.record({
|
|
193
191
|
dryRun: true,
|
|
@@ -197,6 +195,10 @@ Sync modes:
|
|
|
197
195
|
totalBytes: diff.totalBytes,
|
|
198
196
|
});
|
|
199
197
|
}
|
|
198
|
+
else {
|
|
199
|
+
out.status(chalk.yellow('Dry run — no changes will be made:'));
|
|
200
|
+
out.status(formatDiff(diff));
|
|
201
|
+
}
|
|
200
202
|
return;
|
|
201
203
|
}
|
|
202
204
|
if (flags.verbose) {
|
package/dist/commands/user.js
CHANGED
|
@@ -409,9 +409,19 @@ export function registerUserCommands(program) {
|
|
|
409
409
|
addGlobalFlags(consents.command('set')
|
|
410
410
|
.description('Record a consent decision')
|
|
411
411
|
.requiredOption('--type <t>', 'Consent type')
|
|
412
|
-
.requiredOption('--version <v>', 'Policy version')
|
|
412
|
+
.requiredOption('--policy-version <v>', 'Policy version')
|
|
413
413
|
.option('--granted', 'Grant consent')
|
|
414
|
-
.option('--no-granted', 'Deny consent')
|
|
414
|
+
.option('--no-granted', 'Deny consent')
|
|
415
|
+
.addHelpText('after', `
|
|
416
|
+
CONSENT TYPES
|
|
417
|
+
terms_of_service Terms of Service acceptance
|
|
418
|
+
privacy_policy Privacy Policy acceptance
|
|
419
|
+
ai_processing AI feature data processing consent
|
|
420
|
+
marketing Marketing communications consent
|
|
421
|
+
|
|
422
|
+
EXAMPLES
|
|
423
|
+
lsvault user consents set --type terms_of_service --policy-version 1.0 --granted
|
|
424
|
+
lsvault user consents set --type ai_processing --policy-version 1.0 --no-granted`))
|
|
415
425
|
.action(async (_opts) => {
|
|
416
426
|
const flags = resolveFlags(_opts);
|
|
417
427
|
const out = createOutput(flags);
|
|
@@ -420,7 +430,7 @@ export function registerUserCommands(program) {
|
|
|
420
430
|
const client = await getClientAsync();
|
|
421
431
|
const result = await client.user.recordConsent({
|
|
422
432
|
consentType: _opts.type,
|
|
423
|
-
version: _opts.
|
|
433
|
+
version: _opts.policyVersion,
|
|
424
434
|
granted: _opts.granted !== false,
|
|
425
435
|
});
|
|
426
436
|
out.success(result.message, { message: result.message });
|
package/dist/commands/vaults.js
CHANGED
|
@@ -4,6 +4,7 @@ import { addGlobalFlags, resolveFlags } from '../utils/flags.js';
|
|
|
4
4
|
import { createOutput, handleError } from '../utils/output.js';
|
|
5
5
|
import { generateVaultKey } from '@lifestreamdynamics/vault-sdk';
|
|
6
6
|
import { getCredentialManager } from '../config.js';
|
|
7
|
+
import { resolveVaultId } from '../utils/resolve-vault.js';
|
|
7
8
|
export function registerVaultCommands(program) {
|
|
8
9
|
const vaults = program.command('vaults').description('Create, list, and inspect document vaults');
|
|
9
10
|
addGlobalFlags(vaults.command('list')
|
|
@@ -16,7 +17,7 @@ export function registerVaultCommands(program) {
|
|
|
16
17
|
const client = await getClientAsync();
|
|
17
18
|
const vaultList = await client.vaults.list();
|
|
18
19
|
out.stopSpinner();
|
|
19
|
-
out.list(vaultList.map(v => ({ name: v.name, slug: v.slug, encrypted: v.encryptionEnabled ? 'yes' : 'no', description: v.description
|
|
20
|
+
out.list(vaultList.map(v => ({ name: v.name, slug: v.slug, encrypted: v.encryptionEnabled ? 'yes' : 'no', description: v.description ?? null, id: v.id })), {
|
|
20
21
|
emptyMessage: 'No vaults found.',
|
|
21
22
|
columns: [
|
|
22
23
|
{ key: 'name', header: 'Name' },
|
|
@@ -27,7 +28,8 @@ export function registerVaultCommands(program) {
|
|
|
27
28
|
],
|
|
28
29
|
textFn: (v) => {
|
|
29
30
|
const encIcon = v.encrypted === 'yes' ? chalk.green(' [encrypted]') : '';
|
|
30
|
-
|
|
31
|
+
const desc = v.description != null ? ` -- ${String(v.description)}` : '';
|
|
32
|
+
return `${chalk.cyan(String(v.name))} ${chalk.dim(`(${String(v.slug)})`)}${encIcon}${desc}`;
|
|
31
33
|
},
|
|
32
34
|
});
|
|
33
35
|
}
|
|
@@ -160,12 +162,13 @@ EXAMPLES
|
|
|
160
162
|
// vault tree
|
|
161
163
|
addGlobalFlags(vaults.command('tree')
|
|
162
164
|
.description('Show vault file tree')
|
|
163
|
-
.argument('<vaultId>', 'Vault ID'))
|
|
165
|
+
.argument('<vaultId>', 'Vault ID or slug'))
|
|
164
166
|
.action(async (vaultId, _opts) => {
|
|
165
167
|
const flags = resolveFlags(_opts);
|
|
166
168
|
const out = createOutput(flags);
|
|
167
169
|
out.startSpinner('Fetching vault tree...');
|
|
168
170
|
try {
|
|
171
|
+
vaultId = await resolveVaultId(vaultId);
|
|
169
172
|
const client = await getClientAsync();
|
|
170
173
|
const tree = await client.vaults.getTree(vaultId);
|
|
171
174
|
out.stopSpinner();
|
|
@@ -2,11 +2,12 @@ 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 registerVersionCommands(program) {
|
|
6
7
|
const versions = program.command('versions').description('View and manage document version history');
|
|
7
8
|
addGlobalFlags(versions.command('list')
|
|
8
9
|
.description('List version history for a document')
|
|
9
|
-
.argument('<vaultId>', 'Vault ID')
|
|
10
|
+
.argument('<vaultId>', 'Vault ID or slug')
|
|
10
11
|
.argument('<path>', 'Document path (e.g., notes/todo.md)')
|
|
11
12
|
.addHelpText('after', `
|
|
12
13
|
EXAMPLES
|
|
@@ -16,6 +17,7 @@ EXAMPLES
|
|
|
16
17
|
const out = createOutput(flags);
|
|
17
18
|
out.startSpinner('Fetching versions...');
|
|
18
19
|
try {
|
|
20
|
+
vaultId = await resolveVaultId(vaultId);
|
|
19
21
|
const client = await getClientAsync();
|
|
20
22
|
const versionList = await client.documents.listVersions(vaultId, docPath);
|
|
21
23
|
out.stopSpinner();
|
|
@@ -23,7 +25,7 @@ EXAMPLES
|
|
|
23
25
|
version: v.versionNum,
|
|
24
26
|
source: v.changeSource,
|
|
25
27
|
sizeBytes: v.sizeBytes,
|
|
26
|
-
pinned: v.isPinned ? 'yes' : 'no',
|
|
28
|
+
pinned: flags.output === 'json' ? v.isPinned : (v.isPinned ? 'yes' : 'no'),
|
|
27
29
|
createdAt: v.createdAt,
|
|
28
30
|
})), {
|
|
29
31
|
emptyMessage: 'No versions found.',
|
|
@@ -49,7 +51,7 @@ EXAMPLES
|
|
|
49
51
|
});
|
|
50
52
|
addGlobalFlags(versions.command('view')
|
|
51
53
|
.description('View content of a specific version')
|
|
52
|
-
.argument('<vaultId>', 'Vault ID')
|
|
54
|
+
.argument('<vaultId>', 'Vault ID or slug')
|
|
53
55
|
.argument('<path>', 'Document path')
|
|
54
56
|
.argument('<version>', 'Version number')
|
|
55
57
|
.addHelpText('after', `
|
|
@@ -65,6 +67,7 @@ EXAMPLES
|
|
|
65
67
|
return;
|
|
66
68
|
}
|
|
67
69
|
try {
|
|
70
|
+
vaultId = await resolveVaultId(vaultId);
|
|
68
71
|
const client = await getClientAsync();
|
|
69
72
|
const version = await client.documents.getVersion(vaultId, docPath, versionNum);
|
|
70
73
|
if (version.content === null) {
|
|
@@ -72,7 +75,12 @@ EXAMPLES
|
|
|
72
75
|
process.exitCode = 1;
|
|
73
76
|
return;
|
|
74
77
|
}
|
|
75
|
-
|
|
78
|
+
if (flags.output === 'json') {
|
|
79
|
+
out.raw(JSON.stringify({ version: version.versionNum, createdAt: version.createdAt, content: version.content }) + '\n');
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
out.raw(version.content);
|
|
83
|
+
}
|
|
76
84
|
}
|
|
77
85
|
catch (err) {
|
|
78
86
|
handleError(out, err, 'Failed to get version');
|
|
@@ -80,7 +88,7 @@ EXAMPLES
|
|
|
80
88
|
});
|
|
81
89
|
addGlobalFlags(versions.command('diff')
|
|
82
90
|
.description('Show diff between two versions')
|
|
83
|
-
.argument('<vaultId>', 'Vault ID')
|
|
91
|
+
.argument('<vaultId>', 'Vault ID or slug')
|
|
84
92
|
.argument('<path>', 'Document path')
|
|
85
93
|
.argument('<from>', 'Source version number')
|
|
86
94
|
.argument('<to>', 'Target version number')
|
|
@@ -99,6 +107,7 @@ EXAMPLES
|
|
|
99
107
|
}
|
|
100
108
|
out.startSpinner('Computing diff...');
|
|
101
109
|
try {
|
|
110
|
+
vaultId = await resolveVaultId(vaultId);
|
|
102
111
|
const client = await getClientAsync();
|
|
103
112
|
const diff = await client.documents.diffVersions(vaultId, docPath, from, to);
|
|
104
113
|
out.stopSpinner();
|
|
@@ -121,12 +130,18 @@ EXAMPLES
|
|
|
121
130
|
}
|
|
122
131
|
}
|
|
123
132
|
catch (err) {
|
|
124
|
-
|
|
133
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
134
|
+
if (message.includes('not found') || message.includes('Not found')) {
|
|
135
|
+
handleError(out, new Error(`Version ${from} or ${to} not found for document "${docPath}". Use 'versions list' to see available versions.`), 'Failed to compute diff');
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
handleError(out, err, 'Failed to compute diff');
|
|
139
|
+
}
|
|
125
140
|
}
|
|
126
141
|
});
|
|
127
142
|
addGlobalFlags(versions.command('restore')
|
|
128
143
|
.description('Restore a document to a previous version')
|
|
129
|
-
.argument('<vaultId>', 'Vault ID')
|
|
144
|
+
.argument('<vaultId>', 'Vault ID or slug')
|
|
130
145
|
.argument('<path>', 'Document path')
|
|
131
146
|
.argument('<version>', 'Version number to restore')
|
|
132
147
|
.addHelpText('after', `
|
|
@@ -143,6 +158,7 @@ EXAMPLES
|
|
|
143
158
|
}
|
|
144
159
|
out.startSpinner(`Restoring to version ${versionNum}...`);
|
|
145
160
|
try {
|
|
161
|
+
vaultId = await resolveVaultId(vaultId);
|
|
146
162
|
const client = await getClientAsync();
|
|
147
163
|
const doc = await client.documents.restoreVersion(vaultId, docPath, versionNum);
|
|
148
164
|
out.success(`Restored ${chalk.cyan(docPath)} to version ${versionNum}`, {
|
|
@@ -156,7 +172,7 @@ EXAMPLES
|
|
|
156
172
|
});
|
|
157
173
|
addGlobalFlags(versions.command('pin')
|
|
158
174
|
.description('Pin a version to prevent pruning')
|
|
159
|
-
.argument('<vaultId>', 'Vault ID')
|
|
175
|
+
.argument('<vaultId>', 'Vault ID or slug')
|
|
160
176
|
.argument('<path>', 'Document path')
|
|
161
177
|
.argument('<version>', 'Version number to pin')
|
|
162
178
|
.addHelpText('after', `
|
|
@@ -173,6 +189,7 @@ EXAMPLES
|
|
|
173
189
|
}
|
|
174
190
|
out.startSpinner(`Pinning version ${versionNum}...`);
|
|
175
191
|
try {
|
|
192
|
+
vaultId = await resolveVaultId(vaultId);
|
|
176
193
|
const client = await getClientAsync();
|
|
177
194
|
await client.documents.pinVersion(vaultId, docPath, versionNum);
|
|
178
195
|
out.success(`Pinned version ${versionNum} of ${chalk.cyan(docPath)}`, {
|
|
@@ -187,7 +204,7 @@ EXAMPLES
|
|
|
187
204
|
});
|
|
188
205
|
addGlobalFlags(versions.command('unpin')
|
|
189
206
|
.description('Unpin a version, allowing it to be pruned')
|
|
190
|
-
.argument('<vaultId>', 'Vault ID')
|
|
207
|
+
.argument('<vaultId>', 'Vault ID or slug')
|
|
191
208
|
.argument('<path>', 'Document path')
|
|
192
209
|
.argument('<version>', 'Version number to unpin')
|
|
193
210
|
.addHelpText('after', `
|
|
@@ -204,6 +221,7 @@ EXAMPLES
|
|
|
204
221
|
}
|
|
205
222
|
out.startSpinner(`Unpinning version ${versionNum}...`);
|
|
206
223
|
try {
|
|
224
|
+
vaultId = await resolveVaultId(vaultId);
|
|
207
225
|
const client = await getClientAsync();
|
|
208
226
|
await client.documents.unpinVersion(vaultId, docPath, versionNum);
|
|
209
227
|
out.success(`Unpinned version ${versionNum} of ${chalk.cyan(docPath)}`, {
|
|
@@ -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 registerWebhookCommands(program) {
|
|
6
7
|
const webhooks = program.command('webhooks').description('Manage vault webhooks');
|
|
7
8
|
addGlobalFlags(webhooks.command('list')
|
|
8
9
|
.description('List all webhooks 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 webhooks...');
|
|
14
15
|
try {
|
|
16
|
+
vaultId = await resolveVaultId(vaultId);
|
|
15
17
|
const client = await getClientAsync();
|
|
16
18
|
const webhookList = await client.webhooks.list(vaultId);
|
|
17
19
|
out.stopSpinner();
|
|
@@ -42,18 +44,45 @@ export function registerWebhookCommands(program) {
|
|
|
42
44
|
});
|
|
43
45
|
addGlobalFlags(webhooks.command('create')
|
|
44
46
|
.description('Create a new webhook')
|
|
45
|
-
.argument('<vaultId>', 'Vault ID')
|
|
47
|
+
.argument('<vaultId>', 'Vault ID or slug')
|
|
46
48
|
.argument('<url>', 'Webhook endpoint URL')
|
|
47
|
-
.option('--events <events>', 'Comma-separated events (
|
|
49
|
+
.option('--events <events>', 'Comma-separated events (document.created, document.updated, document.deleted, document.moved, document.copied, or * for all)', 'document.created,document.updated,document.deleted')
|
|
50
|
+
.addHelpText('after', `
|
|
51
|
+
VALID EVENT NAMES
|
|
52
|
+
document.created Document was created
|
|
53
|
+
document.updated Document content was updated
|
|
54
|
+
document.deleted Document was deleted
|
|
55
|
+
document.moved Document was moved or renamed
|
|
56
|
+
document.copied Document was copied
|
|
57
|
+
* All events
|
|
58
|
+
|
|
59
|
+
EXAMPLES
|
|
60
|
+
lsvault webhooks create <vaultId> https://example.com/hook
|
|
61
|
+
lsvault webhooks create <vaultId> https://example.com/hook --events "document.created,document.deleted"
|
|
62
|
+
lsvault webhooks create <vaultId> https://example.com/hook --events "*"`))
|
|
48
63
|
.action(async (vaultId, url, _opts) => {
|
|
49
64
|
const flags = resolveFlags(_opts);
|
|
50
65
|
const out = createOutput(flags);
|
|
66
|
+
if (!/^https?:\/\//i.test(url)) {
|
|
67
|
+
out.error(`Invalid webhook URL "${url}". URL must start with http:// or https://`);
|
|
68
|
+
process.exitCode = 1;
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const VALID_EVENTS = ['document.created', 'document.updated', 'document.deleted', 'document.moved', 'document.copied', '*'];
|
|
72
|
+
const events = String(_opts.events || 'document.created,document.updated,document.deleted').split(',').map((e) => e.trim());
|
|
73
|
+
const invalid = events.filter(e => !VALID_EVENTS.includes(e));
|
|
74
|
+
if (invalid.length > 0) {
|
|
75
|
+
out.error(`Invalid event name(s): ${invalid.join(', ')}. Valid values: ${VALID_EVENTS.join(', ')}`);
|
|
76
|
+
process.exitCode = 1;
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
51
79
|
out.startSpinner('Creating webhook...');
|
|
52
80
|
try {
|
|
81
|
+
vaultId = await resolveVaultId(vaultId);
|
|
53
82
|
const client = await getClientAsync();
|
|
54
83
|
const params = {
|
|
55
84
|
url,
|
|
56
|
-
events
|
|
85
|
+
events,
|
|
57
86
|
};
|
|
58
87
|
const webhook = await client.webhooks.create(vaultId, params);
|
|
59
88
|
out.stopSpinner();
|
|
@@ -79,7 +108,7 @@ export function registerWebhookCommands(program) {
|
|
|
79
108
|
});
|
|
80
109
|
addGlobalFlags(webhooks.command('update')
|
|
81
110
|
.description('Update a webhook')
|
|
82
|
-
.argument('<vaultId>', 'Vault ID')
|
|
111
|
+
.argument('<vaultId>', 'Vault ID or slug')
|
|
83
112
|
.argument('<webhookId>', 'Webhook ID')
|
|
84
113
|
.option('--url <url>', 'New webhook URL')
|
|
85
114
|
.option('--events <events>', 'Comma-separated events')
|
|
@@ -95,6 +124,7 @@ export function registerWebhookCommands(program) {
|
|
|
95
124
|
}
|
|
96
125
|
out.startSpinner('Updating webhook...');
|
|
97
126
|
try {
|
|
127
|
+
vaultId = await resolveVaultId(vaultId);
|
|
98
128
|
const client = await getClientAsync();
|
|
99
129
|
const params = {};
|
|
100
130
|
if (_opts.url)
|
|
@@ -118,7 +148,7 @@ export function registerWebhookCommands(program) {
|
|
|
118
148
|
});
|
|
119
149
|
addGlobalFlags(webhooks.command('delete')
|
|
120
150
|
.description('Delete a webhook')
|
|
121
|
-
.argument('<vaultId>', 'Vault ID')
|
|
151
|
+
.argument('<vaultId>', 'Vault ID or slug')
|
|
122
152
|
.argument('<webhookId>', 'Webhook ID')
|
|
123
153
|
.option('-y, --yes', 'Skip confirmation prompt'))
|
|
124
154
|
.action(async (vaultId, webhookId, _opts) => {
|
|
@@ -130,6 +160,7 @@ export function registerWebhookCommands(program) {
|
|
|
130
160
|
}
|
|
131
161
|
out.startSpinner('Deleting webhook...');
|
|
132
162
|
try {
|
|
163
|
+
vaultId = await resolveVaultId(vaultId);
|
|
133
164
|
const client = await getClientAsync();
|
|
134
165
|
await client.webhooks.delete(vaultId, webhookId);
|
|
135
166
|
out.success('Webhook deleted successfully', { id: webhookId, deleted: true });
|
|
@@ -140,13 +171,14 @@ export function registerWebhookCommands(program) {
|
|
|
140
171
|
});
|
|
141
172
|
addGlobalFlags(webhooks.command('deliveries')
|
|
142
173
|
.description('List recent deliveries for a webhook')
|
|
143
|
-
.argument('<vaultId>', 'Vault ID')
|
|
174
|
+
.argument('<vaultId>', 'Vault ID or slug')
|
|
144
175
|
.argument('<webhookId>', 'Webhook ID'))
|
|
145
176
|
.action(async (vaultId, webhookId, _opts) => {
|
|
146
177
|
const flags = resolveFlags(_opts);
|
|
147
178
|
const out = createOutput(flags);
|
|
148
179
|
out.startSpinner('Fetching deliveries...');
|
|
149
180
|
try {
|
|
181
|
+
vaultId = await resolveVaultId(vaultId);
|
|
150
182
|
const client = await getClientAsync();
|
|
151
183
|
const deliveries = await client.webhooks.listDeliveries(vaultId, webhookId);
|
|
152
184
|
out.stopSpinner();
|
package/dist/utils/flags.js
CHANGED
|
@@ -5,7 +5,7 @@ import chalk from 'chalk';
|
|
|
5
5
|
*/
|
|
6
6
|
export function addGlobalFlags(cmd) {
|
|
7
7
|
return cmd
|
|
8
|
-
.option('-o, --output <format>', 'Output format: text, json, table (default: auto)')
|
|
8
|
+
.option('-o, --output <format>', 'Output format: text, json, table (default: auto — text for TTY, json for pipes)')
|
|
9
9
|
.option('-v, --verbose', 'Verbose output (debug info)')
|
|
10
10
|
.option('-q, --quiet', 'Minimal output (errors only)')
|
|
11
11
|
.option('--no-color', 'Disable colored output')
|
package/dist/utils/output.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { GlobalFlags } from './flags.js';
|
|
1
|
+
import type { GlobalFlags, OutputFormat } from './flags.js';
|
|
2
2
|
/**
|
|
3
3
|
* Column definition for table output.
|
|
4
4
|
*/
|
|
@@ -41,8 +41,11 @@ export declare class Output {
|
|
|
41
41
|
debug(message: string): void;
|
|
42
42
|
/**
|
|
43
43
|
* Print an error message to stderr.
|
|
44
|
+
* When output format is json, writes a JSON error envelope instead of colored text.
|
|
44
45
|
*/
|
|
45
46
|
error(message: string): void;
|
|
47
|
+
/** Expose the current output format (for error handlers that need it). */
|
|
48
|
+
get format(): OutputFormat;
|
|
46
49
|
/**
|
|
47
50
|
* Print a warning message to stderr.
|
|
48
51
|
*/
|