@learncard/cli 3.4.17 → 3.5.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/src/index.tsx CHANGED
@@ -19,6 +19,8 @@ import { getLerRsPlugin } from '@learncard/ler-rs-plugin';
19
19
  import { getRenderMethodPlugin } from '@learncard/render-method-plugin';
20
20
 
21
21
  import { generateRandomSeed } from './random';
22
+ import { runSend } from './send';
23
+ import { out } from './out';
22
24
  import {
23
25
  createLearnCardBundle,
24
26
  exportLearnCardBundle as writeLearnCardBundle,
@@ -300,7 +302,7 @@ const startReadlineRepl = async (colorize?: (input: string) => string): Promise<
300
302
  );
301
303
  } catch (error) {
302
304
  process.stdout.write(
303
- `${error instanceof Error ? error.stack ?? error.message : String(error)}\n`
305
+ `${error instanceof Error ? (error.stack ?? error.message) : String(error)}\n`
304
306
  );
305
307
  }
306
308
 
@@ -327,162 +329,486 @@ const startCliRepl = async (colorize: (input: string) => string): Promise<void>
327
329
  };
328
330
 
329
331
  program
330
- .version(packageJson.version)
331
- .argument('[seed]')
332
- .action(async (_seed: string = generateRandomSeed()) => {
333
- console.clear();
334
-
335
- const envSeed = process.env.LEARNCARD_CLI_SEED ?? process.env.SEED;
336
- const seedInput = envSeed ?? _seed;
337
- const seed = seedInput.padStart(64, '0');
338
-
339
- console.log(
340
- gradient(['cyan', 'green'])(figlet.textSync('Learn Card', 'Big Money-ne' as any))
341
- );
342
- console.log('Welcome to the Learn Card CLI!\n');
343
-
344
- console.log(`Your seed is ${seed}\n`);
345
-
346
- if (envSeed) {
347
- console.log('Using seed from LEARNCARD_CLI_SEED / SEED.\n');
332
+ .command('send <email>')
333
+ .description(
334
+ 'Send a "Quickstart Complete" badge to an email address. Creates .env and send.mjs in the current folder.'
335
+ )
336
+ .option('-y, --yes', 'accept defaults without prompting')
337
+ .option('--name <displayName>', 'display name for your issuer profile')
338
+ .option('--badge <name>', 'name of the badge to send (default: "Quickstart Complete")')
339
+ .option('--description <text>', 'badge description')
340
+ .option(
341
+ '--profile-id <id>',
342
+ 'public handle for your profile (default: derived from the display name)'
343
+ )
344
+ .option('--network <url>', 'network tRPC URL (default: production)')
345
+ .option('--template', 'send using a reusable template and hosted signing authority')
346
+ .option('--template-uri <uri>', 'send from a specific template (implies --template)')
347
+ .option('--webhook-url <url>', 'receive ISSUANCE_DELIVERED / ISSUANCE_CLAIMED at this URL')
348
+ .option('--suppress-delivery', 'skip the claim email; you deliver inbox.claimUrl yourself')
349
+ .option(
350
+ '--guardian-email <email>',
351
+ "require a guardian's approval before the recipient can claim"
352
+ )
353
+ .option('--json', 'print a single JSON result on stdout')
354
+ .action(
355
+ async (
356
+ email: string,
357
+ opts: {
358
+ yes?: boolean;
359
+ name?: string;
360
+ badge?: string;
361
+ description?: string;
362
+ profileId?: string;
363
+ network?: string;
364
+ template?: boolean;
365
+ json?: boolean;
366
+ }
367
+ ) => {
368
+ out.json = !!opts.json;
369
+ out.result = {};
370
+ const didkit = fs.readFile(
371
+ require.resolve('@learncard/didkit-plugin/dist/didkit/didkit_wasm_bg.wasm')
372
+ );
373
+ try {
374
+ await runSend(email, { ...opts, didkit });
375
+ if (out.json) {
376
+ process.stdout.write(
377
+ JSON.stringify({ ok: true, command: 'send', ...out.result }) + '\n'
378
+ );
379
+ }
380
+ process.exit(0);
381
+ } catch (error) {
382
+ const message = error instanceof Error ? error.message : String(error);
383
+ const firstLine = message.split('\n')[0]!;
384
+ if (out.json) {
385
+ process.stdout.write(
386
+ JSON.stringify({ ok: false, command: 'send', error: firstLine }) + '\n'
387
+ );
388
+ process.exit(1);
389
+ }
390
+ console.error(`\n${firstLine}`);
391
+ // Input mistakes explain themselves; keep the docs link for network/auth failures.
392
+ if (!/is not an email address/.test(firstLine))
393
+ console.error(
394
+ 'Troubleshooting: https://docs.learncard.com/start-here/your-first-integration#if-something-goes-wrong'
395
+ );
396
+ process.exit(1);
397
+ }
348
398
  }
349
-
350
- console.log('Creating wallet...');
351
-
352
- cliGlobals.seed = seed;
353
- cliGlobals.generateRandomSeed = generateRandomSeed;
354
- cliGlobals.emptyLearnCard = emptyLearnCard;
355
- cliGlobals.learnCardFromSeed = learnCardFromSeed;
356
- cliGlobals.initLearnCard = initLearnCard;
357
-
358
- const didkit = fs.readFile(
359
- require.resolve('@learncard/didkit-plugin/dist/didkit/didkit_wasm_bg.wasm')
360
- );
361
-
362
- const _learnCard = await initLearnCard({
363
- seed,
364
- network: true,
365
- allowRemoteContexts: true,
366
- didkit,
367
- });
368
-
369
- const lcaApiLc = await _learnCard.addPlugin(
370
- await getLCAPlugin(_learnCard, 'https://api.learncard.app/trpc')
371
- );
372
-
373
- cliGlobals.learnCard = await lcaApiLc.addPlugin(getLerRsPlugin(lcaApiLc));
374
- // Add LinkedClaims plugin so endorse/verify/store/getEndorsements are available in the CLI
375
- cliGlobals.learnCard = await cliGlobals.learnCard.addPlugin(
376
- getLinkedClaimsPlugin(cliGlobals.learnCard)
399
+ );
400
+
401
+ const commandOptions = (command: ReturnType<typeof program.command>) =>
402
+ command
403
+ .option('-y, --yes', 'accept defaults without prompting')
404
+ .option('--profile-id <id>', 'public handle for your issuer profile')
405
+ .option('--network <url>', 'network tRPC URL or staging (default: production)')
406
+ .option('--json', 'print a single JSON result on stdout');
407
+
408
+ /**
409
+ * Shared runner for every subcommand except `send` (which has its own didkit/error
410
+ * formatting). Sets up `out` for this run and, in `--json` mode, prints exactly one
411
+ * envelope on stdout instead of the human-mode stderr message. Pass `wrap: false` for
412
+ * commands (only `verify` today) that already print their own `--json` output.
413
+ */
414
+ const runCommand = async (
415
+ command: string,
416
+ options: { json?: boolean },
417
+ action: (didkit: Promise<Buffer>) => Promise<void>,
418
+ wrap: boolean = true
419
+ ) => {
420
+ out.json = !!options.json;
421
+ out.result = {};
422
+ try {
423
+ await action(
424
+ fs.readFile(require.resolve('@learncard/didkit-plugin/dist/didkit/didkit_wasm_bg.wasm'))
377
425
  );
426
+ if (out.json && wrap) {
427
+ process.stdout.write(JSON.stringify({ ok: true, command, ...out.result }) + '\n');
428
+ }
429
+ process.exit(process.exitCode || 0);
430
+ } catch (error) {
431
+ const message =
432
+ error instanceof Error
433
+ ? error.message.split('\n')[0]!
434
+ : 'Command failed. Please try again.';
435
+ if (out.json && wrap) {
436
+ process.stdout.write(JSON.stringify({ ok: false, command, error: message }) + '\n');
437
+ process.exit(1);
438
+ }
439
+ console.error(message);
440
+ process.exit(1);
441
+ }
442
+ };
378
443
 
379
- // Add OpenBadge v2 wrapper plugin for backwards-compatible OBv2 -> VC wrapping
380
- cliGlobals.learnCard = await cliGlobals.learnCard.addPlugin(
381
- openBadgeV2Plugin(cliGlobals.learnCard)
382
- );
444
+ commandOptions(
445
+ program.command('consent-contract').description("Connect a user's LearnCard to your platform.")
446
+ )
447
+ .option('--name <name>', 'contract name (default: issuer display name)')
448
+ .option(
449
+ '--redirect-url <url>',
450
+ 'callback URL (default: http://localhost:3000/consent-callback)'
451
+ )
452
+ .option('--description <text>', 'what users see when asked to consent')
453
+ .option(
454
+ '--needs-guardian-consent',
455
+ 'GameFlow: minors need a guardian to approve the connection'
456
+ )
457
+ .action(options =>
458
+ runCommand('consent-contract', options, async didkit => {
459
+ const { runConsentContract } = await import('./consent-contract');
460
+ await runConsentContract({ ...options, didkit });
461
+ })
462
+ );
463
+
464
+ commandOptions(program.command('embed').description('Put a Claim button on your site.'))
465
+ .option('--name <name>', 'integration name (default: issuer display name)')
466
+ .option(
467
+ '--domains <origins>',
468
+ 'comma-separated origins (default: http://localhost:3000,http://localhost:5173)'
469
+ )
470
+ .option('--rotate-key', 'rotate the integration publishable key')
471
+ .action(options =>
472
+ runCommand('embed', options, async didkit => {
473
+ const { runEmbed } = await import('./embed');
474
+ await runEmbed({ ...options, didkit });
475
+ })
476
+ );
477
+
478
+ commandOptions(
479
+ program
480
+ .command('setup-signing')
481
+ .description('Set up LearnCard to sign credentials for your project.')
482
+ )
483
+ .option('--name <name>', 'signing authority name (default: default-issuer)')
484
+ .option('--endpoint <url>', 'register your own VC-API signing service instead (with --did)')
485
+ .option('--did <did>', 'DID of your own signing service (with --endpoint)')
486
+ .action(options =>
487
+ runCommand('setup-signing', options, async didkit => {
488
+ const { runSetupSigning } = await import('./setup-signing');
489
+ await runSetupSigning({ ...options, didkit });
490
+ })
491
+ );
492
+
493
+ commandOptions(
494
+ program.command('token').description('Create a scoped API token and reusable send.sh.')
495
+ )
496
+ .option('--name <name>', 'auth grant name (default: cli-<date>)')
497
+ .option('--scope <scope>', 'space-separated permissions (default: boosts:write)')
498
+ .option('--revoke <grantId>', 'revoke an existing auth grant')
499
+ .option('--expires <days>', 'token lifetime in days (default: no expiry)')
500
+ .option('--list', 'list your auth grants')
501
+ .action(options =>
502
+ runCommand('token', options, async didkit => {
503
+ const { runToken } = await import('./token');
504
+ await runToken({ ...options, didkit });
505
+ })
506
+ );
383
507
 
384
- // Add Render Method plugin for attaching W3C renderMethod to VCs
385
- cliGlobals.learnCard = await cliGlobals.learnCard.addPlugin(
386
- getRenderMethodPlugin(cliGlobals.learnCard)
387
- );
508
+ program
509
+ .command('verify <file>')
510
+ .description('Verify a credential or presentation JSON file; use - for stdin.')
511
+ .option('--json', 'print the raw verification result')
512
+ .action((file, options) =>
513
+ runCommand(
514
+ 'verify',
515
+ options,
516
+ async didkit => {
517
+ const { runVerify } = await import('./verify');
518
+ await runVerify(file, { ...options, didkit });
519
+ },
520
+ false
521
+ )
522
+ );
523
+
524
+ commandOptions(
525
+ program
526
+ .command('revoke <credentialUri>')
527
+ .description('Revoke or suspend an issued credential on the network.')
528
+ )
529
+ .option('--suspend', 'suspend instead of permanently revoking')
530
+ .option('--template-uri <uri>', 'template URI if it cannot be read from the credential')
531
+ .option(
532
+ '--recipient <profileId>',
533
+ 'recipient profile ID if it cannot be read from the credential'
534
+ )
535
+ .action((uri, options) =>
536
+ runCommand('revoke', options, async didkit => {
537
+ const { runRevoke } = await import('./revoke');
538
+ await runRevoke(uri, { ...options, didkit });
539
+ })
540
+ );
541
+
542
+ commandOptions(
543
+ program
544
+ .command('status [activityId]')
545
+ .description('What happened to a credential you sent: created, delivered, claimed.')
546
+ )
547
+ .option('--limit <n>', 'how many recent sends to list (default: 20)')
548
+ .option(
549
+ '--event <type>',
550
+ 'only list sends whose latest event is this: created|delivered|claimed|expired|failed'
551
+ )
552
+ .action((activityId, options) =>
553
+ runCommand('status', options, async didkit => {
554
+ const { runStatus } = await import('./status');
555
+ await runStatus(activityId, { ...options, didkit });
556
+ })
557
+ );
388
558
 
389
- cliGlobals.types = types;
390
- cliGlobals.getTestCache = getTestCache;
559
+ program
560
+ .command('open [target]')
561
+ .description(
562
+ 'Open the LearnCard app signed in as this project. Targets: portal (default), wallet, template, contract, integration.'
563
+ )
564
+ .option('-y, --yes', 'accept defaults without prompting')
565
+ .option('--network <url>', 'network tRPC URL or staging (default: production)')
566
+ .option('--app-url <url>', 'LearnCard app URL for self-hosted or local networks')
567
+ .option('--url-fragment', 'pass the seed in the URL fragment instead of the clipboard')
568
+ .option('--no-browser', 'print the URL without opening a browser')
569
+ .option('--json', 'print a single JSON result on stdout')
570
+ .action((target, options) =>
571
+ runCommand('open', options, async () => {
572
+ const { runOpen, OPEN_TARGETS } = await import('./open');
573
+ if (target && !(target in OPEN_TARGETS))
574
+ throw new Error(
575
+ `Unknown target "${target}". Use one of: ${Object.keys(OPEN_TARGETS).join(', ')}.`
576
+ );
577
+ await runOpen(target, options);
578
+ })
579
+ );
580
+
581
+ commandOptions(
582
+ program.command('webhook [email]').description('Know when your credential was claimed.')
583
+ )
584
+ .option('--to <email>', 'recipient email')
585
+ .option('--url <publicUrl>', 'public HTTPS webhook URL')
586
+ .option('--port <n>', 'receiver port (default: 8787)')
587
+ .option('--name <name>', 'display name for your issuer profile')
588
+ .option(
589
+ '--timeout <seconds>',
590
+ 'in --json mode, seconds to wait for webhook events (default: 60)'
591
+ )
592
+ .option('--wait-for-claim', 'in --json mode, also wait for ISSUANCE_CLAIMED before exiting')
593
+ .action((email, options) =>
594
+ runCommand('webhook', options, async didkit => {
595
+ const { runWebhook } = await import('./webhook');
596
+ await runWebhook(email, { ...options, didkit });
597
+ })
598
+ );
599
+
600
+ const JOURNEY = [
601
+ 'send',
602
+ 'status',
603
+ 'setup-signing',
604
+ 'token',
605
+ 'webhook',
606
+ 'consent-contract',
607
+ 'embed',
608
+ 'verify',
609
+ 'revoke',
610
+ 'open',
611
+ 'init',
612
+ 'repl',
613
+ ];
391
614
 
392
- cliGlobals.copy = copyFunction;
393
- cliGlobals.getLearnCardBundlePassword = getLearnCardBundlePassword;
394
- cliGlobals.exportLearnCardBundle = createExportLearnCardBundleHelper(
395
- writeLearnCardBundle,
396
- cliGlobals.learnCard
397
- );
615
+ program
616
+ .name('learncard')
617
+ .description(
618
+ 'Issue, track, and verify credentials from the terminal. Every command shares the .env in the current folder.'
619
+ )
620
+ .showSuggestionAfterError()
621
+ .configureHelp({
622
+ sortSubcommands: false,
623
+ visibleCommands: cmd =>
624
+ [...cmd.commands].sort((a, b) => JOURNEY.indexOf(a.name()) - JOURNEY.indexOf(b.name())),
625
+ })
626
+ .addHelpText(
627
+ 'before',
628
+ '\nStart here: npx @learncard/cli send you@example.com\nThen: npx @learncard/cli status\n'
629
+ )
630
+ .addHelpText(
631
+ 'after',
632
+ '\nEvery command: -y skips prompts, --json prints one machine-readable result, --network staging|<url> picks the network.\n' +
633
+ 'Environment:\n' +
634
+ ' LC_YES=1 Same as passing -y to every command.\n' +
635
+ ' LCA_API_URL, NETWORK_URL Override service URLs for self-hosted networks.\n'
636
+ );
637
+
638
+ const runRepl = async (_seed: string = generateRandomSeed()) => {
639
+ console.clear();
640
+
641
+ const envSeed = process.env.LEARNCARD_CLI_SEED ?? process.env.SEED;
642
+ const seedInput = envSeed ?? _seed;
643
+ const seed = seedInput.padStart(64, '0');
644
+
645
+ console.log(
646
+ gradient(['cyan', 'green'])(figlet.textSync('Learn Card', { font: 'Big Money-ne' }))
647
+ );
648
+ console.log('Welcome to the Learn Card CLI!\n');
649
+
650
+ console.log(`Your seed is ${seed}\n`);
651
+
652
+ if (envSeed) {
653
+ console.log('Using seed from LEARNCARD_CLI_SEED / SEED.\n');
654
+ }
398
655
 
399
- cliGlobals.restoreLearnCardFromBundle = createRestoreLearnCardFromBundleHelper(
400
- restoreBundle,
401
- {
402
- network: true,
403
- allowRemoteContexts: true,
404
- didkit,
405
- }
406
- );
407
- cliGlobals.importLearnCardBundle = importLearnCardBundle;
408
- cliGlobals.createLearnCardBundle = createLearnCardBundle;
409
- cliGlobals.readLearnCardBundle = readLearnCardBundle;
410
-
411
- // delete 'Creating wallet...' message
412
- process.stdout.moveCursor?.(0, -1);
413
- process.stdout.clearLine?.(1);
414
-
415
- console.log('Wallet created!\n');
416
-
417
- console.log('┌───────────────────────────────────────────────────────────────┐');
418
- console.log('│ Variables Available │');
419
- console.log('├────────────────────────────┬──────────────────────────────────┤');
420
- console.log('│ Variable │ Description │');
421
- console.log('├────────────────────────────┼──────────────────────────────────┤');
422
- console.log(`│ ${g.learnCard} │ Learn Card Wallet │`);
423
- console.log(`│ ${g.initLearnCard} │ Wallet Instantiation Function │`);
424
- console.log(`│ ${g.seed} │ Seed used to generate wallet │`);
425
- console.log(`│ ${g.generateRandomSeed} │ Generates a random seed │`);
426
- console.log(`│ ${g.types} │ Helpful zod validators │`);
427
- console.log(`│ ${g.copy} │ Copy text to clipboard │`);
428
- console.log(`│ ${g.getLearnCardBundlePassword} │ Prompt for bundle password │`);
429
- console.log(`│ ${g.exportLearnCardBundle} │ Export wallet continuity ZIP │`);
430
- console.log(`│ ${g.importLearnCardBundle} │ Import continuity ZIP │`);
431
- console.log(`│ ${g.restoreLearnCardFromBundle} │ Restore original wallet from ZIP │`);
432
- console.log('└────────────────────────────┴──────────────────────────────────┘');
433
-
434
- console.log('');
435
-
436
- console.log(
437
- 'For help/documentation regarding your wallet, please read the documentation at\n'
438
- );
656
+ console.log('Creating wallet...');
439
657
 
440
- console.log('https://docs.learncard.com/sdks/learncard-core/construction\n');
658
+ cliGlobals.seed = seed;
659
+ cliGlobals.generateRandomSeed = generateRandomSeed;
660
+ cliGlobals.emptyLearnCard = emptyLearnCard;
661
+ cliGlobals.learnCardFromSeed = learnCardFromSeed;
662
+ cliGlobals.initLearnCard = initLearnCard;
441
663
 
442
- console.log("To get a feel for what's possible, try some of the following commands\n");
664
+ const didkit = fs.readFile(
665
+ require.resolve('@learncard/didkit-plugin/dist/didkit/didkit_wasm_bg.wasm')
666
+ );
443
667
 
444
- console.log(
445
- '┌─────────────────────────┬───────────────────────────────────────────────────────────────────────────────────┐'
446
- );
447
- console.log(
448
- '│ Description │ Command │'
449
- );
450
- console.log(
451
- '├─────────────────────────┼───────────────────────────────────────────────────────────────────────────────────┤'
452
- );
453
- console.log(
454
- `│ View your did │ ${g.learnCard}.id.did(); │`
455
- );
456
- console.log(
457
- `│ Generate an unsigned VC │ ${g.learnCard}.invoke.getTestVc(); │`
458
- );
459
- console.log(
460
- `│ Issue a signed VC │ await ${g.learnCard}.invoke.issueCredential(uvc); │`
461
- );
462
- console.log(
463
- `│ Verify a signed VC │ await ${g.learnCard}.invoke.verifyCredential(vc); │`
464
- );
465
- console.log(
466
- `│ Issue a signed VP │ await ${g.learnCard}.invoke.issuePresentation(vc); │`
467
- );
468
- console.log(
469
- `│ Verify a signed VP │ await ${g.learnCard}.invoke.verifyPresentation(vp); │`
470
- );
471
- console.log(
472
- `│ Prompt bundle password │ const password = await ${g.getLearnCardBundlePassword}(); │`
473
- );
474
- console.log(
475
- `│ Export wallet ZIP │ await ${g.exportLearnCardBundle}(${g.learnCard}, { out: './export.zip', password }); │`
476
- );
477
- console.log(
478
- `│ Restore original wallet │ await ${g.restoreLearnCardFromBundle}('./export.zip', { password }); │`
479
- );
480
- console.log(
481
- '└─────────────────────────┴───────────────────────────────────────────────────────────────────────────────────┘'
482
- );
668
+ const _learnCard = await initLearnCard({
669
+ seed,
670
+ network: true,
671
+ allowRemoteContexts: true,
672
+ didkit,
673
+ });
483
674
 
484
- console.log('');
675
+ const lcaApiLc = await _learnCard.addPlugin(
676
+ await getLCAPlugin(_learnCard, 'https://api.learncard.app/trpc')
677
+ );
678
+
679
+ cliGlobals.learnCard = await lcaApiLc.addPlugin(getLerRsPlugin(lcaApiLc));
680
+ // Add LinkedClaims plugin so endorse/verify/store/getEndorsements are available in the CLI
681
+ cliGlobals.learnCard = await cliGlobals.learnCard.addPlugin(
682
+ getLinkedClaimsPlugin(cliGlobals.learnCard)
683
+ );
684
+
685
+ // Add OpenBadge v2 wrapper plugin for backwards-compatible OBv2 -> VC wrapping
686
+ cliGlobals.learnCard = await cliGlobals.learnCard.addPlugin(
687
+ openBadgeV2Plugin(cliGlobals.learnCard)
688
+ );
689
+
690
+ // Add Render Method plugin for attaching W3C renderMethod to VCs
691
+ cliGlobals.learnCard = await cliGlobals.learnCard.addPlugin(
692
+ getRenderMethodPlugin(cliGlobals.learnCard)
693
+ );
694
+
695
+ cliGlobals.types = types;
696
+ cliGlobals.getTestCache = getTestCache;
697
+
698
+ cliGlobals.copy = copyFunction;
699
+ cliGlobals.getLearnCardBundlePassword = getLearnCardBundlePassword;
700
+ cliGlobals.exportLearnCardBundle = createExportLearnCardBundleHelper(
701
+ writeLearnCardBundle,
702
+ cliGlobals.learnCard
703
+ );
704
+
705
+ cliGlobals.restoreLearnCardFromBundle = createRestoreLearnCardFromBundleHelper(restoreBundle, {
706
+ network: true,
707
+ allowRemoteContexts: true,
708
+ didkit,
709
+ });
710
+ cliGlobals.importLearnCardBundle = importLearnCardBundle;
711
+ cliGlobals.createLearnCardBundle = createLearnCardBundle;
712
+ cliGlobals.readLearnCardBundle = readLearnCardBundle;
713
+
714
+ // delete 'Creating wallet...' message
715
+ process.stdout.moveCursor?.(0, -1);
716
+ process.stdout.clearLine?.(1);
717
+
718
+ console.log('Wallet created!\n');
719
+
720
+ console.log('┌───────────────────────────────────────────────────────────────┐');
721
+ console.log('│ Variables Available │');
722
+ console.log('├────────────────────────────┬──────────────────────────────────┤');
723
+ console.log('│ Variable │ Description │');
724
+ console.log('├────────────────────────────┼──────────────────────────────────┤');
725
+ console.log(`│ ${g.learnCard} │ Learn Card Wallet │`);
726
+ console.log(`│ ${g.initLearnCard} │ Wallet Instantiation Function │`);
727
+ console.log(`│ ${g.seed} │ Seed used to generate wallet │`);
728
+ console.log(`│ ${g.generateRandomSeed} │ Generates a random seed │`);
729
+ console.log(`│ ${g.types} │ Helpful zod validators │`);
730
+ console.log(`│ ${g.copy} │ Copy text to clipboard │`);
731
+ console.log(`│ ${g.getLearnCardBundlePassword} │ Prompt for bundle password │`);
732
+ console.log(`│ ${g.exportLearnCardBundle} │ Export wallet continuity ZIP │`);
733
+ console.log(`│ ${g.importLearnCardBundle} │ Import continuity ZIP │`);
734
+ console.log(`│ ${g.restoreLearnCardFromBundle} │ Restore original wallet from ZIP │`);
735
+ console.log('└────────────────────────────┴──────────────────────────────────┘');
736
+
737
+ console.log('');
738
+
739
+ console.log('For help/documentation regarding your wallet, please read the documentation at\n');
740
+
741
+ console.log('https://docs.learncard.com/sdks/learncard-core/construction\n');
742
+
743
+ console.log("To get a feel for what's possible, try some of the following commands\n");
744
+
745
+ console.log(
746
+ '┌─────────────────────────┬───────────────────────────────────────────────────────────────────────────────────┐'
747
+ );
748
+ console.log(
749
+ '│ Description │ Command │'
750
+ );
751
+ console.log(
752
+ '├─────────────────────────┼───────────────────────────────────────────────────────────────────────────────────┤'
753
+ );
754
+ console.log(
755
+ `│ View your did │ ${g.learnCard}.id.did(); │`
756
+ );
757
+ console.log(
758
+ `│ Generate an unsigned VC │ ${g.learnCard}.invoke.getTestVc(); │`
759
+ );
760
+ console.log(
761
+ `│ Issue a signed VC │ await ${g.learnCard}.invoke.issueCredential(uvc); │`
762
+ );
763
+ console.log(
764
+ `│ Verify a signed VC │ await ${g.learnCard}.invoke.verifyCredential(vc); │`
765
+ );
766
+ console.log(
767
+ `│ Issue a signed VP │ await ${g.learnCard}.invoke.issuePresentation(vc); │`
768
+ );
769
+ console.log(
770
+ `│ Verify a signed VP │ await ${g.learnCard}.invoke.verifyPresentation(vp); │`
771
+ );
772
+ console.log(
773
+ `│ Prompt bundle password │ const password = await ${g.getLearnCardBundlePassword}(); │`
774
+ );
775
+ console.log(
776
+ `│ Export wallet ZIP │ await ${g.exportLearnCardBundle}(${g.learnCard}, { out: './export.zip', password }); │`
777
+ );
778
+ console.log(
779
+ `│ Restore original wallet │ await ${g.restoreLearnCardFromBundle}('./export.zip', { password }); │`
780
+ );
781
+ console.log(
782
+ '└─────────────────────────┴───────────────────────────────────────────────────────────────────────────────────┘'
783
+ );
784
+
785
+ console.log('');
786
+
787
+ await startCliRepl(colorizeReplInput);
788
+ };
485
789
 
486
- await startCliRepl(colorizeReplInput);
790
+ commandOptions(
791
+ program
792
+ .command('init')
793
+ .description("Create this folder's issuer identity and profile without sending anything.")
794
+ .option('--name <displayName>', 'display name for your issuer profile')
795
+ ).action(options =>
796
+ runCommand('init', options, async didkit => {
797
+ const { runInit } = await import('./init');
798
+ await runInit({ ...options, didkit });
487
799
  })
488
- .parse(process.argv);
800
+ );
801
+
802
+ program
803
+ .command('repl [seed]')
804
+ .description('Interactive JavaScript console with a LearnCard preloaded (advanced).')
805
+ .action(runRepl);
806
+
807
+ program.version(packageJson.version);
808
+
809
+ // Bare `learncard` with a TTY opens the console, as it always has; anything else is a command.
810
+ if (process.argv.length <= 2 && process.stdin.isTTY) {
811
+ runRepl();
812
+ } else {
813
+ program.parse(process.argv);
814
+ }