@lifestreamdynamics/vault-cli 1.2.0 → 1.3.1
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 +140 -30
- package/dist/client.d.ts +4 -0
- package/dist/client.js +12 -11
- package/dist/commands/admin.js +5 -5
- package/dist/commands/ai.js +17 -4
- package/dist/commands/auth.js +10 -105
- package/dist/commands/booking.d.ts +2 -0
- package/dist/commands/booking.js +739 -0
- package/dist/commands/calendar.js +725 -6
- package/dist/commands/completion.d.ts +5 -0
- package/dist/commands/completion.js +60 -0
- package/dist/commands/config.js +17 -16
- package/dist/commands/connectors.js +12 -1
- package/dist/commands/custom-domains.js +6 -1
- package/dist/commands/docs.js +12 -5
- package/dist/commands/hooks.js +6 -1
- package/dist/commands/links.js +9 -2
- package/dist/commands/mfa.js +1 -70
- package/dist/commands/plugins.d.ts +2 -0
- package/dist/commands/plugins.js +172 -0
- package/dist/commands/publish.js +13 -3
- package/dist/commands/saml.d.ts +2 -0
- package/dist/commands/saml.js +220 -0
- package/dist/commands/scim.d.ts +2 -0
- package/dist/commands/scim.js +238 -0
- package/dist/commands/shares.js +25 -3
- package/dist/commands/subscription.js +9 -2
- package/dist/commands/sync.js +3 -0
- package/dist/commands/teams.js +141 -8
- package/dist/commands/user.js +122 -9
- package/dist/commands/vaults.js +17 -8
- package/dist/commands/webhooks.js +6 -1
- package/dist/config.d.ts +2 -0
- package/dist/config.js +7 -3
- package/dist/index.js +20 -1
- package/dist/lib/credential-manager.js +32 -7
- package/dist/lib/migration.js +2 -2
- package/dist/lib/profiles.js +4 -4
- package/dist/sync/config.js +2 -2
- package/dist/sync/daemon-worker.js +13 -6
- package/dist/sync/daemon.js +2 -1
- package/dist/sync/remote-poller.js +7 -3
- package/dist/sync/state.js +2 -2
- package/dist/utils/confirm.d.ts +11 -0
- package/dist/utils/confirm.js +23 -0
- package/dist/utils/format.js +1 -1
- package/dist/utils/output.js +4 -1
- package/dist/utils/prompt.d.ts +29 -0
- package/dist/utils/prompt.js +146 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -19,8 +19,14 @@ A powerful command-line interface for Lifestream Vault - the multi-user Markdown
|
|
|
19
19
|
- [Search Commands](#search-commands)
|
|
20
20
|
- [Team Commands](#team-commands)
|
|
21
21
|
- [Sharing & Publishing](#sharing--publishing)
|
|
22
|
+
- [Publish Vault Commands](#publish-vault-commands)
|
|
22
23
|
- [Hooks & Webhooks](#hooks--webhooks)
|
|
23
24
|
- [Links & Backlinks](#links--backlinks)
|
|
25
|
+
- [Calendar Commands](#calendar)
|
|
26
|
+
- [Booking Commands](#booking-commands)
|
|
27
|
+
- [AI Commands](#ai-commands)
|
|
28
|
+
- [Analytics Commands](#analytics-commands)
|
|
29
|
+
- [Custom Domain Commands](#custom-domain-commands)
|
|
24
30
|
- [Admin Commands](#admin-commands)
|
|
25
31
|
- [Sync & Watch Mode](#-sync--watch-mode)
|
|
26
32
|
- [Configuration](#️-configuration)
|
|
@@ -135,7 +141,7 @@ lsvault sync watch <syncId>
|
|
|
135
141
|
lsvault search "project notes"
|
|
136
142
|
|
|
137
143
|
# Semantic search (AI-powered)
|
|
138
|
-
lsvault search
|
|
144
|
+
lsvault search "how to deploy the app" --mode semantic
|
|
139
145
|
```
|
|
140
146
|
|
|
141
147
|
## 🔐 Authentication
|
|
@@ -202,16 +208,17 @@ lsvault auth logout
|
|
|
202
208
|
| Command | Description |
|
|
203
209
|
|---------|-------------|
|
|
204
210
|
| `lsvault vaults list` | List all accessible vaults |
|
|
205
|
-
| `lsvault vaults create
|
|
211
|
+
| `lsvault vaults create <name>` | Create a new vault |
|
|
206
212
|
| `lsvault vaults get <vaultId>` | Get vault details |
|
|
207
|
-
| `lsvault vaults update <vaultId>` | Update vault settings |
|
|
208
|
-
| `lsvault vaults delete <vaultId>` | Delete a vault |
|
|
209
213
|
| `lsvault vaults tree <vaultId>` | Display vault directory tree |
|
|
214
|
+
| `lsvault vaults archive <vaultId>` | Archive a vault |
|
|
215
|
+
| `lsvault vaults unarchive <vaultId>` | Unarchive a vault |
|
|
216
|
+
| `lsvault vaults transfer <vaultId> <targetEmail>` | Transfer vault ownership |
|
|
210
217
|
|
|
211
218
|
**Example:**
|
|
212
219
|
```bash
|
|
213
220
|
# Create a vault
|
|
214
|
-
lsvault vaults create
|
|
221
|
+
lsvault vaults create "Work Notes" --description "Professional documentation"
|
|
215
222
|
|
|
216
223
|
# Get vault details
|
|
217
224
|
lsvault vaults get vault_abc123
|
|
@@ -223,9 +230,10 @@ lsvault vaults get vault_abc123
|
|
|
223
230
|
|---------|-------------|
|
|
224
231
|
| `lsvault docs list <vaultId>` | List all documents in a vault |
|
|
225
232
|
| `lsvault docs get <vaultId> <path>` | Get document content |
|
|
226
|
-
| `lsvault docs
|
|
227
|
-
| `lsvault docs update <vaultId> <path>` | Update a document |
|
|
233
|
+
| `lsvault docs put <vaultId> <path>` | Create or update a document (reads from stdin) |
|
|
228
234
|
| `lsvault docs delete <vaultId> <path>` | Delete a document |
|
|
235
|
+
| `lsvault docs move <vaultId> <source> <dest>` | Move or rename a document |
|
|
236
|
+
| `lsvault docs mkdir <vaultId> <path>` | Create a directory |
|
|
229
237
|
|
|
230
238
|
**Example:**
|
|
231
239
|
```bash
|
|
@@ -233,16 +241,16 @@ lsvault vaults get vault_abc123
|
|
|
233
241
|
lsvault docs list vault_abc123
|
|
234
242
|
|
|
235
243
|
# Read a document (outputs to stdout)
|
|
236
|
-
lsvault docs get vault_abc123
|
|
244
|
+
lsvault docs get vault_abc123 notes/meeting.md
|
|
237
245
|
|
|
238
|
-
# Create a document from file
|
|
239
|
-
lsvault docs
|
|
246
|
+
# Create or update a document from a local file (via stdin)
|
|
247
|
+
cat ~/draft.md | lsvault docs put vault_abc123 notes/new.md
|
|
240
248
|
|
|
241
249
|
# Create with inline content
|
|
242
|
-
|
|
250
|
+
echo "# Quick Note\n\nThis is a test." | lsvault docs put vault_abc123 notes/quick.md
|
|
243
251
|
|
|
244
|
-
#
|
|
245
|
-
lsvault docs
|
|
252
|
+
# Show document metadata
|
|
253
|
+
lsvault docs get vault_abc123 notes/meeting.md --meta
|
|
246
254
|
```
|
|
247
255
|
|
|
248
256
|
### Sync Commands
|
|
@@ -285,7 +293,8 @@ lsvault sync daemon start
|
|
|
285
293
|
| Command | Description |
|
|
286
294
|
|---------|-------------|
|
|
287
295
|
| `lsvault search <query>` | Full-text search across all documents |
|
|
288
|
-
| `lsvault search
|
|
296
|
+
| `lsvault search <query> --mode semantic` | Semantic search using AI embeddings |
|
|
297
|
+
| `lsvault search <query> --mode hybrid` | Hybrid text + semantic search |
|
|
289
298
|
|
|
290
299
|
**Example:**
|
|
291
300
|
```bash
|
|
@@ -293,7 +302,7 @@ lsvault sync daemon start
|
|
|
293
302
|
lsvault search "project timeline" --vault vault_abc123
|
|
294
303
|
|
|
295
304
|
# Semantic search
|
|
296
|
-
lsvault search
|
|
305
|
+
lsvault search "explain the deployment process" --mode semantic
|
|
297
306
|
|
|
298
307
|
# Search with filters
|
|
299
308
|
lsvault search "meeting" --tags work,urgent --limit 10
|
|
@@ -346,6 +355,32 @@ lsvault shares create vault_abc123 /reports/Q1.md \
|
|
|
346
355
|
lsvault publish create vault_abc123 /blog/post.md --slug my-first-post
|
|
347
356
|
```
|
|
348
357
|
|
|
358
|
+
### Publish Vault Commands
|
|
359
|
+
|
|
360
|
+
Publish a whole vault as a multi-document public site (Pro tier).
|
|
361
|
+
|
|
362
|
+
| Command | Description |
|
|
363
|
+
|---------|-------------|
|
|
364
|
+
| `lsvault publish-vault list` | List your published vault sites |
|
|
365
|
+
| `lsvault publish-vault publish <vaultId>` | Publish a vault as a public site |
|
|
366
|
+
| `lsvault publish-vault update <vaultId>` | Update a published vault site |
|
|
367
|
+
| `lsvault publish-vault unpublish <vaultId>` | Unpublish a vault site |
|
|
368
|
+
|
|
369
|
+
**Example:**
|
|
370
|
+
```bash
|
|
371
|
+
# Publish a vault as a public site
|
|
372
|
+
lsvault publish-vault publish vault_abc123 \
|
|
373
|
+
--slug my-docs \
|
|
374
|
+
--title "My Documentation" \
|
|
375
|
+
--enable-search
|
|
376
|
+
|
|
377
|
+
# List published vault sites
|
|
378
|
+
lsvault publish-vault list
|
|
379
|
+
|
|
380
|
+
# Unpublish
|
|
381
|
+
lsvault publish-vault unpublish vault_abc123
|
|
382
|
+
```
|
|
383
|
+
|
|
349
384
|
### Hooks & Webhooks
|
|
350
385
|
|
|
351
386
|
| Command | Description |
|
|
@@ -384,6 +419,81 @@ lsvault webhooks create \
|
|
|
384
419
|
| `lsvault calendar update-event <vaultId> <eventId>` | Update a calendar event |
|
|
385
420
|
| `lsvault calendar delete-event <vaultId> <eventId>` | Delete a calendar event |
|
|
386
421
|
|
|
422
|
+
### Booking Commands
|
|
423
|
+
|
|
424
|
+
Manage bookable event slots and guest bookings. Slot CRUD requires Pro tier; team booking groups and waitlist require Business tier.
|
|
425
|
+
|
|
426
|
+
| Command | Description |
|
|
427
|
+
|---------|-------------|
|
|
428
|
+
| `lsvault booking slots list <vaultId>` | List all event slots for a vault |
|
|
429
|
+
| `lsvault booking slots create <vaultId>` | Create a new bookable event slot |
|
|
430
|
+
| `lsvault booking slots update <vaultId> <slotId>` | Update an event slot |
|
|
431
|
+
| `lsvault booking slots delete <vaultId> <slotId>` | Delete an event slot |
|
|
432
|
+
| `lsvault booking list <vaultId>` | List bookings for a vault |
|
|
433
|
+
| `lsvault booking confirm <vaultId> <bookingId>` | Confirm a pending booking |
|
|
434
|
+
| `lsvault booking cancel <vaultId> <bookingId>` | Cancel a booking |
|
|
435
|
+
| `lsvault booking reschedule <token> <newStartAt>` | Reschedule via guest token |
|
|
436
|
+
| `lsvault booking analytics <vaultId>` | View booking analytics (Business tier) |
|
|
437
|
+
| `lsvault booking templates list <vaultId>` | List event templates |
|
|
438
|
+
| `lsvault booking templates create <vaultId>` | Create an event template |
|
|
439
|
+
| `lsvault booking templates delete <vaultId> <templateId>` | Delete an event template |
|
|
440
|
+
| `lsvault booking groups list <teamId>` | List team booking groups |
|
|
441
|
+
| `lsvault booking groups create <teamId>` | Create a team booking group |
|
|
442
|
+
| `lsvault booking groups update <teamId> <groupId>` | Update a team booking group |
|
|
443
|
+
| `lsvault booking groups delete <teamId> <groupId>` | Delete a team booking group |
|
|
444
|
+
| `lsvault booking group-members list <teamId> <groupId>` | List booking group members |
|
|
445
|
+
| `lsvault booking group-members add <teamId> <groupId>` | Add member to booking group |
|
|
446
|
+
| `lsvault booking group-members remove <teamId> <groupId> <userId>` | Remove member from group |
|
|
447
|
+
| `lsvault booking waitlist list <vaultId> <slotId>` | List waitlist entries for a slot |
|
|
448
|
+
|
|
449
|
+
### AI Commands
|
|
450
|
+
|
|
451
|
+
| Command | Description |
|
|
452
|
+
|---------|-------------|
|
|
453
|
+
| `lsvault ai sessions list` | List AI chat sessions |
|
|
454
|
+
| `lsvault ai sessions get <sessionId>` | Get session with messages |
|
|
455
|
+
| `lsvault ai sessions delete <sessionId>` | Delete an AI chat session |
|
|
456
|
+
| `lsvault ai chat <sessionId> <message>` | Send a message in a session |
|
|
457
|
+
| `lsvault ai summarize <vaultId> <docPath>` | Summarize a document with AI |
|
|
458
|
+
|
|
459
|
+
**Example:**
|
|
460
|
+
```bash
|
|
461
|
+
# Chat with AI
|
|
462
|
+
lsvault ai chat session_abc123 "Summarize the key points"
|
|
463
|
+
|
|
464
|
+
# Summarize a document
|
|
465
|
+
lsvault ai summarize vault_abc123 notes/meeting.md
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
### Analytics Commands
|
|
469
|
+
|
|
470
|
+
| Command | Description |
|
|
471
|
+
|---------|-------------|
|
|
472
|
+
| `lsvault analytics published` | Summary of published document views |
|
|
473
|
+
| `lsvault analytics share <vaultId> <shareId>` | Analytics for a share link |
|
|
474
|
+
| `lsvault analytics doc <vaultId> <publishedDocId>` | Analytics for a published document |
|
|
475
|
+
|
|
476
|
+
### Custom Domain Commands
|
|
477
|
+
|
|
478
|
+
| Command | Description |
|
|
479
|
+
|---------|-------------|
|
|
480
|
+
| `lsvault custom-domains list` | List custom domains |
|
|
481
|
+
| `lsvault custom-domains get <domainId>` | Get a custom domain |
|
|
482
|
+
| `lsvault custom-domains add <domain>` | Add a custom domain |
|
|
483
|
+
| `lsvault custom-domains update <domainId>` | Update a custom domain |
|
|
484
|
+
| `lsvault custom-domains remove <domainId>` | Remove a custom domain |
|
|
485
|
+
| `lsvault custom-domains verify <domainId>` | Verify domain via DNS TXT record |
|
|
486
|
+
| `lsvault custom-domains check <domainId>` | Check DNS configuration |
|
|
487
|
+
|
|
488
|
+
**Example:**
|
|
489
|
+
```bash
|
|
490
|
+
# Add a custom domain
|
|
491
|
+
lsvault custom-domains add docs.example.com
|
|
492
|
+
|
|
493
|
+
# Verify after adding DNS TXT record
|
|
494
|
+
lsvault custom-domains verify domain_abc123
|
|
495
|
+
```
|
|
496
|
+
|
|
387
497
|
### Links & Backlinks
|
|
388
498
|
|
|
389
499
|
| Command | Description |
|
|
@@ -416,10 +526,12 @@ lsvault links broken vault_abc123
|
|
|
416
526
|
|---------|-------------|
|
|
417
527
|
| `lsvault admin users list` | List all users |
|
|
418
528
|
| `lsvault admin users get <userId>` | Get user details |
|
|
419
|
-
| `lsvault admin users update <userId>` | Update user
|
|
420
|
-
| `lsvault admin users delete <userId>` | Delete a user |
|
|
529
|
+
| `lsvault admin users update <userId>` | Update user (role, active status) |
|
|
421
530
|
| `lsvault admin stats` | View system statistics |
|
|
422
|
-
| `lsvault
|
|
531
|
+
| `lsvault admin stats timeseries` | Show timeseries data for a metric |
|
|
532
|
+
| `lsvault admin activity` | Show recent system-wide activity |
|
|
533
|
+
| `lsvault admin subscriptions` | Show subscription tier distribution |
|
|
534
|
+
| `lsvault admin health` | Check system health (DB, Redis, uptime) |
|
|
423
535
|
|
|
424
536
|
**Example:**
|
|
425
537
|
```bash
|
|
@@ -429,8 +541,8 @@ lsvault admin users list
|
|
|
429
541
|
# View system stats
|
|
430
542
|
lsvault admin stats
|
|
431
543
|
|
|
432
|
-
# View
|
|
433
|
-
lsvault
|
|
544
|
+
# View system health
|
|
545
|
+
lsvault admin health
|
|
434
546
|
```
|
|
435
547
|
|
|
436
548
|
## 🔄 Sync & Watch Mode
|
|
@@ -562,8 +674,6 @@ Sync configurations are stored per vault in `~/.lsvault/sync/`:
|
|
|
562
674
|
|----------|-------------|---------|
|
|
563
675
|
| `LSVAULT_API_URL` | API server base URL | `https://vault.lifestreamdynamics.com` |
|
|
564
676
|
| `LSVAULT_API_KEY` | API key for authentication | - |
|
|
565
|
-
| `LSVAULT_CONFIG_DIR` | Configuration directory | `~/.lsvault` |
|
|
566
|
-
| `LSVAULT_PROFILE` | Active configuration profile | `default` |
|
|
567
677
|
|
|
568
678
|
**Example:**
|
|
569
679
|
```bash
|
|
@@ -646,7 +756,7 @@ lsvault sync watch sync_xyz789
|
|
|
646
756
|
|
|
647
757
|
```bash
|
|
648
758
|
# Search for documents
|
|
649
|
-
lsvault search "quarterly report" --vault vault_abc123
|
|
759
|
+
lsvault search "quarterly report" --vault vault_abc123 -o json
|
|
650
760
|
|
|
651
761
|
# Create a share link for the found document
|
|
652
762
|
lsvault shares create vault_abc123 /reports/Q4-2025.md \
|
|
@@ -664,8 +774,8 @@ lsvault publish create vault_abc123 /blog/announcement.md \
|
|
|
664
774
|
# Create a team
|
|
665
775
|
lsvault teams create --name "Product Team" --description "Product docs"
|
|
666
776
|
|
|
667
|
-
# Create a
|
|
668
|
-
lsvault vaults create
|
|
777
|
+
# Create a vault
|
|
778
|
+
lsvault vaults create "Product Docs" --description "Product documentation"
|
|
669
779
|
|
|
670
780
|
# Invite team members
|
|
671
781
|
lsvault teams invite team_abc123 --email pm@example.com --role admin
|
|
@@ -689,7 +799,7 @@ lsvault keys create \
|
|
|
689
799
|
|
|
690
800
|
# Use API key in scripts
|
|
691
801
|
export LSVAULT_API_KEY=lsv_k_generated_key
|
|
692
|
-
lsvault vaults list
|
|
802
|
+
lsvault vaults list -o json | jq '.[] | .name'
|
|
693
803
|
```
|
|
694
804
|
|
|
695
805
|
## 🐛 Troubleshooting
|
|
@@ -762,11 +872,11 @@ lsvault auth migrate
|
|
|
762
872
|
|
|
763
873
|
**Problem:** Need machine-readable output
|
|
764
874
|
|
|
765
|
-
**Solution:** Use `--json` or `--quiet` flags:
|
|
875
|
+
**Solution:** Use `-o json` (or `--output json`) or `--quiet` flags:
|
|
766
876
|
```bash
|
|
767
|
-
lsvault vaults list
|
|
768
|
-
lsvault search "query"
|
|
769
|
-
lsvault docs get vault_abc123
|
|
877
|
+
lsvault vaults list -o json
|
|
878
|
+
lsvault search "query" -o json | jq '.[] | .path'
|
|
879
|
+
lsvault docs get vault_abc123 path.md --quiet > output.md
|
|
770
880
|
```
|
|
771
881
|
|
|
772
882
|
## 🔗 Related Packages
|
package/dist/client.d.ts
CHANGED
|
@@ -3,10 +3,14 @@ import { LifestreamVaultClient } from '@lifestreamdynamics/vault-sdk';
|
|
|
3
3
|
* Create an SDK client from CLI configuration.
|
|
4
4
|
* Supports both API key and JWT (access + refresh token) authentication.
|
|
5
5
|
* When using JWT tokens, auto-refresh is enabled and new tokens are persisted.
|
|
6
|
+
*
|
|
7
|
+
* @throws {Error} If no credentials are configured.
|
|
6
8
|
*/
|
|
7
9
|
export declare function getClient(): LifestreamVaultClient;
|
|
8
10
|
/**
|
|
9
11
|
* Create an SDK client from async config resolution (secure credential manager).
|
|
10
12
|
* This resolves credentials from keychain/encrypted storage.
|
|
13
|
+
*
|
|
14
|
+
* @throws {Error} If no credentials are configured.
|
|
11
15
|
*/
|
|
12
16
|
export declare function getClientAsync(): Promise<LifestreamVaultClient>;
|
package/dist/client.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { LifestreamVaultClient } from '@lifestreamdynamics/vault-sdk';
|
|
2
2
|
import { loadConfig, loadConfigAsync, getCredentialManager } from './config.js';
|
|
3
|
-
import chalk from 'chalk';
|
|
4
3
|
/**
|
|
5
4
|
* Create an SDK client from CLI configuration.
|
|
6
5
|
* Supports both API key and JWT (access + refresh token) authentication.
|
|
7
6
|
* When using JWT tokens, auto-refresh is enabled and new tokens are persisted.
|
|
7
|
+
*
|
|
8
|
+
* @throws {Error} If no credentials are configured.
|
|
8
9
|
*/
|
|
9
10
|
export function getClient() {
|
|
10
11
|
const config = loadConfig();
|
|
@@ -35,15 +36,16 @@ export function getClient() {
|
|
|
35
36
|
apiKey: config.apiKey,
|
|
36
37
|
});
|
|
37
38
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
process.exit(1);
|
|
39
|
+
throw new Error('No credentials configured.\n' +
|
|
40
|
+
'Run: lsvault auth login --api-key <key>\n' +
|
|
41
|
+
' or: lsvault auth login --email <email>\n' +
|
|
42
|
+
'Or set LSVAULT_API_KEY environment variable');
|
|
43
43
|
}
|
|
44
44
|
/**
|
|
45
45
|
* Create an SDK client from async config resolution (secure credential manager).
|
|
46
46
|
* This resolves credentials from keychain/encrypted storage.
|
|
47
|
+
*
|
|
48
|
+
* @throws {Error} If no credentials are configured.
|
|
47
49
|
*/
|
|
48
50
|
export async function getClientAsync() {
|
|
49
51
|
const config = await loadConfigAsync();
|
|
@@ -71,9 +73,8 @@ export async function getClientAsync() {
|
|
|
71
73
|
apiKey: config.apiKey,
|
|
72
74
|
});
|
|
73
75
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
process.exit(1);
|
|
76
|
+
throw new Error('No credentials configured.\n' +
|
|
77
|
+
'Run: lsvault auth login --api-key <key>\n' +
|
|
78
|
+
' or: lsvault auth login --email <email>\n' +
|
|
79
|
+
'Or set LSVAULT_API_KEY environment variable');
|
|
79
80
|
}
|
package/dist/commands/admin.js
CHANGED
|
@@ -93,7 +93,7 @@ export function registerAdminCommands(program) {
|
|
|
93
93
|
out.list(result.users.map(u => ({
|
|
94
94
|
email: u.email,
|
|
95
95
|
id: u.id,
|
|
96
|
-
|
|
96
|
+
displayName: u.displayName || '',
|
|
97
97
|
role: u.role,
|
|
98
98
|
subscriptionTier: u.subscriptionTier,
|
|
99
99
|
isActive: u.isActive,
|
|
@@ -101,15 +101,15 @@ export function registerAdminCommands(program) {
|
|
|
101
101
|
emptyMessage: 'No users found.',
|
|
102
102
|
columns: [
|
|
103
103
|
{ key: 'email', header: 'Email' },
|
|
104
|
-
{ key: '
|
|
104
|
+
{ key: 'displayName', header: 'Name' },
|
|
105
105
|
{ key: 'role', header: 'Role' },
|
|
106
106
|
{ key: 'subscriptionTier', header: 'Tier' },
|
|
107
107
|
{ key: 'isActive', header: 'Active' },
|
|
108
108
|
],
|
|
109
109
|
textFn: (u) => {
|
|
110
110
|
const active = u.isActive ? chalk.green('active') : chalk.red('inactive');
|
|
111
|
-
const
|
|
112
|
-
return ` ${chalk.cyan(String(u.email))} ${chalk.dim(`(${String(u.id)})`)} -- ${
|
|
111
|
+
const displayName = u.displayName || chalk.dim('no name');
|
|
112
|
+
return ` ${chalk.cyan(String(u.email))} ${chalk.dim(`(${String(u.id)})`)} -- ${displayName} -- ${chalk.magenta(String(u.role))} -- ${String(u.subscriptionTier)} -- ${active}`;
|
|
113
113
|
},
|
|
114
114
|
});
|
|
115
115
|
}
|
|
@@ -131,7 +131,7 @@ export function registerAdminCommands(program) {
|
|
|
131
131
|
out.record({
|
|
132
132
|
email: user.email,
|
|
133
133
|
id: user.id,
|
|
134
|
-
|
|
134
|
+
displayName: user.displayName,
|
|
135
135
|
role: user.role,
|
|
136
136
|
isActive: user.isActive,
|
|
137
137
|
subscriptionTier: user.subscriptionTier,
|
package/dist/commands/ai.js
CHANGED
|
@@ -48,7 +48,7 @@ export function registerAiCommands(program) {
|
|
|
48
48
|
process.stdout.write(`Title: ${result.session.title ?? 'Untitled'}\n\n`);
|
|
49
49
|
for (const msg of result.messages ?? []) {
|
|
50
50
|
const role = msg.role === 'assistant' ? chalk.green('AI') : chalk.blue('You');
|
|
51
|
-
process.stdout.write(`${role}: ${
|
|
51
|
+
process.stdout.write(`${role}: ${msg.content}\n\n`);
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
}
|
|
@@ -58,10 +58,15 @@ export function registerAiCommands(program) {
|
|
|
58
58
|
});
|
|
59
59
|
addGlobalFlags(sessions.command('delete')
|
|
60
60
|
.description('Delete an AI chat session')
|
|
61
|
-
.argument('<sessionId>', 'Session ID')
|
|
61
|
+
.argument('<sessionId>', 'Session ID')
|
|
62
|
+
.option('-y, --yes', 'Skip confirmation prompt'))
|
|
62
63
|
.action(async (sessionId, _opts) => {
|
|
63
64
|
const flags = resolveFlags(_opts);
|
|
64
65
|
const out = createOutput(flags);
|
|
66
|
+
if (!_opts.yes) {
|
|
67
|
+
out.status(chalk.yellow(`Pass --yes to delete AI session ${sessionId}.`));
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
65
70
|
out.startSpinner('Deleting AI session...');
|
|
66
71
|
try {
|
|
67
72
|
const client = await getClientAsync();
|
|
@@ -84,7 +89,11 @@ export function registerAiCommands(program) {
|
|
|
84
89
|
const client = await getClientAsync();
|
|
85
90
|
const response = await client.ai.chat({ message, sessionId });
|
|
86
91
|
out.stopSpinner();
|
|
87
|
-
|
|
92
|
+
// response.message is typed as { role: string; content: string; sources: string[] }
|
|
93
|
+
process.stdout.write(response.message.content + '\n');
|
|
94
|
+
if (response.message.sources.length > 0) {
|
|
95
|
+
process.stdout.write(chalk.dim(`Sources: ${response.message.sources.join(', ')}`) + '\n');
|
|
96
|
+
}
|
|
88
97
|
}
|
|
89
98
|
catch (err) {
|
|
90
99
|
handleError(out, err, 'Failed to send AI message');
|
|
@@ -102,7 +111,11 @@ export function registerAiCommands(program) {
|
|
|
102
111
|
const client = await getClientAsync();
|
|
103
112
|
const summary = await client.ai.summarize(vaultId, docPath);
|
|
104
113
|
out.stopSpinner();
|
|
105
|
-
|
|
114
|
+
// summary is typed as { summary: string; keyTopics: string[]; tokensUsed: number }
|
|
115
|
+
process.stdout.write(summary.summary + '\n');
|
|
116
|
+
if (summary.keyTopics.length > 0) {
|
|
117
|
+
process.stdout.write(chalk.dim(`Key topics: ${summary.keyTopics.join(', ')}`) + '\n');
|
|
118
|
+
}
|
|
106
119
|
}
|
|
107
120
|
catch (err) {
|
|
108
121
|
handleError(out, err, 'Failed to summarize document');
|
package/dist/commands/auth.js
CHANGED
|
@@ -4,6 +4,7 @@ import { LifestreamVaultClient } from '@lifestreamdynamics/vault-sdk';
|
|
|
4
4
|
import { loadConfigAsync, getCredentialManager } from '../config.js';
|
|
5
5
|
import { getClientAsync } from '../client.js';
|
|
6
6
|
import { migrateCredentials, hasPlaintextCredentials, checkAndPromptMigration } from '../lib/migration.js';
|
|
7
|
+
import { promptPassword, promptMfaCode } from '../utils/prompt.js';
|
|
7
8
|
export function registerAuthCommands(program) {
|
|
8
9
|
const auth = program.command('auth').description('Authentication and credential management');
|
|
9
10
|
auth.command('login')
|
|
@@ -38,6 +39,7 @@ EXAMPLES
|
|
|
38
39
|
const password = opts.password ?? await promptPassword();
|
|
39
40
|
if (!password) {
|
|
40
41
|
console.error(chalk.red('Password is required for email login.'));
|
|
42
|
+
process.exitCode = 1;
|
|
41
43
|
return;
|
|
42
44
|
}
|
|
43
45
|
const config = await loadConfigAsync();
|
|
@@ -64,7 +66,7 @@ EXAMPLES
|
|
|
64
66
|
refreshToken: refreshToken ?? undefined,
|
|
65
67
|
});
|
|
66
68
|
spinner.succeed(`Logged in as ${chalk.cyan(tokens.user.email)}`);
|
|
67
|
-
console.log(` Name: ${tokens.user.
|
|
69
|
+
console.log(` Name: ${tokens.user.displayName || chalk.dim('not set')}`);
|
|
68
70
|
console.log(` Role: ${tokens.user.role}`);
|
|
69
71
|
if (!refreshToken) {
|
|
70
72
|
console.log(chalk.yellow(' Note: No refresh token received. Session will expire.'));
|
|
@@ -73,6 +75,7 @@ EXAMPLES
|
|
|
73
75
|
catch (err) {
|
|
74
76
|
spinner.fail('Login failed');
|
|
75
77
|
console.error(err instanceof Error ? err.message : String(err));
|
|
78
|
+
process.exitCode = 1;
|
|
76
79
|
}
|
|
77
80
|
return;
|
|
78
81
|
}
|
|
@@ -87,6 +90,7 @@ EXAMPLES
|
|
|
87
90
|
catch (err) {
|
|
88
91
|
spinner.fail('Failed to save API key to secure storage');
|
|
89
92
|
console.error(err instanceof Error ? err.message : String(err));
|
|
93
|
+
process.exitCode = 1;
|
|
90
94
|
}
|
|
91
95
|
return;
|
|
92
96
|
}
|
|
@@ -102,6 +106,7 @@ EXAMPLES
|
|
|
102
106
|
const config = await loadConfigAsync();
|
|
103
107
|
if (!config.refreshToken) {
|
|
104
108
|
console.error(chalk.red('No refresh token stored. Login first with --email.'));
|
|
109
|
+
process.exitCode = 1;
|
|
105
110
|
return;
|
|
106
111
|
}
|
|
107
112
|
const spinner = ora('Refreshing access token...').start();
|
|
@@ -126,6 +131,7 @@ EXAMPLES
|
|
|
126
131
|
spinner.fail('Token refresh failed');
|
|
127
132
|
console.error(err instanceof Error ? err.message : String(err));
|
|
128
133
|
console.log(chalk.dim('You may need to log in again: lsvault auth login --email <email>'));
|
|
134
|
+
process.exitCode = 1;
|
|
129
135
|
}
|
|
130
136
|
});
|
|
131
137
|
auth.command('logout')
|
|
@@ -198,119 +204,18 @@ EXAMPLES
|
|
|
198
204
|
const user = await client.user.me();
|
|
199
205
|
spinner.stop();
|
|
200
206
|
console.log(`User: ${chalk.cyan(user.email)}`);
|
|
201
|
-
console.log(`Name: ${user.
|
|
207
|
+
console.log(`Name: ${user.displayName || chalk.dim('not set')}`);
|
|
202
208
|
console.log(`Role: ${user.role}`);
|
|
203
209
|
console.log(`Plan: ${chalk.green(user.subscriptionTier)}`);
|
|
204
210
|
}
|
|
205
211
|
catch (err) {
|
|
206
212
|
spinner.fail('Could not fetch user info');
|
|
207
|
-
console.error(err instanceof Error ? err.message : err);
|
|
213
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
214
|
+
process.exitCode = 1;
|
|
208
215
|
}
|
|
209
216
|
}
|
|
210
217
|
});
|
|
211
218
|
}
|
|
212
|
-
/**
|
|
213
|
-
* Prompt for a password from stdin (non-echoing).
|
|
214
|
-
* Returns the password or null if stdin is not a TTY.
|
|
215
|
-
*/
|
|
216
|
-
async function promptPassword() {
|
|
217
|
-
// In non-interactive mode, cannot prompt
|
|
218
|
-
if (!process.stdin.isTTY) {
|
|
219
|
-
return null;
|
|
220
|
-
}
|
|
221
|
-
const readline = await import('node:readline');
|
|
222
|
-
return new Promise((resolve) => {
|
|
223
|
-
const rl = readline.createInterface({
|
|
224
|
-
input: process.stdin,
|
|
225
|
-
output: process.stderr,
|
|
226
|
-
terminal: true,
|
|
227
|
-
});
|
|
228
|
-
// Disable echoing
|
|
229
|
-
process.stderr.write('Password: ');
|
|
230
|
-
process.stdin.setRawMode?.(true);
|
|
231
|
-
let password = '';
|
|
232
|
-
const onData = (chunk) => {
|
|
233
|
-
const char = chunk.toString('utf-8');
|
|
234
|
-
if (char === '\n' || char === '\r' || char === '\u0004') {
|
|
235
|
-
process.stderr.write('\n');
|
|
236
|
-
process.stdin.setRawMode?.(false);
|
|
237
|
-
process.stdin.removeListener('data', onData);
|
|
238
|
-
rl.close();
|
|
239
|
-
resolve(password);
|
|
240
|
-
}
|
|
241
|
-
else if (char === '\u0003') {
|
|
242
|
-
// Ctrl+C
|
|
243
|
-
process.stderr.write('\n');
|
|
244
|
-
process.stdin.setRawMode?.(false);
|
|
245
|
-
process.stdin.removeListener('data', onData);
|
|
246
|
-
rl.close();
|
|
247
|
-
resolve(null);
|
|
248
|
-
}
|
|
249
|
-
else if (char === '\u007F' || char === '\b') {
|
|
250
|
-
// Backspace
|
|
251
|
-
if (password.length > 0) {
|
|
252
|
-
password = password.slice(0, -1);
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
else {
|
|
256
|
-
password += char;
|
|
257
|
-
}
|
|
258
|
-
};
|
|
259
|
-
process.stdin.on('data', onData);
|
|
260
|
-
process.stdin.resume();
|
|
261
|
-
});
|
|
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
|
-
}
|
|
314
219
|
function formatMethod(method) {
|
|
315
220
|
switch (method) {
|
|
316
221
|
case 'keychain': return chalk.green('OS Keychain');
|