@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/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
|
@@ -24,7 +24,17 @@ export function registerSyncCommands(program) {
|
|
|
24
24
|
.option('--on-conflict <strategy>', 'Conflict strategy: newer, local, remote, ask (default: newer)')
|
|
25
25
|
.option('--ignore <patterns...>', 'Glob patterns to ignore')
|
|
26
26
|
.option('--interval <interval>', 'Auto-sync interval (e.g., 5m, 1h)')
|
|
27
|
-
.option('--auto-sync', 'Enable auto-sync')
|
|
27
|
+
.option('--auto-sync', 'Enable auto-sync')
|
|
28
|
+
.addHelpText('after', `
|
|
29
|
+
Examples:
|
|
30
|
+
lsvault sync init <vaultId> ~/my-vault
|
|
31
|
+
lsvault sync init <vaultId> ~/mirror --mode pull --on-conflict remote
|
|
32
|
+
lsvault sync init <vaultId> ~/docs --mode push --on-conflict local --auto-sync
|
|
33
|
+
|
|
34
|
+
Sync modes:
|
|
35
|
+
pull Download remote changes only (ideal for cron/automation)
|
|
36
|
+
push Upload local changes only (ideal for CI pipelines)
|
|
37
|
+
sync Bidirectional with conflict detection (default)`))
|
|
28
38
|
.action(async (vaultId, localPath, _opts) => {
|
|
29
39
|
const flags = resolveFlags(_opts);
|
|
30
40
|
const out = createOutput(flags);
|
|
@@ -158,26 +168,37 @@ export function registerSyncCommands(program) {
|
|
|
158
168
|
out.debug(`Found ${Object.keys(remoteFiles).length} remote files`);
|
|
159
169
|
out.startSpinner('Computing diff...');
|
|
160
170
|
const diff = computePullDiff(localFiles, remoteFiles, lastState);
|
|
171
|
+
const unchanged = Object.keys(remoteFiles).length - diff.downloads.length;
|
|
161
172
|
const totalOps = diff.downloads.length + diff.deletes.length;
|
|
162
173
|
if (totalOps === 0) {
|
|
163
174
|
out.succeedSpinner('Everything is up to date');
|
|
164
175
|
if (flags.output === 'json') {
|
|
165
|
-
out.record({
|
|
176
|
+
out.record({
|
|
177
|
+
status: 'up-to-date',
|
|
178
|
+
downloaded: 0,
|
|
179
|
+
deleted: 0,
|
|
180
|
+
unchanged: Object.keys(remoteFiles).length,
|
|
181
|
+
bytesTransferred: 0,
|
|
182
|
+
errors: 0,
|
|
183
|
+
});
|
|
166
184
|
}
|
|
167
185
|
return;
|
|
168
186
|
}
|
|
169
187
|
out.stopSpinner();
|
|
170
188
|
if (flags.dryRun) {
|
|
171
|
-
out.status(chalk.yellow('Dry run — no changes will be made:'));
|
|
172
|
-
out.status(formatDiff(diff));
|
|
173
189
|
if (flags.output === 'json') {
|
|
174
190
|
out.record({
|
|
175
191
|
dryRun: true,
|
|
176
192
|
downloads: diff.downloads.length,
|
|
177
193
|
deletes: diff.deletes.length,
|
|
194
|
+
unchanged,
|
|
178
195
|
totalBytes: diff.totalBytes,
|
|
179
196
|
});
|
|
180
197
|
}
|
|
198
|
+
else {
|
|
199
|
+
out.status(chalk.yellow('Dry run — no changes will be made:'));
|
|
200
|
+
out.status(formatDiff(diff));
|
|
201
|
+
}
|
|
181
202
|
return;
|
|
182
203
|
}
|
|
183
204
|
if (flags.verbose) {
|
|
@@ -201,6 +222,7 @@ export function registerSyncCommands(program) {
|
|
|
201
222
|
out.success('', {
|
|
202
223
|
downloaded: result.filesDownloaded,
|
|
203
224
|
deleted: result.filesDeleted,
|
|
225
|
+
unchanged,
|
|
204
226
|
bytesTransferred: result.bytesTransferred,
|
|
205
227
|
errors: result.errors.length,
|
|
206
228
|
});
|
|
@@ -234,11 +256,19 @@ export function registerSyncCommands(program) {
|
|
|
234
256
|
out.debug(`Found ${Object.keys(remoteFiles).length} remote files`);
|
|
235
257
|
out.startSpinner('Computing diff...');
|
|
236
258
|
const diff = computePushDiff(localFiles, remoteFiles, lastState);
|
|
259
|
+
const unchanged = Object.keys(localFiles).length - diff.uploads.length;
|
|
237
260
|
const totalOps = diff.uploads.length + diff.deletes.length;
|
|
238
261
|
if (totalOps === 0) {
|
|
239
262
|
out.succeedSpinner('Everything is up to date');
|
|
240
263
|
if (flags.output === 'json') {
|
|
241
|
-
out.record({
|
|
264
|
+
out.record({
|
|
265
|
+
status: 'up-to-date',
|
|
266
|
+
uploaded: 0,
|
|
267
|
+
deleted: 0,
|
|
268
|
+
unchanged: Object.keys(localFiles).length,
|
|
269
|
+
bytesTransferred: 0,
|
|
270
|
+
errors: 0,
|
|
271
|
+
});
|
|
242
272
|
}
|
|
243
273
|
return;
|
|
244
274
|
}
|
|
@@ -251,6 +281,7 @@ export function registerSyncCommands(program) {
|
|
|
251
281
|
dryRun: true,
|
|
252
282
|
uploads: diff.uploads.length,
|
|
253
283
|
deletes: diff.deletes.length,
|
|
284
|
+
unchanged,
|
|
254
285
|
totalBytes: diff.totalBytes,
|
|
255
286
|
});
|
|
256
287
|
}
|
|
@@ -277,6 +308,7 @@ export function registerSyncCommands(program) {
|
|
|
277
308
|
out.success('', {
|
|
278
309
|
uploaded: result.filesUploaded,
|
|
279
310
|
deleted: result.filesDeleted,
|
|
311
|
+
unchanged,
|
|
280
312
|
bytesTransferred: result.bytesTransferred,
|
|
281
313
|
errors: result.errors.length,
|
|
282
314
|
});
|
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)}`, {
|