@lifestreamdynamics/vault-cli 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  A powerful command-line interface for Lifestream Vault - the multi-user Markdown document storage service with WebDAV sync, search, and collaboration features.
4
4
 
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
6
- [![npm version](https://img.shields.io/npm/v/@lifestream-vault/cli.svg)](https://www.npmjs.com/package/@lifestream-vault/cli)
6
+ [![npm version](https://img.shields.io/npm/v/@lifestreamdynamics/vault-cli.svg)](https://www.npmjs.com/package/@lifestreamdynamics/vault-cli)
7
7
 
8
8
  ## 📖 Table of Contents
9
9
 
@@ -20,6 +20,7 @@ A powerful command-line interface for Lifestream Vault - the multi-user Markdown
20
20
  - [Team Commands](#team-commands)
21
21
  - [Sharing & Publishing](#sharing--publishing)
22
22
  - [Hooks & Webhooks](#hooks--webhooks)
23
+ - [Links & Backlinks](#links--backlinks)
23
24
  - [Admin Commands](#admin-commands)
24
25
  - [Sync & Watch Mode](#-sync--watch-mode)
25
26
  - [Configuration](#️-configuration)
@@ -49,14 +50,14 @@ A powerful command-line interface for Lifestream Vault - the multi-user Markdown
49
50
  - **🛡️ Admin Tools** - User management, system stats, and audit logs (admin only)
50
51
  - **⚙️ Flexible Configuration** - Multiple profiles, environment variable support, and configurable sync behavior
51
52
  - **🌐 Offline Support** - Work offline and sync when reconnected
52
- - **📦 TypeScript SDK** - Built on `@lifestream-vault/sdk` with full type safety
53
+ - **📦 TypeScript SDK** - Built on `@lifestreamdynamics/vault-sdk` with full type safety
53
54
 
54
55
  ## 📦 Installation
55
56
 
56
57
  ### Global Installation (Recommended)
57
58
 
58
59
  ```bash
59
- npm install -g @lifestream-vault/cli
60
+ npm install -g @lifestreamdynamics/vault-cli
60
61
  ```
61
62
 
62
63
  After installation, the `lsvault` command will be available globally:
@@ -68,7 +69,7 @@ lsvault --help
68
69
  ### Local Installation (Project-Specific)
69
70
 
70
71
  ```bash
71
- npm install @lifestream-vault/cli
72
+ npm install @lifestreamdynamics/vault-cli
72
73
 
73
74
  # Run with npx
74
75
  npx lsvault --help
@@ -98,7 +99,7 @@ lsvault auth login --api-key lsv_k_your_api_key_here
98
99
  lsvault auth login --email user@example.com
99
100
 
100
101
  # Set a custom API URL (optional)
101
- lsvault auth login --api-key lsv_k_your_key --api-url https://vault.example.com
102
+ lsvault auth login --api-key lsv_k_your_key --api-url https://vault.lifestreamdynamics.com
102
103
  ```
103
104
 
104
105
  ### 2. List Your Vaults
@@ -372,6 +373,41 @@ lsvault webhooks create \
372
373
  --secret webhook_secret_key
373
374
  ```
374
375
 
376
+ ### Calendar
377
+
378
+ | Command | Description |
379
+ |---------|-------------|
380
+ | `lsvault calendar view <vaultId>` | Browse calendar views and activity heatmap |
381
+ | `lsvault calendar due <vaultId>` | List documents by due date |
382
+ | `lsvault calendar events <vaultId>` | List calendar events |
383
+ | `lsvault calendar create-event <vaultId>` | Create a calendar event |
384
+ | `lsvault calendar update-event <vaultId> <eventId>` | Update a calendar event |
385
+ | `lsvault calendar delete-event <vaultId> <eventId>` | Delete a calendar event |
386
+
387
+ ### Links & Backlinks
388
+
389
+ | Command | Description |
390
+ |---------|-------------|
391
+ | `lsvault links list <vaultId> <path>` | List forward links from a document |
392
+ | `lsvault links backlinks <vaultId> <path>` | List backlinks pointing to a document |
393
+ | `lsvault links graph <vaultId>` | Get the link graph for a vault |
394
+ | `lsvault links broken <vaultId>` | List unresolved (broken) links in a vault |
395
+
396
+ **Example:**
397
+ ```bash
398
+ # List forward links from a document
399
+ lsvault links list vault_abc123 notes/index.md
400
+
401
+ # Find all documents linking to a specific document
402
+ lsvault links backlinks vault_abc123 notes/important.md
403
+
404
+ # Get the full link graph for visualization
405
+ lsvault links graph vault_abc123 --output json > graph.json
406
+
407
+ # Find broken links
408
+ lsvault links broken vault_abc123
409
+ ```
410
+
375
411
  ### Admin Commands
376
412
 
377
413
  **Note:** Admin commands require admin role.
@@ -474,12 +510,14 @@ The CLI stores configuration in `~/.lsvault/config.json`:
474
510
 
475
511
  ```json
476
512
  {
477
- "apiUrl": "http://localhost:4660"
513
+ "apiUrl": "https://vault.lifestreamdynamics.com"
478
514
  }
479
515
  ```
480
516
 
481
517
  **Note:** Credentials are stored securely in the system keychain (or encrypted file fallback), not in the plaintext config file.
482
518
 
519
+ > **Self-hosting?** Replace `https://vault.lifestreamdynamics.com` with your server's URL, or set the `LSVAULT_API_URL` environment variable.
520
+
483
521
  ### Configuration Profiles
484
522
 
485
523
  Manage multiple configurations with profiles:
@@ -489,13 +527,13 @@ Manage multiple configurations with profiles:
489
527
  lsvault config profiles
490
528
 
491
529
  # Create a profile
492
- lsvault config create-profile production --api-url https://vault.example.com
530
+ lsvault config create-profile production --api-url https://vault.lifestreamdynamics.com
493
531
 
494
532
  # Switch profiles
495
533
  lsvault config use production
496
534
 
497
535
  # Set config values
498
- lsvault config set apiUrl https://vault.example.com
536
+ lsvault config set apiUrl https://vault.lifestreamdynamics.com
499
537
 
500
538
  # Get config values
501
539
  lsvault config get apiUrl
@@ -522,14 +560,14 @@ Sync configurations are stored per vault in `~/.lsvault/sync/`:
522
560
 
523
561
  | Variable | Description | Default |
524
562
  |----------|-------------|---------|
525
- | `LSVAULT_API_URL` | API server base URL | `http://localhost:4660` |
563
+ | `LSVAULT_API_URL` | API server base URL | `https://vault.lifestreamdynamics.com` |
526
564
  | `LSVAULT_API_KEY` | API key for authentication | - |
527
565
  | `LSVAULT_CONFIG_DIR` | Configuration directory | `~/.lsvault` |
528
566
  | `LSVAULT_PROFILE` | Active configuration profile | `default` |
529
567
 
530
568
  **Example:**
531
569
  ```bash
532
- export LSVAULT_API_URL=https://vault.example.com
570
+ export LSVAULT_API_URL=https://vault.lifestreamdynamics.com
533
571
  export LSVAULT_API_KEY=lsv_k_your_key_here
534
572
  lsvault vaults list
535
573
  ```
@@ -718,7 +756,7 @@ lsvault auth migrate
718
756
  1. Verify API URL is correct: `lsvault config get apiUrl`
719
757
  2. Test connectivity: `curl <API_URL>/api/v1/health`
720
758
  3. Check firewall/proxy settings
721
- 4. Use custom API URL: `lsvault auth login --api-url https://vault.example.com`
759
+ 4. Use custom API URL: `lsvault auth login --api-url https://vault.lifestreamdynamics.com`
722
760
 
723
761
  ### Output Format Issues
724
762
 
@@ -733,9 +771,9 @@ lsvault docs get vault_abc123 /path.md --quiet > output.md
733
771
 
734
772
  ## 🔗 Related Packages
735
773
 
736
- - **[@lifestream-vault/sdk](https://npmjs.com/package/@lifestream-vault/sdk)** - TypeScript SDK for Lifestream Vault API
737
- - **[@lifestream-vault/api](https://github.com/lifestreamdynamics/lifestream-vault)** - Backend API server
738
- - **[@lifestream-vault/web](https://github.com/lifestreamdynamics/lifestream-vault)** - Web frontend
774
+ - **[@lifestreamdynamics/vault-sdk](https://npmjs.com/package/@lifestreamdynamics/vault-sdk)** - TypeScript SDK for Lifestream Vault API
775
+ - **[@lifestreamdynamics/vault-api](https://github.com/lifestreamdynamics/lifestream-vault)** - Backend API server
776
+ - **[@lifestreamdynamics/vault-web](https://github.com/lifestreamdynamics/lifestream-vault)** - Web frontend
739
777
 
740
778
  ## 📄 Documentation
741
779
 
@@ -1,5 +1,5 @@
1
1
  import chalk from 'chalk';
2
- import { getClient } from '../client.js';
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
5
  import { formatBytes, formatUptime } from '../utils/format.js';
@@ -12,7 +12,7 @@ export function registerAdminCommands(program) {
12
12
  const out = createOutput(flags);
13
13
  out.startSpinner('Fetching system stats...');
14
14
  try {
15
- const client = getClient();
15
+ const client = await getClientAsync();
16
16
  const data = await client.admin.getStats();
17
17
  out.stopSpinner();
18
18
  out.record({
@@ -41,7 +41,7 @@ export function registerAdminCommands(program) {
41
41
  const out = createOutput(flags);
42
42
  out.startSpinner('Fetching timeseries data...');
43
43
  try {
44
- const client = getClient();
44
+ const client = await getClientAsync();
45
45
  const data = await client.admin.getTimeseries(String(_opts.metric), String(_opts.period));
46
46
  out.stopSpinner();
47
47
  if (flags.output === 'text') {
@@ -79,7 +79,7 @@ export function registerAdminCommands(program) {
79
79
  const out = createOutput(flags);
80
80
  out.startSpinner('Fetching users...');
81
81
  try {
82
- const client = getClient();
82
+ const client = await getClientAsync();
83
83
  const result = await client.admin.listUsers({
84
84
  page: _opts.page,
85
85
  limit: _opts.limit,
@@ -125,7 +125,7 @@ export function registerAdminCommands(program) {
125
125
  const out = createOutput(flags);
126
126
  out.startSpinner('Fetching user...');
127
127
  try {
128
- const client = getClient();
128
+ const client = await getClientAsync();
129
129
  const user = await client.admin.getUser(userId);
130
130
  out.stopSpinner();
131
131
  out.record({
@@ -169,7 +169,7 @@ export function registerAdminCommands(program) {
169
169
  }
170
170
  out.startSpinner('Updating user...');
171
171
  try {
172
- const client = getClient();
172
+ const client = await getClientAsync();
173
173
  const updated = await client.admin.updateUser(userId, params);
174
174
  out.success(`User updated: ${chalk.cyan(updated.email)} -- ${chalk.magenta(updated.role)} -- ${updated.isActive ? chalk.green('active') : chalk.red('inactive')}`, {
175
175
  email: updated.email,
@@ -190,7 +190,7 @@ export function registerAdminCommands(program) {
190
190
  const out = createOutput(flags);
191
191
  out.startSpinner('Fetching activity...');
192
192
  try {
193
- const client = getClient();
193
+ const client = await getClientAsync();
194
194
  const activity = await client.admin.getActivity(_opts.limit);
195
195
  out.stopSpinner();
196
196
  out.list(activity.map(a => ({
@@ -224,7 +224,7 @@ export function registerAdminCommands(program) {
224
224
  const out = createOutput(flags);
225
225
  out.startSpinner('Fetching subscription summary...');
226
226
  try {
227
- const client = getClient();
227
+ const client = await getClientAsync();
228
228
  const summary = await client.admin.getSubscriptionSummary();
229
229
  out.stopSpinner();
230
230
  out.record({
@@ -246,7 +246,7 @@ export function registerAdminCommands(program) {
246
246
  const out = createOutput(flags);
247
247
  out.startSpinner('Checking system health...');
248
248
  try {
249
- const client = getClient();
249
+ const client = await getClientAsync();
250
250
  const health = await client.admin.getHealth();
251
251
  out.stopSpinner();
252
252
  out.record({
@@ -2,7 +2,7 @@ import chalk from 'chalk';
2
2
  import ora from 'ora';
3
3
  import { LifestreamVaultClient } from '@lifestreamdynamics/vault-sdk';
4
4
  import { loadConfigAsync, getCredentialManager } from '../config.js';
5
- import { getClient } from '../client.js';
5
+ import { getClientAsync } from '../client.js';
6
6
  import { migrateCredentials, hasPlaintextCredentials, checkAndPromptMigration } from '../lib/migration.js';
7
7
  export function registerAuthCommands(program) {
8
8
  const auth = program.command('auth').description('Authentication and credential management');
@@ -11,11 +11,13 @@ export function registerAuthCommands(program) {
11
11
  .option('--api-key <key>', 'API key (lsv_k_... prefix)')
12
12
  .option('--email <email>', 'Email address for password login')
13
13
  .option('--password <password>', 'Password (prompts interactively if omitted)')
14
- .option('--api-url <url>', 'API server URL (default: http://localhost:4660)')
14
+ .option('--mfa-code <code>', 'MFA code (TOTP or backup code) if account has MFA enabled')
15
+ .option('--api-url <url>', 'API server URL (default: https://vault.lifestreamdynamics.com)')
15
16
  .addHelpText('after', `
16
17
  EXAMPLES
17
18
  lsvault auth login --api-key lsv_k_abc123
18
19
  lsvault auth login --email user@example.com
20
+ lsvault auth login --email user@example.com --mfa-code 123456
19
21
  lsvault auth login --email user@example.com --api-url https://api.example.com`)
20
22
  .action(async (opts) => {
21
23
  const cm = getCredentialManager();
@@ -42,7 +44,20 @@ EXAMPLES
42
44
  const apiUrl = opts.apiUrl ?? config.apiUrl;
43
45
  const spinner = ora('Authenticating...').start();
44
46
  try {
45
- const { tokens, refreshToken } = await LifestreamVaultClient.login(apiUrl, opts.email, password);
47
+ const { tokens, refreshToken } = await LifestreamVaultClient.login(apiUrl, opts.email, password, {}, {
48
+ mfaCode: opts.mfaCode,
49
+ onMfaRequired: async (challenge) => {
50
+ spinner.stop();
51
+ console.log(chalk.yellow('MFA required for this account.'));
52
+ console.log(`Available methods: ${challenge.methods.join(', ')}`);
53
+ const code = await promptMfaCode();
54
+ if (!code) {
55
+ throw new Error('MFA code is required');
56
+ }
57
+ spinner.start('Verifying MFA code...');
58
+ return { method: 'totp', code };
59
+ },
60
+ });
46
61
  // Save tokens to secure storage
47
62
  await cm.saveCredentials({
48
63
  accessToken: tokens.accessToken,
@@ -179,7 +194,7 @@ EXAMPLES
179
194
  if (config.apiKey || config.accessToken) {
180
195
  const spinner = ora('Fetching user info...').start();
181
196
  try {
182
- const client = getClient();
197
+ const client = await getClientAsync();
183
198
  const user = await client.user.me();
184
199
  spinner.stop();
185
200
  console.log(`User: ${chalk.cyan(user.email)}`);
@@ -245,6 +260,57 @@ async function promptPassword() {
245
260
  process.stdin.resume();
246
261
  });
247
262
  }
263
+ /**
264
+ * Prompt for an MFA code from stdin (6 digits, non-echoing).
265
+ * Returns the code or null if stdin is not a TTY.
266
+ */
267
+ async function promptMfaCode() {
268
+ // In non-interactive mode, cannot prompt
269
+ if (!process.stdin.isTTY) {
270
+ return null;
271
+ }
272
+ const readline = await import('node:readline');
273
+ return new Promise((resolve) => {
274
+ const rl = readline.createInterface({
275
+ input: process.stdin,
276
+ output: process.stderr,
277
+ terminal: true,
278
+ });
279
+ // Disable echoing
280
+ process.stderr.write('MFA code: ');
281
+ process.stdin.setRawMode?.(true);
282
+ let code = '';
283
+ const onData = (chunk) => {
284
+ const char = chunk.toString('utf-8');
285
+ if (char === '\n' || char === '\r' || char === '\u0004') {
286
+ process.stderr.write('\n');
287
+ process.stdin.setRawMode?.(false);
288
+ process.stdin.removeListener('data', onData);
289
+ rl.close();
290
+ resolve(code);
291
+ }
292
+ else if (char === '\u0003') {
293
+ // Ctrl+C
294
+ process.stderr.write('\n');
295
+ process.stdin.setRawMode?.(false);
296
+ process.stdin.removeListener('data', onData);
297
+ rl.close();
298
+ resolve(null);
299
+ }
300
+ else if (char === '\u007F' || char === '\b') {
301
+ // Backspace
302
+ if (code.length > 0) {
303
+ code = code.slice(0, -1);
304
+ }
305
+ }
306
+ else {
307
+ code += char;
308
+ }
309
+ };
310
+ process.stdin.on('data', onData);
311
+ process.stdin.resume();
312
+ });
313
+ }
248
314
  function formatMethod(method) {
249
315
  switch (method) {
250
316
  case 'keychain': return chalk.green('OS Keychain');
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerCalendarCommands(program: Command): void;
@@ -0,0 +1,167 @@
1
+ import chalk from 'chalk';
2
+ import { getClientAsync } from '../client.js';
3
+ import { addGlobalFlags, resolveFlags } from '../utils/flags.js';
4
+ import { createOutput, handleError } from '../utils/output.js';
5
+ export function registerCalendarCommands(program) {
6
+ const calendar = program.command('calendar').description('Document calendar and due date management');
7
+ // calendar view
8
+ addGlobalFlags(calendar.command('view')
9
+ .description('View calendar activity for a vault')
10
+ .argument('<vaultId>', 'Vault ID')
11
+ .option('--start <date>', 'Start date (YYYY-MM-DD)', getDefaultStart())
12
+ .option('--end <date>', 'End date (YYYY-MM-DD)', getDefaultEnd()))
13
+ .action(async (vaultId, _opts) => {
14
+ const flags = resolveFlags(_opts);
15
+ const out = createOutput(flags);
16
+ out.startSpinner('Loading calendar...');
17
+ try {
18
+ const client = await getClientAsync();
19
+ const response = await client.calendar.getActivity(vaultId, {
20
+ start: _opts.start,
21
+ end: _opts.end,
22
+ });
23
+ out.stopSpinner();
24
+ if (flags.output === 'text') {
25
+ out.status(chalk.dim(`Activity from ${response.start} to ${response.end}:\n`));
26
+ }
27
+ out.list(response.days.map(d => ({
28
+ date: d.date,
29
+ created: String(d.created),
30
+ updated: String(d.updated),
31
+ deleted: String(d.deleted),
32
+ total: String(d.total),
33
+ })), {
34
+ emptyMessage: 'No activity in this period.',
35
+ columns: [
36
+ { key: 'date', header: 'Date' },
37
+ { key: 'created', header: 'Created' },
38
+ { key: 'updated', header: 'Updated' },
39
+ { key: 'deleted', header: 'Deleted' },
40
+ { key: 'total', header: 'Total' },
41
+ ],
42
+ textFn: (d) => {
43
+ const bar = '█'.repeat(Math.min(Number(d.total), 20));
44
+ return `${chalk.dim(String(d.date))} ${chalk.green(bar)} ${chalk.bold(String(d.total))}`;
45
+ },
46
+ });
47
+ }
48
+ catch (err) {
49
+ handleError(out, err, 'Calendar view failed');
50
+ }
51
+ });
52
+ // calendar due
53
+ addGlobalFlags(calendar.command('due')
54
+ .description('List documents with due dates')
55
+ .argument('<vaultId>', 'Vault ID')
56
+ .option('--status <status>', 'Filter: overdue, upcoming, all', 'all'))
57
+ .action(async (vaultId, _opts) => {
58
+ const flags = resolveFlags(_opts);
59
+ const out = createOutput(flags);
60
+ out.startSpinner('Loading due dates...');
61
+ try {
62
+ const client = await getClientAsync();
63
+ const docs = await client.calendar.getDueDates(vaultId, {
64
+ status: _opts.status,
65
+ });
66
+ out.stopSpinner();
67
+ out.list(docs.map(d => ({
68
+ title: d.title || d.path,
69
+ path: d.path,
70
+ dueAt: d.dueAt,
71
+ priority: d.priority || '-',
72
+ status: d.overdue ? 'OVERDUE' : d.completed ? 'Done' : 'Pending',
73
+ })), {
74
+ emptyMessage: 'No documents with due dates.',
75
+ columns: [
76
+ { key: 'title', header: 'Title' },
77
+ { key: 'dueAt', header: 'Due' },
78
+ { key: 'priority', header: 'Priority' },
79
+ { key: 'status', header: 'Status' },
80
+ ],
81
+ textFn: (d) => {
82
+ const statusColor = d.status === 'OVERDUE' ? chalk.red : d.status === 'Done' ? chalk.green : chalk.yellow;
83
+ return `${chalk.cyan(String(d.title))} — due ${chalk.dim(String(d.dueAt))} ${statusColor(String(d.status))}`;
84
+ },
85
+ });
86
+ }
87
+ catch (err) {
88
+ handleError(out, err, 'Due dates failed');
89
+ }
90
+ });
91
+ // calendar set-due
92
+ addGlobalFlags(calendar.command('set-due')
93
+ .description('Set due date on a document')
94
+ .argument('<vaultId>', 'Vault ID')
95
+ .argument('<path>', 'Document path')
96
+ .requiredOption('--date <date>', 'Due date (YYYY-MM-DD or "clear")')
97
+ .option('--priority <priority>', 'Priority (low/medium/high)')
98
+ .option('--recurrence <recurrence>', 'Recurrence (daily/weekly/monthly/yearly)'))
99
+ .action(async (vaultId, path, _opts) => {
100
+ const flags = resolveFlags(_opts);
101
+ const out = createOutput(flags);
102
+ out.startSpinner('Setting due date...');
103
+ try {
104
+ const client = await getClientAsync();
105
+ const dateStr = _opts.date;
106
+ await client.calendar.setDocumentDue(vaultId, path, {
107
+ dueAt: dateStr === 'clear' ? null : new Date(dateStr).toISOString(),
108
+ priority: _opts.priority || null,
109
+ recurrence: _opts.recurrence || null,
110
+ });
111
+ out.stopSpinner();
112
+ out.status(dateStr === 'clear'
113
+ ? chalk.green(`Due date cleared for ${path}`)
114
+ : chalk.green(`Due date set to ${dateStr} for ${path}`));
115
+ }
116
+ catch (err) {
117
+ handleError(out, err, 'Set due date failed');
118
+ }
119
+ });
120
+ // calendar events
121
+ addGlobalFlags(calendar.command('events')
122
+ .description('List calendar events')
123
+ .argument('<vaultId>', 'Vault ID')
124
+ .option('--start <date>', 'Start date')
125
+ .option('--end <date>', 'End date'))
126
+ .action(async (vaultId, _opts) => {
127
+ const flags = resolveFlags(_opts);
128
+ const out = createOutput(flags);
129
+ out.startSpinner('Loading events...');
130
+ try {
131
+ const client = await getClientAsync();
132
+ const events = await client.calendar.listEvents(vaultId, {
133
+ start: _opts.start,
134
+ end: _opts.end,
135
+ });
136
+ out.stopSpinner();
137
+ out.list(events.map(e => ({
138
+ title: e.title,
139
+ startDate: e.startDate,
140
+ priority: e.priority || '-',
141
+ completed: e.completed ? '✓' : '-',
142
+ })), {
143
+ emptyMessage: 'No calendar events.',
144
+ columns: [
145
+ { key: 'title', header: 'Title' },
146
+ { key: 'startDate', header: 'Date' },
147
+ { key: 'priority', header: 'Priority' },
148
+ { key: 'completed', header: 'Done' },
149
+ ],
150
+ textFn: (e) => `${chalk.cyan(String(e.title))} — ${chalk.dim(String(e.startDate))}`,
151
+ });
152
+ }
153
+ catch (err) {
154
+ handleError(out, err, 'Calendar events failed');
155
+ }
156
+ });
157
+ }
158
+ function getDefaultStart() {
159
+ const d = new Date();
160
+ d.setDate(1);
161
+ return d.toISOString().split('T')[0];
162
+ }
163
+ function getDefaultEnd() {
164
+ const d = new Date();
165
+ d.setMonth(d.getMonth() + 1, 0);
166
+ return d.toISOString().split('T')[0];
167
+ }
@@ -1,5 +1,5 @@
1
1
  import chalk from 'chalk';
2
- import { getClient } from '../client.js';
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
5
  export function registerConnectorCommands(program) {
@@ -12,7 +12,7 @@ export function registerConnectorCommands(program) {
12
12
  const out = createOutput(flags);
13
13
  out.startSpinner('Fetching connectors...');
14
14
  try {
15
- const client = getClient();
15
+ const client = await getClientAsync();
16
16
  const connectorList = await client.connectors.list(_opts.vault);
17
17
  out.stopSpinner();
18
18
  out.list(connectorList.map(c => ({
@@ -48,7 +48,7 @@ export function registerConnectorCommands(program) {
48
48
  const out = createOutput(flags);
49
49
  out.startSpinner('Fetching connector...');
50
50
  try {
51
- const client = getClient();
51
+ const client = await getClientAsync();
52
52
  const c = await client.connectors.get(connectorId);
53
53
  out.stopSpinner();
54
54
  out.record({
@@ -81,7 +81,7 @@ export function registerConnectorCommands(program) {
81
81
  const out = createOutput(flags);
82
82
  out.startSpinner('Creating connector...');
83
83
  try {
84
- const client = getClient();
84
+ const client = await getClientAsync();
85
85
  const connector = await client.connectors.create({
86
86
  provider: provider,
87
87
  name,
@@ -109,7 +109,7 @@ export function registerConnectorCommands(program) {
109
109
  const out = createOutput(flags);
110
110
  out.startSpinner('Updating connector...');
111
111
  try {
112
- const client = getClient();
112
+ const client = await getClientAsync();
113
113
  const params = {};
114
114
  if (_opts.name)
115
115
  params.name = _opts.name;
@@ -133,7 +133,7 @@ export function registerConnectorCommands(program) {
133
133
  const out = createOutput(flags);
134
134
  out.startSpinner('Deleting connector...');
135
135
  try {
136
- const client = getClient();
136
+ const client = await getClientAsync();
137
137
  await client.connectors.delete(connectorId);
138
138
  out.success('Connector deleted.', { id: connectorId, deleted: true });
139
139
  }
@@ -149,7 +149,7 @@ export function registerConnectorCommands(program) {
149
149
  const out = createOutput(flags);
150
150
  out.startSpinner('Testing connection...');
151
151
  try {
152
- const client = getClient();
152
+ const client = await getClientAsync();
153
153
  const result = await client.connectors.test(connectorId);
154
154
  if (result.success) {
155
155
  out.success('Connection test passed.', { success: true });
@@ -174,7 +174,7 @@ export function registerConnectorCommands(program) {
174
174
  const out = createOutput(flags);
175
175
  out.startSpinner('Triggering sync...');
176
176
  try {
177
- const client = getClient();
177
+ const client = await getClientAsync();
178
178
  const result = await client.connectors.sync(connectorId);
179
179
  out.success(result.message, { message: result.message });
180
180
  }
@@ -190,7 +190,7 @@ export function registerConnectorCommands(program) {
190
190
  const out = createOutput(flags);
191
191
  out.startSpinner('Fetching sync logs...');
192
192
  try {
193
- const client = getClient();
193
+ const client = await getClientAsync();
194
194
  const logs = await client.connectors.logs(connectorId);
195
195
  out.stopSpinner();
196
196
  out.list(logs.map(log => ({
@@ -1,5 +1,5 @@
1
1
  import chalk from 'chalk';
2
- import { getClient } from '../client.js';
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
5
  import { createCredentialManager } from '../lib/credential-manager.js';
@@ -18,7 +18,7 @@ EXAMPLES
18
18
  const out = createOutput(flags);
19
19
  out.startSpinner('Fetching documents...');
20
20
  try {
21
- const client = getClient();
21
+ const client = await getClientAsync();
22
22
  const documents = await client.documents.list(vaultId, _opts.dir);
23
23
  out.stopSpinner();
24
24
  out.list(documents.map(doc => ({
@@ -62,7 +62,7 @@ EXAMPLES
62
62
  const flags = resolveFlags(_opts);
63
63
  const out = createOutput(flags);
64
64
  try {
65
- const client = getClient();
65
+ const client = await getClientAsync();
66
66
  const result = await client.documents.get(vaultId, docPath);
67
67
  // Auto-decrypt if the document is encrypted
68
68
  if (result.document.encrypted && !_opts.meta) {
@@ -119,7 +119,7 @@ EXAMPLES
119
119
  process.stdin.on('end', () => resolve(data));
120
120
  });
121
121
  out.startSpinner('Uploading document...');
122
- const client = getClient();
122
+ const client = await getClientAsync();
123
123
  // Check if vault is encrypted and auto-encrypt
124
124
  const vault = await client.vaults.get(vaultId);
125
125
  let doc;
@@ -157,7 +157,7 @@ EXAMPLES
157
157
  const out = createOutput(flags);
158
158
  out.startSpinner('Deleting document...');
159
159
  try {
160
- const client = getClient();
160
+ const client = await getClientAsync();
161
161
  await client.documents.delete(vaultId, docPath);
162
162
  out.success(`Deleted: ${chalk.cyan(docPath)}`, { path: docPath, deleted: true });
163
163
  }
@@ -180,7 +180,7 @@ EXAMPLES
180
180
  const out = createOutput(flags);
181
181
  out.startSpinner('Moving document...');
182
182
  try {
183
- const client = getClient();
183
+ const client = await getClientAsync();
184
184
  const result = await client.documents.move(vaultId, source, dest, _opts.overwrite);
185
185
  out.success(`Moved: ${chalk.cyan(result.source)} -> ${chalk.cyan(result.destination)}`, {
186
186
  source: result.source,