@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/CHANGELOG.md +38 -0
- package/README.md +10 -0
- package/dist/index.js +1933 -56
- package/package.json +24 -24
- package/rollup.config.js +25 -3
- package/scripts/embed-snippets.ts +25 -0
- package/src/consent-contract.ts +116 -0
- package/src/embed.ts +102 -0
- package/src/index.tsx +473 -147
- package/src/init.ts +26 -0
- package/src/open.test.ts +40 -0
- package/src/open.ts +186 -0
- package/src/out.test.ts +40 -0
- package/src/out.ts +16 -0
- package/src/phase-two.test.ts +154 -0
- package/src/project.test.ts +207 -0
- package/src/project.ts +394 -0
- package/src/revoke.test.ts +29 -0
- package/src/revoke.ts +65 -0
- package/src/send-template.test.ts +20 -0
- package/src/send.test.ts +45 -0
- package/src/send.ts +245 -0
- package/src/setup-signing.test.ts +62 -0
- package/src/setup-signing.ts +185 -0
- package/src/snippet-files.ts +16 -0
- package/src/status.test.ts +33 -0
- package/src/status.ts +96 -0
- package/src/token.test.ts +28 -0
- package/src/token.ts +138 -0
- package/src/verify.test.ts +35 -0
- package/src/verify.ts +66 -0
- package/src/webhook.ts +247 -0
- package/tsconfig.json +1 -0
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
|
-
.
|
|
331
|
-
.
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
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
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
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
|
-
|
|
380
|
-
|
|
381
|
-
|
|
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
|
-
|
|
385
|
-
|
|
386
|
-
|
|
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
|
-
|
|
390
|
-
|
|
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
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
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
|
-
|
|
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
|
-
|
|
658
|
+
cliGlobals.seed = seed;
|
|
659
|
+
cliGlobals.generateRandomSeed = generateRandomSeed;
|
|
660
|
+
cliGlobals.emptyLearnCard = emptyLearnCard;
|
|
661
|
+
cliGlobals.learnCardFromSeed = learnCardFromSeed;
|
|
662
|
+
cliGlobals.initLearnCard = initLearnCard;
|
|
441
663
|
|
|
442
|
-
|
|
664
|
+
const didkit = fs.readFile(
|
|
665
|
+
require.resolve('@learncard/didkit-plugin/dist/didkit/didkit_wasm_bg.wasm')
|
|
666
|
+
);
|
|
443
667
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
}
|