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

@newinstance/bugwatch-cli

v0.1.3

Published

BugWatch CLI — upload Apple dSYM debug symbols for native crash symbolication.

Downloads

395

Readme

@newinstance/bugwatch-cli

Upload debug symbols and symbolication artifacts to BugWatch so crashes arrive symbolicated — function names, file paths, and line numbers instead of raw memory addresses and minified frames. CI-friendly, runs anywhere Node 18+ is available.

# Binary debug-symbol archives (Apple dSYM; Android native .so pre-zipped)
npx @newinstance/bugwatch-cli symbols upload MyApp.xcarchive \
  --release 1.4.2 --build-number 318

# Text artifacts (R8/ProGuard mapping, JS source map, Dart symbols)
npx @newinstance/bugwatch-cli artifacts upload mapping.txt \
  --release 1.4.2 --platform android --type r8

Which command do I need?

The two commands are not interchangeable — they validate different things and feed different parts of the symbolication pipeline.

| You have | Command | Notes | |---|---|---| | .dSYM bundle, .xcarchive, dir of dSYMs, symbols.zip | symbols upload | Apple platforms. Matched by the Mach-O debug UUID. | | Android native .so / .debug (ELF), zipped first | symbols upload native-symbols.zip --platform android | Matched by GNU build-id. Bare .so files and .so directories are rejected; only a pre-made .zip is accepted (directory scanning is dSYM-only). | | R8 / ProGuard mapping.txt | artifacts upload --type r8 | Text file, not an archive. | | JS / React Native source map (.map) | artifacts upload --type sourcemap | | | Flutter Dart --split-debug-info symbols | artifacts upload --type dart-symbols | |

symbols upload accepts binary archives only and verifies magic bytes — passing it a mapping.txt or a .map is rejected as UNRECOGNIZED_ARCHIVE.


Requirements

  • Node.js 18 or later (ESM-native package)
  • A macOS machine or macOS CI runner — dSYM files are produced by Xcode
  • A BugWatch API key with the symbols:upload permission scope (see Getting an API key)

Install

No install needed — run it on demand with npx (requires Node 18+):

npx @newinstance/bugwatch-cli symbols upload <path> ...

Or install it globally — the bin name is bugwatch, so this exposes bugwatch on $PATH:

npm install -g @newinstance/bugwatch-cli
bugwatch symbols upload <path> ...

Build from source (contributors / unreleased revisions)

To run a local build (e.g. an unreleased change, or an air-gapped runner without npm access):

# from the repo root
cd bugwatch-cli
npm install        # or: bun install
npm run build      # outputs dist/ + bin/bugwatch.mjs
node bin/bugwatch.mjs symbols upload <path> ...

Getting an API key

  1. Open the merchant dashboard for your organisation.
  2. Navigate to API Keys (project-level or org-level depending on your plan).
  3. Create a secret key (sk_test_… / sk_live_…).
  4. Make sure the key has the symbols:upload permission scope assigned. The key validator rejects requests from keys that lack this scope.
  5. Copy the full value — it is shown only once. Store it as a CI secret (never hard-code it or commit it to source control).

The key is sent on every request as the x-api-key: <keyId>:<secret> header. It is never placed in the URL or logged; the CLI prints a redacted form (sk_l…3456) for human confirmation only.


Authentication

| Method | How | |---|---| | CLI flag | --token <keyId>:<secret> | | Environment variable | BUGWATCH_AUTH_TOKEN=<keyId>:<secret> |

The environment variable is preferred for CI — set it as a secret in your pipeline so it is not visible in build logs.

The API endpoint can similarly be overridden via --api-url or BUGWATCH_API_URL (default: https://api.newinstance.cloud).


Command reference

bugwatch symbols upload <path> [options]

<path> is the only required positional argument. All metadata flags are optional but recommended — the debug UUID embedded in every dSYM is the authoritative match criterion; metadata improves search and display in the dashboard.

Flag reference

| Flag | Env | Type | Default | Description | |---|---|---|---|---| | --token | BUGWATCH_AUTH_TOKEN | string | — | Required. API secret key (keyId:secret). Never ship to a browser or app binary. | | --api-url | BUGWATCH_API_URL | string | https://api.newinstance.cloud | BugWatch REST API base URL. | | --platform | — | enum | ios | Target platform: ios · macos · tvos · watchos · visionos · catalyst | | --release | — | string | — | Marketing version (e.g. 1.4.2). Improves dashboard discoverability. | | --build-number | — | string | — | Build number (e.g. 318). Used to disambiguate the same version string across builds. | | --bundle-id | — | string | — | App bundle identifier (e.g. com.example.MyApp). | | --distribution | — | enum | — | app-store · testflight · adhoc · enterprise · development | | --environment | — | string | — | Environment tag (e.g. production, staging). | | --commit | — | string | — | Source commit SHA. Links crash reports to a specific code revision. | | --upload-source | — | string | cli | Tag identifying the upload origin (e.g. ci, fastlane, run-script). Visible in the dashboard. |

Accepted input formats

| Input | Behaviour | |---|---| | MyApp.app.dSYM | Single dSYM bundle — packed into a zip and uploaded. | | build/dSYMs/ (directory) | All *.dSYM bundles found recursively (max depth 4) are packed together into one zip. | | MyApp.xcarchive | The dSYMs/ subdirectory of the archive is walked; all discovered bundles are packed. | | symbols.zip | Uploaded as-is (packed dSYM bundles, or zipped Android ELF .so files with --platform android). | | libfoo.so or a directory of .so files | Rejected (unsupported input / no .dSYM bundles found). Zip the tree first: (cd path/to/libs && zip -r native-symbols.zip .). |

artifacts upload

bugwatch artifacts upload <path> --release <v> --platform <p> --type <t>

For text symbolication artifacts: R8/ProGuard mappings, JS source maps, and Flutter Dart symbols. Unlike symbols upload, nothing is packed — the file is streamed as-is.

| Flag | Env | Type | Default | Description | |---|---|---|---|---| | --release | — | string | — | Required. Build/release identifier. Must match the release your SDK reports, or frames will not resolve. | | --platform | — | enum | — | Required. android · ios · react-native · flutter | | --type | — | enum | — | Required. proguard · r8 · dsym · sourcemap · dart-symbols | | --build-id | — | string | — | Optional build identifier. | | --token | BUGWATCH_AUTH_TOKEN | string | — | Required. API secret key (keyId:secret). Never ship to a browser or app binary. | | --api-url | BUGWATCH_API_URL | string | https://api.newinstance.cloud | BugWatch REST API base URL. |

Uploads are idempotent per (release, platform, type) — re-uploading replaces the previous artifact and deletes the superseded file.

# React Native / Expo source map
npx @newinstance/bugwatch-cli artifacts upload \
  dist/_expo/static/js/ios/index-*.hbc.map \
  --release "1.4.2+318" --platform ios --type sourcemap

# Android R8 mapping
npx @newinstance/bugwatch-cli artifacts upload \
  app/build/outputs/mapping/release/mapping.txt \
  --release 1.4.2 --platform android --type r8

# Flutter Dart obfuscation symbols
npx @newinstance/bugwatch-cli artifacts upload build/symbols.zip \
  --release 1.4.2 --platform flutter --type dart-symbols

Pack-and-retry behaviour

  • Multiple dSYM bundles are zipped into a single symbols.zip before upload, preserving each bundle's internal directory tree.
  • Each step is retried up to 3 times on transient failures (HTTP 5xx, HTTP 429, network errors), with 500 ms / 1 000 ms / 1 500 ms back-off between attempts. A failed transfer is retried without re-presigning, and the file stream is re-opened for each attempt.
  • HTTP 4xx errors (auth failure, validation error) are not retried.
  • The temp zip is always cleaned up on exit.
  • If the server already holds an archive with the same sha256, it reports a duplicate and the transfer is skipped entirely.

How the upload works

Both commands use the same three-step flow. The file itself never passes through the BugWatch API — it is streamed directly to object storage, so uploads are not bound by request-size limits and memory stays flat regardless of file size (a 2 GiB archive uses the same memory as a 200 MiB one).

1. POST /api/v1/bugwatch/{debug-symbols,artifacts}/presign   → { uploadId, uploadUrl }
2. PUT  <uploadUrl>                                          → the bytes, streamed to storage
3. POST /api/v1/bugwatch/{…}/uploads/{uploadId}/complete      → verified, then queued

The API key is sent on steps 1 and 3 only — never to the storage host, which is authorized by the presigned URL's own signature.

Verification. The CLI declares the file's size and sha256 at step 1. Step 3 confirms the object actually landed and that its size matches. For debug symbols the worker then re-hashes the real bytes before indexing anything, and rejects a mismatch (CHECKSUM_MISMATCH) — a declared checksum is never trusted on its own.


Verifying the upload

After a successful upload the CLI prints the uploadId:

✓ uploaded: ul_abc123 [QUEUED]

You can poll the REST API to confirm indexing is complete:

curl -H "x-api-key: $BUGWATCH_AUTH_TOKEN" \
  "https://api.newinstance.cloud/api/v1/bugwatch/debug-symbols/uploads/ul_abc123"

status transitions: QUEUEDPROCESSINGAVAILABLE (ready to symbolicate) or FAILED.

You can also list recent uploads:

curl -H "x-api-key: $BUGWATCH_AUTH_TOKEN" \
  "https://api.newinstance.cloud/api/v1/bugwatch/debug-symbols/uploads?limit=10&status=AVAILABLE"

Both endpoints require the symbols:read permission scope on the key.


CI integration recipes

Every recipe follows the same four steps:

  1. Build / archive the app so Xcode produces dSYMs (set Debug Information Format = DWARF with dSYM File for the Release config).
  2. Expose BUGWATCH_AUTH_TOKEN as a secret env var in the pipeline.
  3. Run npx @newinstance/bugwatch-cli symbols upload <path> --release <v> --build-number <n>.
  4. Optionally confirm indexing via the REST API or dashboard.

Only the provider-specific wiring differs — see below.

Xcode Run Script build phase

Add a Run Script phase in Xcode (after "Compile Sources"). Set it to run for Release builds only. Xcode provides the dSYM path via a built-in env var.

if [ "$CONFIGURATION" != "Release" ] || [ "$PLATFORM_NAME" = "iphonesimulator" ]; then
  exit 0
fi
[ -z "$BUGWATCH_AUTH_TOKEN" ] && {
  echo "warning: BUGWATCH_AUTH_TOKEN unset — skipping dSYM upload"
  exit 0
}

npx @newinstance/bugwatch-cli symbols upload "$DWARF_DSYM_FOLDER_PATH" \
  --release "$MARKETING_VERSION" \
  --build-number "$CURRENT_PROJECT_VERSION" \
  --bundle-id "$PRODUCT_BUNDLE_IDENTIFIER" \
  --upload-source run-script

Store BUGWATCH_AUTH_TOKEN in Xcode build settings or a non-committed .xcconfig file — not hard-coded in the script.

Local Xcode (manual archive)

To publish dSYMs from a build you archived on your own Mac (Xcode Product → Archive) — handy for a one-off symbolication fix without wiring up CI:

  1. Open Window → Organizer → Archives, right-click your archive → Show in Finder.
  2. Drag the .xcarchive into Terminal to paste its exact path (Release archives contain a dSYMs/ folder; the CLI walks it for you).
export BUGWATCH_AUTH_TOKEN="<keyId>:<secret>"
npx @newinstance/bugwatch-cli symbols upload \
  "~/Library/Developer/Xcode/Archives/2026-06-28/MyApp 2026-06-28 10.30.xcarchive" \
  --release 1.4.2 \
  --build-number 318 \
  --bundle-id com.example.MyApp \
  --upload-source local-xcode

This is the same upload the CI recipes run, just triggered by hand. (You can also point it at the exported dSYMs/ directory or a single MyApp.app.dSYM.)

Fastlane

After build_app / gym, pass the archive path to the CLI:

build_app(scheme: "MyApp", export_method: "app-store")

sh(
  "npx @newinstance/bugwatch-cli symbols upload " \
  "'#{lane_context[SharedValues::XCODEBUILD_ARCHIVE]}' " \
  "--release '#{get_version_number}' " \
  "--build-number '#{get_build_number}' " \
  "--upload-source fastlane"
)

Set BUGWATCH_AUTH_TOKEN in your Fastfile environment, a .env file, or your CI's secret store.

GitHub Actions (macOS runner)

jobs:
  ios-build:
    runs-on: macos-14
    env:
      BUGWATCH_AUTH_TOKEN: ${{ secrets.BUGWATCH_AUTH_TOKEN }}
    steps:
      - uses: actions/checkout@v4

      - name: Select Xcode
        run: sudo xcode-select -s /Applications/Xcode_15.4.app

      - name: Archive
        run: |
          xcodebuild \
            -scheme MyApp \
            -configuration Release \
            -archivePath build/MyApp.xcarchive \
            archive

      - name: Upload dSYMs to BugWatch
        run: |
          npx @newinstance/bugwatch-cli symbols upload build/MyApp.xcarchive \
            --release "$(agvtool mvers -terse1)" \
            --build-number "${{ github.run_number }}" \
            --upload-source ci

Add BUGWATCH_AUTH_TOKEN in Settings → Secrets and variables → Actions.

Xcode Cloud

Add a ci_post_xcodebuild.sh script to the root of your repo. Xcode Cloud sets $CI_ARCHIVE_PATH to the .xcarchive produced by the workflow.

#!/bin/sh
set -e
[ -z "$BUGWATCH_AUTH_TOKEN" ] && exit 0

npx @newinstance/bugwatch-cli symbols upload "$CI_ARCHIVE_PATH" \
  --release "$CI_PRODUCT_VERSION" \
  --build-number "$CI_BUILD_NUMBER" \
  --upload-source xcode-cloud

Set BUGWATCH_AUTH_TOKEN in Xcode Cloud → Workflows → Environment → Environment Variables (mark it Secret).

Bitrise

Add a Script step after the Xcode Archive & Export for iOS step. Bitrise provides $BITRISE_XCARCHIVE_PATH (the archive) and $BITRISE_DSYM_DIR_PATH (extracted dSYMs — either works).

npx @newinstance/bugwatch-cli symbols upload "$BITRISE_XCARCHIVE_PATH" \
  --release "$BITRISE_APP_VERSION" \
  --build-number "$BITRISE_BUILD_NUMBER" \
  --upload-source bitrise

Add BUGWATCH_AUTH_TOKEN as a Secret in Bitrise Secrets.

Codemagic

Add a post-build script in your codemagic.yaml:

scripts:
  - name: Upload dSYMs to BugWatch
    script: |
      npx @newinstance/bugwatch-cli symbols upload "$CM_EXPORT_DIR" \
        --release "$CM_APP_VERSION" \
        --build-number "$CM_BUILD_NUMBER" \
        --upload-source codemagic
    environment:
      BUGWATCH_AUTH_TOKEN: $BUGWATCH_AUTH_TOKEN

Store BUGWATCH_AUTH_TOKEN in Codemagic → Team / App → Environment variables (mark it Secret / Secured).

Other macOS CI (GitLab, CircleCI, Jenkins, generic shell)

The command is the same everywhere. The only differences are how you expose the archive path and secret:

  • GitLab CI (macOS runner): add a job script step; set BUGWATCH_AUTH_TOKEN as a masked CI/CD variable in Settings → CI/CD → Variables.
  • CircleCI (macOS executor): add a run step; store the token in a Context or project environment variable.
  • Jenkins (macOS agent): add a shell build step; bind the token from a Credentials (Secret Text) entry to the BUGWATCH_AUTH_TOKEN env var.
  • Any macOS shell:
    BUGWATCH_AUTH_TOKEN=<keyId>:<secret> \
      npx @newinstance/bugwatch-cli symbols upload <archive> \
      --release 1.4.2 --build-number 318

Deployment reporting (deploy)

The CLI also reports CI deployments so BugWatch can mark releases, catch failed deploys and tag logs by deployment (requires the deploy:write scope):

bugwatch deploy start --release 1.4.2          # creates the deployment, prints/persists its id
bugwatch deploy run --stage build -- npm run build   # wraps a command as a named stage
bugwatch deploy run --stage migrate -- npm run migrate
bugwatch deploy finish                          # marks success
bugwatch deploy fail --summary "migrations failed"   # or marks failure
  • The deployment id resolves from --id, then BUGWATCH_DEPLOY_ID, then the .bugwatch-deploy file deploy start writes; on GitHub Actions it is also appended to $GITHUB_ENV.
  • deploy run requires --stage and streams the wrapped command's output as deploy logs; the command's exit code is preserved. Without a token or deploy id it warns and runs the command unreported rather than blocking your pipeline.
  • deploy fail defaults --summary to deployment failed.

Exit codes

| Code | Meaning | |---|---| | 0 | Upload accepted by the server. | | 1 | Usage or input error (missing path, no token, unsupported file type). | | 2 | Upload failure (all retries exhausted or server rejected the request). |


Troubleshooting

No dSYM file generated Confirm the scheme's Release configuration has Build Settings → Debug Information Format = DWARF with dSYM File. Simulator builds never produce dSYMs — the guard [ "$PLATFORM_NAME" = "iphonesimulator" ] in the run-script recipe handles this.

UUID mismatch — crash still not symbolicated The dSYM must come from the exact same build that crashed. Re-upload the dSYM from that specific archive. Compare UUIDs with dwarfdump --uuid MyApp.app.dSYM.

Missing framework symbols (React Native / Hermes / Flutter / third-party) Upload the full .xcarchive or the dSYMs/ directory — not just your app's .dSYM. Each bundled framework ships its own dSYM.

Duplicate upload The CLI reports (duplicate of an existing upload) and exits 0. Nothing breaks; the server de-duplicates by UUID.

Archive too large or rejected Confirm the input is a real .dSYM, .xcarchive, directory, or .zip. The server enforces a 2 GB size cap on dSYM uploads.

Authentication failure (HTTP 401) The x-api-key value is wrong, expired, or the key does not have the symbols:upload scope. Regenerate the key in the dashboard and confirm the scope is assigned.

HTTP 429 / rate-limited The CLI retries automatically with back-off. If it still fails, wait a moment and re-run — or contact support.

Apple system framework frames unresolved Expected. Your uploaded dSYMs only contain symbols for your app and bundled frameworks. Apple's private OS symbols are resolved separately (or not at all for private-framework frames).

React Native JS or Flutter Dart frames unresolved Those require source-map or --split-debug-info symbol uploads, which are separate from dSYMs and handled by a different upload flow.


Links

  • BugWatch dashboard: merchant dashboard → your project → Issues / Logs
  • REST API Swagger: https://api.newinstance.cloud/swagger.json
  • Upload status endpoint: GET /api/v1/bugwatch/debug-symbols/uploads/{uploadId}
  • iOS SDK: bug-watch-ios
  • Android SDK: cloud.newinstance:bugwatch on Maven Central
  • JS/TS SDK: @newinstance/bugwatch on npm

Package status

| Field | Value | |---|---| | Package name | @newinstance/bugwatch-cli | | Version | 0.1.2 | | npm published | Yes — 0.1.2 on npm | | Tests | bun test — 17 passing (discovery, upload retry/redaction, CLI validation) | | Platforms supported | Apple dSYM (ios, macos, tvos, watchos, visionos, catalyst) plus pre-zipped Android ELF (--platform android); text artifacts via artifacts upload (android, ios, react-native, flutter) | | Node requirement | ≥ 18 |