@lifestreamdynamics/vault-cli 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +759 -0
  3. package/dist/client.d.ts +12 -0
  4. package/dist/client.js +79 -0
  5. package/dist/commands/admin.d.ts +2 -0
  6. package/dist/commands/admin.js +263 -0
  7. package/dist/commands/audit.d.ts +2 -0
  8. package/dist/commands/audit.js +119 -0
  9. package/dist/commands/auth.d.ts +2 -0
  10. package/dist/commands/auth.js +256 -0
  11. package/dist/commands/config.d.ts +2 -0
  12. package/dist/commands/config.js +130 -0
  13. package/dist/commands/connectors.d.ts +2 -0
  14. package/dist/commands/connectors.js +224 -0
  15. package/dist/commands/docs.d.ts +2 -0
  16. package/dist/commands/docs.js +194 -0
  17. package/dist/commands/hooks.d.ts +2 -0
  18. package/dist/commands/hooks.js +159 -0
  19. package/dist/commands/keys.d.ts +2 -0
  20. package/dist/commands/keys.js +165 -0
  21. package/dist/commands/publish.d.ts +2 -0
  22. package/dist/commands/publish.js +138 -0
  23. package/dist/commands/search.d.ts +2 -0
  24. package/dist/commands/search.js +61 -0
  25. package/dist/commands/shares.d.ts +2 -0
  26. package/dist/commands/shares.js +121 -0
  27. package/dist/commands/subscription.d.ts +2 -0
  28. package/dist/commands/subscription.js +166 -0
  29. package/dist/commands/sync.d.ts +2 -0
  30. package/dist/commands/sync.js +565 -0
  31. package/dist/commands/teams.d.ts +2 -0
  32. package/dist/commands/teams.js +322 -0
  33. package/dist/commands/user.d.ts +2 -0
  34. package/dist/commands/user.js +48 -0
  35. package/dist/commands/vaults.d.ts +2 -0
  36. package/dist/commands/vaults.js +157 -0
  37. package/dist/commands/versions.d.ts +2 -0
  38. package/dist/commands/versions.js +219 -0
  39. package/dist/commands/webhooks.d.ts +2 -0
  40. package/dist/commands/webhooks.js +181 -0
  41. package/dist/config.d.ts +24 -0
  42. package/dist/config.js +88 -0
  43. package/dist/index.d.ts +2 -0
  44. package/dist/index.js +63 -0
  45. package/dist/lib/credential-manager.d.ts +48 -0
  46. package/dist/lib/credential-manager.js +101 -0
  47. package/dist/lib/encrypted-config.d.ts +20 -0
  48. package/dist/lib/encrypted-config.js +102 -0
  49. package/dist/lib/keychain.d.ts +8 -0
  50. package/dist/lib/keychain.js +82 -0
  51. package/dist/lib/migration.d.ts +31 -0
  52. package/dist/lib/migration.js +92 -0
  53. package/dist/lib/profiles.d.ts +43 -0
  54. package/dist/lib/profiles.js +104 -0
  55. package/dist/sync/config.d.ts +32 -0
  56. package/dist/sync/config.js +100 -0
  57. package/dist/sync/conflict.d.ts +30 -0
  58. package/dist/sync/conflict.js +60 -0
  59. package/dist/sync/daemon-worker.d.ts +1 -0
  60. package/dist/sync/daemon-worker.js +128 -0
  61. package/dist/sync/daemon.d.ts +44 -0
  62. package/dist/sync/daemon.js +174 -0
  63. package/dist/sync/diff.d.ts +43 -0
  64. package/dist/sync/diff.js +166 -0
  65. package/dist/sync/engine.d.ts +41 -0
  66. package/dist/sync/engine.js +233 -0
  67. package/dist/sync/ignore.d.ts +16 -0
  68. package/dist/sync/ignore.js +72 -0
  69. package/dist/sync/remote-poller.d.ts +23 -0
  70. package/dist/sync/remote-poller.js +145 -0
  71. package/dist/sync/state.d.ts +32 -0
  72. package/dist/sync/state.js +98 -0
  73. package/dist/sync/types.d.ts +68 -0
  74. package/dist/sync/types.js +4 -0
  75. package/dist/sync/watcher.d.ts +23 -0
  76. package/dist/sync/watcher.js +207 -0
  77. package/dist/utils/flags.d.ts +18 -0
  78. package/dist/utils/flags.js +31 -0
  79. package/dist/utils/format.d.ts +2 -0
  80. package/dist/utils/format.js +22 -0
  81. package/dist/utils/output.d.ts +87 -0
  82. package/dist/utils/output.js +229 -0
  83. package/package.json +62 -0
@@ -0,0 +1,61 @@
1
+ import chalk from 'chalk';
2
+ import { getClient } from '../client.js';
3
+ import { addGlobalFlags, resolveFlags } from '../utils/flags.js';
4
+ import { createOutput, handleError } from '../utils/output.js';
5
+ export function registerSearchCommands(program) {
6
+ addGlobalFlags(program.command('search')
7
+ .description('Full-text search across all documents')
8
+ .argument('<query>', 'Search query (PostgreSQL websearch syntax)')
9
+ .option('--vault <vaultId>', 'Limit search to a specific vault')
10
+ .option('--tags <tags>', 'Filter by tags (comma-separated)')
11
+ .option('--limit <n>', 'Maximum number of results', '20')
12
+ .addHelpText('after', `
13
+ EXAMPLES
14
+ lsvault search "meeting notes"
15
+ lsvault search "project plan" --vault abc123
16
+ lsvault search "typescript" --tags dev,code --limit 5`))
17
+ .action(async (query, _opts) => {
18
+ const flags = resolveFlags(_opts);
19
+ const out = createOutput(flags);
20
+ out.startSpinner('Searching...');
21
+ try {
22
+ const client = getClient();
23
+ const response = await client.search.search({
24
+ q: query,
25
+ vault: _opts.vault,
26
+ tags: _opts.tags,
27
+ limit: parseInt(String(_opts.limit || '20'), 10),
28
+ });
29
+ out.stopSpinner();
30
+ if (flags.output === 'text') {
31
+ out.status(chalk.dim(`${response.total} result(s) for "${response.query}":\n`));
32
+ }
33
+ out.list(response.results.map(r => ({
34
+ title: r.title || r.path,
35
+ path: r.path,
36
+ vaultName: r.vaultName,
37
+ tags: r.tags.join(', '),
38
+ snippet: r.snippet ? r.snippet.replace(/<[^>]+>/g, '') : '',
39
+ })), {
40
+ emptyMessage: 'No results found.',
41
+ columns: [
42
+ { key: 'title', header: 'Title' },
43
+ { key: 'path', header: 'Path' },
44
+ { key: 'vaultName', header: 'Vault' },
45
+ { key: 'tags', header: 'Tags' },
46
+ ],
47
+ textFn: (r) => {
48
+ const tags = r.tags ? chalk.blue(` [${String(r.tags)}]`) : '';
49
+ const lines = [`${chalk.cyan(String(r.title))}${tags}`];
50
+ lines.push(` ${chalk.dim(String(r.vaultName))} / ${chalk.dim(String(r.path))}`);
51
+ if (r.snippet)
52
+ lines.push(` ${String(r.snippet)}`);
53
+ return lines.join('\n');
54
+ },
55
+ });
56
+ }
57
+ catch (err) {
58
+ handleError(out, err, 'Search failed');
59
+ }
60
+ });
61
+ }
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerShareCommands(program: Command): void;
@@ -0,0 +1,121 @@
1
+ import chalk from 'chalk';
2
+ import { getClient } from '../client.js';
3
+ import { addGlobalFlags, resolveFlags } from '../utils/flags.js';
4
+ import { createOutput, handleError } from '../utils/output.js';
5
+ export function registerShareCommands(program) {
6
+ const shares = program.command('shares').description('Create, list, and revoke document share links');
7
+ addGlobalFlags(shares.command('list')
8
+ .description('List share links for a document')
9
+ .argument('<vaultId>', 'Vault ID')
10
+ .argument('<docPath>', 'Document path (e.g., notes/meeting.md)'))
11
+ .action(async (vaultId, docPath, _opts) => {
12
+ const flags = resolveFlags(_opts);
13
+ const out = createOutput(flags);
14
+ out.startSpinner('Fetching share links...');
15
+ try {
16
+ const client = getClient();
17
+ const links = await client.shares.list(vaultId, docPath);
18
+ out.stopSpinner();
19
+ out.list(links.map(link => ({
20
+ id: link.id,
21
+ tokenPrefix: link.tokenPrefix,
22
+ permission: link.permission,
23
+ viewCount: link.viewCount,
24
+ maxViews: link.maxViews || null,
25
+ isActive: link.isActive,
26
+ expiresAt: link.expiresAt || null,
27
+ createdAt: link.createdAt,
28
+ })), {
29
+ emptyMessage: 'No share links found for this document.',
30
+ columns: [
31
+ { key: 'tokenPrefix', header: 'Token' },
32
+ { key: 'permission', header: 'Permission' },
33
+ { key: 'viewCount', header: 'Views' },
34
+ { key: 'isActive', header: 'Active' },
35
+ ],
36
+ textFn: (link) => {
37
+ const lines = [chalk.cyan(` ${String(link.tokenPrefix)}...`)];
38
+ lines.push(` ID: ${String(link.id)}`);
39
+ lines.push(` Permission: ${String(link.permission)}`);
40
+ lines.push(` Views: ${String(link.viewCount)}${link.maxViews ? `/${String(link.maxViews)}` : ''}`);
41
+ lines.push(` Active: ${link.isActive ? chalk.green('Yes') : chalk.red('No')}`);
42
+ if (link.expiresAt)
43
+ lines.push(` Expires: ${new Date(String(link.expiresAt)).toLocaleString()}`);
44
+ lines.push(` Created: ${new Date(String(link.createdAt)).toLocaleString()}`);
45
+ return lines.join('\n');
46
+ },
47
+ });
48
+ }
49
+ catch (err) {
50
+ handleError(out, err, 'Failed to fetch share links');
51
+ }
52
+ });
53
+ addGlobalFlags(shares.command('create')
54
+ .description('Create a share link for a document')
55
+ .argument('<vaultId>', 'Vault ID')
56
+ .argument('<docPath>', 'Document path (e.g., notes/meeting.md)')
57
+ .option('--permission <perm>', 'Permission level: view or edit', 'view')
58
+ .option('--password <password>', 'Password to protect the link')
59
+ .option('--expires <date>', 'Expiration date (ISO 8601)')
60
+ .option('--max-views <count>', 'Maximum number of views'))
61
+ .action(async (vaultId, docPath, _opts) => {
62
+ const flags = resolveFlags(_opts);
63
+ const out = createOutput(flags);
64
+ out.startSpinner('Creating share link...');
65
+ try {
66
+ const client = getClient();
67
+ const params = {};
68
+ if (_opts.permission)
69
+ params.permission = String(_opts.permission);
70
+ if (_opts.password)
71
+ params.password = String(_opts.password);
72
+ if (_opts.expires)
73
+ params.expiresAt = String(_opts.expires);
74
+ if (_opts.maxViews)
75
+ params.maxViews = parseInt(String(_opts.maxViews), 10);
76
+ const result = await client.shares.create(vaultId, docPath, params);
77
+ out.stopSpinner();
78
+ if (flags.output === 'json') {
79
+ out.record({
80
+ token: result.fullToken,
81
+ id: result.shareLink.id,
82
+ permission: result.shareLink.permission,
83
+ expiresAt: result.shareLink.expiresAt || null,
84
+ maxViews: result.shareLink.maxViews || null,
85
+ });
86
+ }
87
+ else {
88
+ out.warn('\nIMPORTANT: Save this token. It cannot be retrieved later.\n');
89
+ process.stdout.write(chalk.green.bold(`Token: ${result.fullToken}\n`));
90
+ process.stdout.write(`\nID: ${result.shareLink.id}\n`);
91
+ process.stdout.write(`Permission: ${result.shareLink.permission}\n`);
92
+ if (result.shareLink.expiresAt) {
93
+ process.stdout.write(`Expires: ${new Date(result.shareLink.expiresAt).toLocaleString()}\n`);
94
+ }
95
+ if (result.shareLink.maxViews) {
96
+ process.stdout.write(`Max views: ${result.shareLink.maxViews}\n`);
97
+ }
98
+ }
99
+ }
100
+ catch (err) {
101
+ handleError(out, err, 'Failed to create share link');
102
+ }
103
+ });
104
+ addGlobalFlags(shares.command('revoke')
105
+ .description('Revoke a share link')
106
+ .argument('<vaultId>', 'Vault ID')
107
+ .argument('<shareId>', 'Share link ID'))
108
+ .action(async (vaultId, shareId, _opts) => {
109
+ const flags = resolveFlags(_opts);
110
+ const out = createOutput(flags);
111
+ out.startSpinner('Revoking share link...');
112
+ try {
113
+ const client = getClient();
114
+ await client.shares.revoke(vaultId, shareId);
115
+ out.success('Share link revoked successfully', { id: shareId, revoked: true });
116
+ }
117
+ catch (err) {
118
+ handleError(out, err, 'Failed to revoke share link');
119
+ }
120
+ });
121
+ }
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerSubscriptionCommands(program: Command): void;
@@ -0,0 +1,166 @@
1
+ import chalk from 'chalk';
2
+ import { getClient } from '../client.js';
3
+ import { addGlobalFlags, resolveFlags } from '../utils/flags.js';
4
+ import { createOutput, handleError } from '../utils/output.js';
5
+ import { formatBytes } from '../utils/format.js';
6
+ export function registerSubscriptionCommands(program) {
7
+ const sub = program.command('subscription').description('View plans, manage subscription, and access billing');
8
+ addGlobalFlags(sub.command('status')
9
+ .description('Show current plan, active status, and resource usage'))
10
+ .action(async (_opts) => {
11
+ const flags = resolveFlags(_opts);
12
+ const out = createOutput(flags);
13
+ out.startSpinner('Fetching subscription...');
14
+ try {
15
+ const client = getClient();
16
+ const data = await client.subscription.get();
17
+ out.stopSpinner();
18
+ if (flags.output === 'json') {
19
+ out.record({
20
+ tier: data.subscription.tier,
21
+ isActive: data.subscription.isActive,
22
+ expiresAt: data.subscription.expiresAt,
23
+ ...data.usage,
24
+ });
25
+ }
26
+ else {
27
+ const s = data.subscription;
28
+ process.stdout.write(`Plan: ${chalk.green(s.tier)}\n`);
29
+ process.stdout.write(`Active: ${s.isActive ? chalk.green('yes') : chalk.red('no')}\n`);
30
+ process.stdout.write(`Expires: ${s.expiresAt || chalk.dim('never')}\n`);
31
+ process.stdout.write('\n');
32
+ process.stdout.write(chalk.dim('Usage:') + '\n');
33
+ process.stdout.write(` Vaults: ${data.usage.vaultCount}\n`);
34
+ process.stdout.write(` Storage: ${formatBytes(data.usage.totalStorageBytes)}\n`);
35
+ process.stdout.write(` API calls today: ${data.usage.apiCallsToday}\n`);
36
+ process.stdout.write(` AI tokens: ${data.usage.aiTokens}\n`);
37
+ process.stdout.write(` Hook executions: ${data.usage.hookExecutions}\n`);
38
+ process.stdout.write(` Webhook deliveries: ${data.usage.webhookDeliveries}\n`);
39
+ }
40
+ }
41
+ catch (err) {
42
+ handleError(out, err, 'Failed to fetch subscription');
43
+ }
44
+ });
45
+ addGlobalFlags(sub.command('plans')
46
+ .description('List available subscription plans and their limits'))
47
+ .action(async (_opts) => {
48
+ const flags = resolveFlags(_opts);
49
+ const out = createOutput(flags);
50
+ out.startSpinner('Fetching plans...');
51
+ try {
52
+ const client = getClient();
53
+ const plans = await client.subscription.listPlans();
54
+ out.stopSpinner();
55
+ out.list(plans.map(p => ({ name: p.name, tier: p.tier, ...p.limits })), {
56
+ emptyMessage: 'No plans available.',
57
+ columns: [
58
+ { key: 'name', header: 'Name' },
59
+ { key: 'tier', header: 'Tier' },
60
+ ],
61
+ textFn: (p) => {
62
+ const lines = [`${chalk.cyan(String(p.name))} (${String(p.tier)})`];
63
+ for (const [key, val] of Object.entries(p)) {
64
+ if (key !== 'name' && key !== 'tier') {
65
+ lines.push(` ${key}: ${String(val)}`);
66
+ }
67
+ }
68
+ return lines.join('\n');
69
+ },
70
+ });
71
+ }
72
+ catch (err) {
73
+ handleError(out, err, 'Failed to fetch plans');
74
+ }
75
+ });
76
+ addGlobalFlags(sub.command('upgrade')
77
+ .description('Start a checkout session to upgrade your plan')
78
+ .argument('<tier>', 'Target tier: pro or business')
79
+ .requiredOption('--return-url <url>', 'URL to redirect after checkout')
80
+ .addHelpText('after', `
81
+ EXAMPLES
82
+ lsvault subscription upgrade pro --return-url https://app.example.com/settings`))
83
+ .action(async (tier, _opts) => {
84
+ const flags = resolveFlags(_opts);
85
+ const out = createOutput(flags);
86
+ out.startSpinner('Creating checkout session...');
87
+ try {
88
+ const client = getClient();
89
+ const session = await client.subscription.createCheckoutSession(tier, String(_opts.returnUrl));
90
+ out.success('Checkout session created', { url: session.url });
91
+ if (flags.output === 'text') {
92
+ process.stdout.write(`Open this URL to complete upgrade: ${chalk.cyan(session.url)}\n`);
93
+ }
94
+ }
95
+ catch (err) {
96
+ handleError(out, err, 'Failed to create checkout session');
97
+ }
98
+ });
99
+ addGlobalFlags(sub.command('cancel')
100
+ .description('Cancel your current subscription')
101
+ .option('--reason <text>', 'Reason for cancellation'))
102
+ .action(async (_opts) => {
103
+ const flags = resolveFlags(_opts);
104
+ const out = createOutput(flags);
105
+ out.startSpinner('Cancelling subscription...');
106
+ try {
107
+ const client = getClient();
108
+ await client.subscription.cancel(_opts.reason);
109
+ out.success('Subscription cancelled', { cancelled: true });
110
+ }
111
+ catch (err) {
112
+ handleError(out, err, 'Failed to cancel subscription');
113
+ }
114
+ });
115
+ addGlobalFlags(sub.command('portal')
116
+ .description('Get a URL to the billing management portal')
117
+ .requiredOption('--return-url <url>', 'URL to redirect after portal session'))
118
+ .action(async (_opts) => {
119
+ const flags = resolveFlags(_opts);
120
+ const out = createOutput(flags);
121
+ out.startSpinner('Creating portal session...');
122
+ try {
123
+ const client = getClient();
124
+ const portal = await client.subscription.createPortalSession(String(_opts.returnUrl));
125
+ out.success('Portal session created', { url: portal.url });
126
+ if (flags.output === 'text') {
127
+ process.stdout.write(`Open this URL to manage billing: ${chalk.cyan(portal.url)}\n`);
128
+ }
129
+ }
130
+ catch (err) {
131
+ handleError(out, err, 'Failed to create portal session');
132
+ }
133
+ });
134
+ addGlobalFlags(sub.command('invoices')
135
+ .description('List past invoices and payment history'))
136
+ .action(async (_opts) => {
137
+ const flags = resolveFlags(_opts);
138
+ const out = createOutput(flags);
139
+ out.startSpinner('Fetching invoices...');
140
+ try {
141
+ const client = getClient();
142
+ const invoices = await client.subscription.listInvoices();
143
+ out.stopSpinner();
144
+ out.list(invoices.map(inv => ({
145
+ date: inv.createdAt,
146
+ amount: `${(inv.amount / 100).toFixed(2)} ${inv.currency.toUpperCase()}`,
147
+ status: inv.status,
148
+ invoiceUrl: inv.invoiceUrl || null,
149
+ })), {
150
+ emptyMessage: 'No invoices found.',
151
+ columns: [
152
+ { key: 'date', header: 'Date' },
153
+ { key: 'amount', header: 'Amount' },
154
+ { key: 'status', header: 'Status' },
155
+ ],
156
+ textFn: (inv) => {
157
+ const status = String(inv.status) === 'paid' ? chalk.green(String(inv.status)) : chalk.yellow(String(inv.status));
158
+ return `${String(inv.date)} ${String(inv.amount)} ${status}${inv.invoiceUrl ? ` ${chalk.dim(String(inv.invoiceUrl))}` : ''}`;
159
+ },
160
+ });
161
+ }
162
+ catch (err) {
163
+ handleError(out, err, 'Failed to fetch invoices');
164
+ }
165
+ });
166
+ }
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerSyncCommands(program: Command): void;