npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@learncard/cli

v3.6.8

Published

Command Line Interface for LearnCard

Downloads

1,669

Readme

@learncard/cli

npm version npm downloads vulnerabilities

LearnCard CLI is an easy to use node REPL that instantiates a Learn Card wallet for you and gives you all the tools you need to easily play around with the Learn Card SDK!

LearnCard CLI

Documentation

All LearnCard documentation can be found at: https://docs.learncard.com

Usage

Send your first credential

npx @learncard/cli send

Prompts for a recipient if omitted (or pass it: send [email protected]), generates a seed into .env, creates your issuer profile, sends a "Quickstart Complete" badge, and writes the code it ran to ./send.mjs. The recipient can be an email or phone number (delivered through the Universal Inbox with a claim link) or an existing profile ID or DID (delivered straight into their wallet). Placeholder addresses like [email protected] are rejected — nobody would receive the badge. Pass --yes to accept defaults (the recipient must then be given as an argument); --name "Acme", --badge "Welcome to Acme", --description, and --profile-id set them explicitly. Walkthrough: https://docs.learncard.com/start-here/your-first-integration

Demonstrate credential refresh

Run a guided, real-network demonstration without writing code:

learncard demo refresh

Press Enter between each step: send a Provisional Course Certificate, publish a Final Course Certificate, then refresh the recipient's copy. The CLI shows the before and after and verifies that the credential keeps its identity. It uses the existing sendBoost flow with enableRefresh: true and publishes using the receipt returned by that send. No second badge is sent.

The default network is http://localhost:4000/trpc. A developer must first start the local services with managed refresh enabled. To present against staging once LC-2198's service and SDK changes are deployed, use learncard demo refresh --network staging. This command requires a CLI build containing this feature; it is not available in older published versions. From this branch's repository root, run:

bun --cwd packages/learn-card-cli start demo refresh

By default, each run creates two demo accounts and a badge on the selected network. Keys stay in memory, and those demo records remain on the network after the command exits. The demo does not use your .env identity or send email. The refreshed copy is shown in the CLI session; it is not saved to the LearnCard app.

Use --yes to run without pauses, or --json for a machine-readable result.

Show the certificate and notifications in the local app

Start the normal local app stack first (from apps/learn-card-app, run bun run lc dev learncard local full). Once the app is available at http://localhost:3000, run this in a second terminal from the repository root:

bun --cwd packages/learn-card-cli start demo refresh --ui
  1. Follow the printed sign-in link to enter a fresh Refresh Demo Learner account. If already signed in, switch to the demo account when prompted.
  2. Press Enter in the terminal to send the certificate. In the app, open Alerts → Claim → Accept, skip the optional connection prompt, then view Provisional Course Certificate in Passport → Achievements. Open it to see Provisional Results and Final grade: Pending on the full certificate.
  3. Press Enter in the terminal to publish the final certificate. The command checks that the app has saved the original first.
  4. Reload the app if needed and open Alerts. Select the notification that the school updated a credential. The app retrieves and verifies Final Course Certificate, replacing the existing copy. Open it to see Final Results and Final grade: A.
  5. Press Enter in the terminal to confirm. The CLI checks that the updated credential was actually saved by the app and verifies it.

Keep the terminal open throughout. The sign-in link grants access to this disposable demo account; keep it private and use it only for test data. The CLI does not write account keys to files, but the recipient remains signed into the browser after the demo. Rerunning creates fresh accounts. UI mode requires an interactive terminal and cannot be combined with --yes, --json, or LC_YES=1.

For a different local app port, pass --app-url http://localhost:3001. The CLI reads the app's served tenant-config.json to use the same LearnCloud and notification services. Both the app and all these services must use loopback URLs, and --network must match the app's Brain service. Local refresh trust is automatic in the development app. If using the E2E service stack instead of the normal app stack, set the generated app config's apis.lcaApi to http://localhost:5200/trpc and apis.notificationsEndpoint to http://localhost:5200/api/notifications/send before starting the demo. Do not run database-resetting E2E tests during a demonstration.

Receive the claim at your own email (opt-in real email)

Use --email to run the Universal Inbox lifecycle against an address you own. The CLI asks the locally configured delivery service to email a provisional claim link, you claim it in the app, then the school publishes a single visible final update.

bun --cwd packages/learn-card-cli start demo refresh --inbox --ui --email [email protected]

--email without a value prompts for the address. It requires --inbox, --ui, and an interactive terminal, and the address is validated before any account is created. The walkthrough is:

  1. Issue provisional results. The CLI asks the delivery service to email a claim link for a Provisional Course Certificate to your address. It reports only that delivery was requested; it never claims the email was sent or received.
  2. Claim in the app. Open the mailbox link, sign in or create an account using that same address, and claim the provisional certificate. Keep the app open.
  3. Publish final results. Return to the terminal and press Enter. The CLI reads the issuer's inbox-credential record for the bound holder DID, then publishes Final Certificate / Final grade: A as the single visible update (version 2). That publication requests an in-app notification and a update email with the issuer and certificate name prompting you to log in and view notifications.
  4. View the update. Open the app notifications to see the final certificate. The CLI never creates, signs in as, or reads the recipient wallet, so it does not verify the wallet contents and cannot confirm that any email or notification was delivered.

Real email mode is local-only like the rest of the inbox demo. It never suppresses delivery, and it never publishes a hidden pre-claim version: version 2 is the only update.

Operator setup. --email only requests delivery. The operator must enable and configure the local email adapter/delivery service first; otherwise no email is sent. In the local test mode the adapter logs the message instead of sending it, so open the link on the same computer that runs the app. The CLI cannot verify that an address is deliverable, and it masks the address in --json output.

Advanced: refresh for a recipient who has no account yet (disposable preclaim path)

This path exercises the deferred Universal Inbox mechanics — including publishing an update before any holder exists — with a fake @example.com address and suppressed delivery. It is not the recommended mail walkthrough; use --email above for that.

The direct demo sends to a profile that already exists. Add --inbox to exercise the deferred Universal Inbox path: the school issues a refreshable certificate to a random @example.com address with no LearnCard account, publishes a new version before anyone claims, and only then is a real holder bound and sent an update. Email delivery is suppressed, so the CLI shows the claim link instead of mailing it.

bun --cwd packages/learn-card-cli start demo refresh --inbox

Press Enter through four stages:

  1. Issue provisional results. Queues a Provisional Course Certificate for the demo address. Nothing is emailed and there is no recipient to notify.
  2. Publish final results before claim. The school publishes Final Results / Final grade: A. Because no holder exists yet, the publication reports notification: "not-applicable"; nothing is announced.
  3. Claim. The CLI claims with a fresh local wallet using a real DIDAuth presentation, exactly like the app. It receives the newest version (final results), verifies the proof, and confirms the same credential identity.
  4. Publish honors results. Now that the claim has bound the holder DID, the school publishes Honors Results / Final grade: A+. The holder refreshes and verifies that the same credential now shows the honors version.

--yes runs every stage without pausing; --json prints one machine-readable result with no seeds, tokens, or claim links. Terminal mode is local-only (it rejects a non-loopback --network) and needs the local LCA signing service. --lca-url defaults to http://localhost:5100/trpc; pass --lca-url http://localhost:5200/trpc when presenting against the E2E stack on port 5200.

For the guided app experience, start the normal local app stack as above and run:

bun --cwd packages/learn-card-cli start demo refresh --inbox --ui

The CLI reads Brain, LearnCloud, LCA, and notification services from the app's tenant-config.json and requires them all to be loopback, just like the direct demo.

  1. Press Enter to start, then follow the terminal prompts to issue provisional results and publish Final Results / Final grade: A before the recipient account exists.
  2. Press Enter to create the demo recipient. Open the printed sign-in link and choose Switch account if prompted. It takes you directly to the claim screen; the separate claim link is also printed. Keep both links private.
  3. Click Claim my credential → Accept. The first certificate already shows final results, not provisional results. Find it in Passport → Achievements. This first delivery uses a claim link because there was no account to notify at issue time.
  4. Return to the terminal and press Enter to confirm the certificate is saved, then Enter again to publish the honors update. Early confirmation keeps the demo waiting.
  5. Open Alerts → “Inbox Demo School updated one of your credentials” → View Credential. Reload if the notification is not visible yet. The certificate now shows Honors Results / Final grade: A+.
  6. Press Enter in the terminal to finish. The CLI verifies that the app replaced the same entry (not a duplicate) and that the saved certificate has a valid signature.

In --ui mode the CLI never claims, accepts, refreshes, or saves on the app's behalf; a human performs every app action. Rerunning creates fresh accounts. This mode requires an interactive terminal and cannot be combined with --yes, --json, or LC_YES=1.

The unknown email is simulated with suppressed delivery, but the claim link, signing authority, publication, claim binding, and refresh are all real. The direct demo refresh (without --inbox) is unchanged.

Interactive REPL

npx @learncard/cli

# Optionally specify a seed
npx @learncard/cli 1b498556081a298261313657c32d5d0a9ce8285dc4d659e6787392207e4a7ac2

Holder continuity export

The CLI exposes REPL helpers from @learncard/holder-continuity for holder-controlled export, restore, and self-import:

const password = await getLearnCardBundlePassword();

await exportLearnCardBundle(learnCard, {
    out: './learncard-export.zip',
    password,
});

const freshWallet = await initLearnCard({ seed: '0'.repeat(64), network: true });
await importLearnCardBundle('./learncard-export.zip', {
    password,
    wallet: freshWallet,
    verifyBeforeImport: true,
});
const restoredWallet = await restoreLearnCardFromBundle('./learncard-export.zip', { password });

getLearnCardBundlePassword() prompts without echoing the password into the REPL, which avoids saving it in REPL history. You can still pass a password string directly for local scripts.

restoreLearnCardFromBundle(...) decrypts the exported seed and returns a wallet with the original DID. It does not upload bundle payloads or recreate index records; use importLearnCardBundle(...) when copying credentials into another wallet.

If you omit the first argument, the CLI exports the default learnCard wallet it created at startup:

const password = await getLearnCardBundlePassword();
await exportLearnCardBundle({ out: './learncard-export.zip', password });

See @learncard/holder-continuity BUNDLE_SPEC.md for the ZIP layout and manifest hashing rules.

Bootstrap an organization

npx @learncard/cli org apply ./org.yaml

Reconciles a declarative YAML/JSON spec (issuer profile, signing authority, districts/managed profiles, service-account tokens) against the network. With a learncard-hosted signer, every managed profile also gets its own hosted signing authority registered, so API tokens acting as a district can sign through it. Idempotent — re-running with the same file makes no changes. Use --dry-run to preview, and --secrets-out ./secrets.env to save any newly created service-account tokens (required the first time a serviceAccounts entry is created; each is written as NAME=token with the account name upper-cased and hyphens replaced by underscores, e.g. EA_CLR_ISSUER=…). branding (on the issuer and on each managed profile) sets the profile image, hero image, bios, website, type, and wallet display colours; it is diffed field-by-field, so re-running only sends what changed and display colours are merged, never wiped. Images must be https URLs for now — host them and paste the link. Webhooks are not registered on the network — LearnCard calls configuration.webhookUrl per issuance — so the first webhooks[].url is saved as WEBHOOK_URL in .env for doctor and your issuer code to default to. Start from one of examples/*.network.yaml — minimal, service-account, branded, self-hosted-signing, state-districts, or delegated-service-account — each shows one idea.

issuer:
    profileId: scde
    displayName: South Carolina Department of Education
    branding:
        image: https://cdn.example.org/scde-logo.png
        shortBio: State education agency.
        websiteLink: https://ed.sc.gov
        display: { backgroundColor: '#18224E', accentColor: '#2E7D32' }
    signingAuthority: { type: learncard-hosted, name: scde-clr }
profileManager:
    displayName: SC Districts
    managed:
        - { profileId: sc-greenville, displayName: Greenville County Schools }
serviceAccounts:
    - name: ea-clr-issuer
      scopes: [inbox:write, inbox:read, credentials:write, credentials:read]

A service account's actAs controls whether its token can act as a managed profile: '*' allows any profile under profileManager.managed, or list specific profile IDs (e.g. actAs: [sc-greenville, sc-north]) to limit it to those. Omit actAs and the token cannot act as anyone — delegation is deny-by-default, so add it explicitly wherever a backend needs to issue on a district's behalf. See examples/delegated-service-account.network.yaml.

Once the org exists, send picks up its pieces automatically: with a signing authority registered it signs through that authority by default (--no-template opts back out to the local key), and --as <managedProfileId> sends as one of the managed profiles — signed with that profile's own did:web, no separate seed or folder needed. inbox list --as <managedProfileId> shows what that profile has sent. LEARNCARD_AS=<profileId> does the same for a whole shell session without persisting anything, and whoami shows the folder's identity plus every profile --as can target (plus any service accounts and what they may act as). A folder is one identity; passing a different --profile-id is an error that points you at --as. Backends using an API token act as a managed profile by sending the X-LearnCard-Act-As header (SDK: initLearnCard({ apiKey, actAs })) — allowed only if the token's actAs permits it.

Spec objects are strict: an unknown or misspelled key fails validation rather than being dropped. Display names on the issuer, profile manager, and managed profiles are updated in place when they differ; a self-hosted signer whose registered DID differs from the spec is reported as drift. Service-account scopes, expiry, and actAs are all compared with the spec. If any of them drifts, apply errors with a revoke command: revoke the grant and re-run to create a replacement (dry-run reports drifted). With --secrets-out, a matching grant whose normalized key is missing from the secrets file gets a re-issued token. Tokens are written only to that file, never printed or stored elsewhere by the CLI.

New send --as scripts save MANAGED_DID in .env and use it with didWeb on the selected network. They reject missing or changed managed identities. Existing scripts are left untouched, with a warning to use the CLI if their issuer differs.

Preflight with doctor

npx @learncard/cli doctor

Checks this project's issuer setup against the network — identity, network reachability, API token scopes, signing authority (with a real test-sign + verify), did:web resolution, an optional --webhook-url POST ping, and whether managed credential refresh is enabled — printing one line per check with a remediation command for anything that fails. It calls network services but does not deliver credentials or provision resources. Trusted Registry verification is a manual check reported as skipped. Pass --strict to also exit non-zero on warnings, or --scopes "..." to check different permissions than the default Universal Inbox set.

Promote staging → production

npx @learncard/cli promote --from staging --to production --org ./org.yaml

Re-applies your org spec in its own .learncard/production folder (each network needs its own .env), writes fresh service-account tokens to <target>/secrets.env (or --secrets-out), and runs doctor — then prints the per-network checklist (profile, tokens, signing authority, templates, ConsentFlow contracts, credentials) that doesn't carry over; only your seed's did:key does. Add --dry-run to preview or --skip-doctor to skip the preflight.

Contributing

Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.

Please make sure to update tests as appropriate.

Who is Learning Economy Foundation?

Learning Economy Foundation (LEF) is a 501(c)(3) non-profit organization leveraging global standards and web3 protocols to bring quality skills and equal opportunity to every human on earth, and address the persistent inequities that exist around the globe in education and employment. We help you build the future of education and work with:

License

MIT © Learning Economy Foundation