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

@shenora/cli

v0.16.0

Published

Build-time CLI for Shenora — a devDependency that runs on your machine and ships inside nothing. Takes an app onto a device: build, sign, install, launch, tail the log. iOS simulators and real iPhones with no Xcode project of your own; Android emulators a

Readme

@shenora/cli

Get a Shenora app onto a real iPhone — without owning an Xcode project.

npm i -D @shenora/cli

npx shenora init            # write shenora.deploy.json
npx shenora ios doctor      # is this Mac able to build, sign and install?
npx shenora ios deploy      # build → sign → verify extensions → install → launch
npx shenora ios log         # the app's own output, off the device
npx shenora ios build       # a distributable: Release publish → .ipa
{
  "project": "src/MyApp/MyApp.csproj",
  "iosTfm": "net10.0-ios",
  "androidTfm": "net10.0-android",
  "bundleId": "com.example.myapp",
  "team": "ABCDE12345"
}

One TFM per platform. iosTfm and androidTfm name the two heads a MAUI app has. The older unqualified tfm still works and is read as the iOS one — which is exactly how it bites, so a value naming the other platform is now refused by name instead of failing minutes later inside the SDK.

Why this exists

A hybrid framework's real measure is how little native code an adopting app has to write — and the device loop is part of that. Reaching a phone normally means an Xcode project you did not want, plus a handful of failures you only learn by hitting them. Each check here is one this kit hit on real hardware:

  • set -o pipefail on every pipeline. Without it a piped command reports tail's status, which is always 0 — so a REJECTED install reports success, the launch runs against an app that was never installed, and the tool finishes by printing "running on the device".
  • App extensions are verified BEFORE install. An extension is provisioned separately from its container and will not launch without its own entitlements and embedded profile. One that cannot launch installs perfectly happily and then does nothing: a Live Activity shows as an empty capsule while every ActivityKit call reports success. A simulator cannot catch this — it does not enforce code signing.
  • It refuses to guess between two connected devices. Silently picking the first one deploys to the wrong phone and you debug the wrong build.
  • The log filters before it tails. A process-wide predicate is ~99% platform chatter, so tailing the raw stream shows a screen of noise with none of your app's lines — which looks exactly like a broken log sink.

The Mac can be somewhere else

Most .NET developers shipping to iOS work on Windows and have a Mac on the network rather than under the desk. Every ios verb takes --host:

npx shenora ios doctor --host [email protected]     # or: set SHENORA_IOS_HOST once
npx shenora ios push                            # send this tree over, uncommitted edits included
npx shenora ios deploy --simulator

push sends what git would list as source — tracked files plus anything not ignored — so bin/, obj/ and node_modules stay here. Uncommitted edits travel, deliberately: the obvious implementation is git push, and it is the wrong one for a dev loop, because the Mac would build HEAD and the fix you just made would never arrive. It adds and overwrites; it does not delete.

It needs Remote Login on (System Settings → General → Sharing) and your key in the Mac's ~/.ssh/authorized_keys. When it will not connect, doctor says which of the six unrelated causes it is — an asleep Mac, Remote Login off, a key never authorised, an .local name that does not resolve, ssh's auth-retry budget, or no ssh client here — because "cannot connect" sends you round all six.

A device build needs the Mac logged in at its screen. codesign cannot reach a login-keychain key from an ssh session — an ssh login is a different audit session, so signing dies with errSecInternalComponent no matter what you sign. The way through is to hand the build to the Mac's own GUI session, which is what this does, and it needs a session to hand it to. Simulator builds sign ad-hoc and never meet this.

shenora inspect — a device that answers back

Getting an app onto a phone is half the problem; the other half is that a phone tells you nothing. There is no console, no devtools worth the name on iOS, and the failure you care about is usually "it launched and the screen is blank".

npx shenora inspect serve            # prints a LAN URL — open it on the phone
npx shenora inspect devices          # who has checked in
npx shenora inspect report           # what the device says about itself
npx shenora inspect eval "location.href"
npx shenora ios exec "xcodebuild -version"    # run something on the Mac

The device opens the page and polls — because a webview cannot be dialled into, there is no port to open and no agent to install, so the only channel that exists is one the page itself opens. From then on you queue work and it drains the queue.

It is a devtool you start, and it ships inside nothing. @shenora/cli is a devDependency; running arbitrary JS in a page is not something that should be a flag in a product binary. A diagnostic hosted inside the app would also die with it, exactly when you need it most.

The operator half — queueing work, reading results, running a command on the Mac — is loopback only, tested. The device half is open to the LAN, because the device being diagnosed is routinely the one that cannot authenticate.

Requirements, and the two that cannot be automated

macOS with Xcode, the maui-ios workload, and an Apple Development signing identity — shenora ios doctor checks all of it and names what is missing. The Mac may be this machine or one on your LAN.

A device build needs a provisioning profile for your bundle id, and the .NET SDK cannot make one — it consumes profiles, it does not create them, so a bundle id nobody has provisioned fails with "Could not find any available provisioning profiles", an error about your app caused by a missing step the toolchain does not offer. shenora ios provision is that step: it drives xcodebuild -allowProvisioningUpdates against a throwaway project, once per bundle id.

npx shenora ios provision                          # your app
npx shenora ios provision com.example.app.widget   # …and every extension it embeds

Extensions need their own profiles. An extension is provisioned separately from its container, and forgetting one fails at the very end of a device install with an error naming the app.

Two things are yours and no tool can do them for you:

  1. A free/personal team profile expires after 7 days. Re-deploy to refresh it.
  2. A first install needs the certificate TRUSTED on the phone: Settings → General → VPN & Device Management → your developer account → Trust.

Scope, next to Capacitor

Shenora's premise is that you do not own a native project, so several of Capacitor's commands have no counterpart here — that is a difference in design, not a missing feature.

| cap | shenora | | |---|---|---| | init, doctor, copy, sync | same names | ✅ | | run (--list, --target) | ios deploy --simulator/--device, ios devices, ios simulators | ✅ | | build (a distributable) | ios build | ✅ — dotnet publish, Release by default, .ipa via ArchiveOnBuild | | add, open, ls, migrate | — | N/A by design: they manage an Xcode/Android Studio project you edit. There isn't one | | Android (cap run android) | android deploy, android log, android build | ✅ — and it runs on Windows | | — | ios … --host, inspect | no counterpart: a remote Mac, and a device that answers back |

Android is here tooandroid doctor|devices|deploy|log|build, and it runs on Windows, which is where most .NET Android work happens. It does not exist to wrap adb; it exists for the four things that are not adb:

  • finding a JDK. The Android build needs one and reports its absence from deep inside an MSBuild target as a Java error, which reads as a broken SDK. Android Studio ships one in jbr/ and sets no variable, so the common case is a machine that has one and cannot say where.
  • finding adb. Visual Studio's SDK lands in %LOCALAPPDATA%\Android\Sdk and exports nothing.
  • refusing to guess between devices. An emulator and a phone are routinely attached at once.
  • reading the log by PID rather than by tag. A tag filter has to know how your app logs — DOTNET for Console.WriteLine, something else for Android.Util.Log — and gets it wrong silently. The PID is every line your app wrote under any tag, and it excludes a stale instance.

This CLI does not build your web bundle or run your tests — it is the last mile only.