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

@byterm/agc-cli

v0.2.0

Published

Command-line interface for the AppGallery Connect API

Downloads

546

Readme

agc — AppGallery Connect CLI

A command-line interface for the AppGallery Connect (AGC) API, built on @byterm/agc-sdk. Authenticates with a Service Account and prints either human-readable tables or clean, single-document JSON for scripts and agents.

Install

From the workspace root:

pnpm install
pnpm build                 # builds @byterm/agc-sdk then @byterm/agc-cli

Run it via node:

node packages/cli/dist/index.js --help

Or expose the agc binary on your PATH:

pnpm --filter @byterm/agc-cli exec npm link     # now `agc` works anywhere
# or during development, without building:
pnpm --filter @byterm/agc-cli dev -- app info 123     # runs via ts-node

Authentication

The CLI uses Service Account auth only. Create a developer-level Service Account in AGC (Users and permissions → API key → Connect API → Service Account), download its *.private.json credential file, and make it findable.

Credentials are resolved in this order (first hit wins):

  1. --credentials <path>
  2. $AGC_CREDENTIALS
  3. ./agc-credentials.json (current directory)
  4. ~/.agc/credentials.json

The CLI signs a short-lived PS256 JWT from the credential file and sends it as Authorization: Bearer …. Nothing is sent anywhere except the AGC API host.

Keep the credential file secret. The repo .gitignore excludes *.private.json, agc-credentials.json, and .agc/.

Verify your setup:

agc auth whoami       # prints the sub-account the credentials authenticate as

Global options

These apply to every command and may appear before the subcommand:

| Flag | Description | Default | | --- | --- | --- | | -c, --credentials <path> | Path to the Service Account *.private.json file | (see resolution order) | | -s, --site <site> | Region: CN | DE | SG | RU, or an explicit base URL | CN | | --json | Emit JSON instead of formatted tables | off | | --no-mask | Do not redact sensitive values (see Masking) | masking on | | -v, --verbose | Log each HTTP request to stderr | off | | --timeout <seconds> | Per-request timeout | 60 | | -V, --version | Print the CLI version | | | -h, --help | Show help for the program or a command | |

The --site domain must match the project's configured data-processing location:

| Site | Domain | | --- | --- | | CN | connect-api.cloud.huawei.com | | DE | connect-api-dre.cloud.huawei.com | | SG | connect-api-dra.cloud.huawei.com | | RU | connect-api-drru.cloud.huawei.com |

Output modes

Human mode (default) renders tables and short status lines, e.g.:

$ agc app appid com.example.app
┌─────────┬─────────────────────┐
│ appName │ appId               │
├─────────┼─────────────────────┤
│ Term    │ 1234567890 │
└─────────┴─────────────────────┘

JSON mode (--json) prints the AGC API response as exactly one JSON document — nothing else on stdout. This is the mode to use from scripts and agents:

$ agc app appid com.example.app --json
{
  "ret": { "code": 0, "msg": "success" },
  "appids": [ { "key": "Term", "value": "1234567890" } ]
}

Human mode may show a focused subset of columns for readability; --json carries every field of the response. Both modes redact sensitive values by default — see Masking below. Add --no-mask for the full, unredacted payload.

Masking sensitive data

To avoid leaking secrets and PII into terminals, logs, screenshots or an agent's context, the CLI redacts sensitive values by default in both human and JSON output. Pass --no-mask to disable it when you genuinely need the raw values.

What gets redacted:

| Category | Examples of fields / values | Result | | --- | --- | --- | | Secrets | token, access_token, client_secret, private_key, signature, apiKey; any JWT-shaped string | fully hidden → •••••• | | Signed URLs | signature-bearing query params (HW-CC-Sign, …Signature, …token, …key) | that param's value hidden, rest of the URL kept | | PII | email, phone/mobile, qqNum, idNumber, developerName*, appReviewName, entityName, account | partially hidden, keeping a short hint → te••••et, 11••••51 |

Non-sensitive identifiers you need for scripting are never masked — e.g. appId, versionId, packageId, projectId, ret.code, status/state fields, timestamps.

agc app info 1234567890 --json                 # developer name/email redacted
agc app info 1234567890 --json --no-mask        # raw values

The auth token command is exempt (its purpose is to emit a usable token), so it always prints the real JWT regardless of masking.

Exit codes

| Code | Meaning | | --- | --- | | 0 | Success | | 1 | Any error — bad credentials, network/HTTP failure, or an AGC business error (ret.code != 0) |

On error:

  • Human mode prints ✗ <label>: <message> to stderr.
  • JSON mode prints one structured error document to stdout:
{ "error": "AgcApiError", "code": 204144641, "message": "…", "response": { } }

Error error is one of AgcApiError (2xx with non-zero ret.code, includes code), AgcHttpError (network / non-2xx, includes status), or Error.

The --data convention

Endpoints with a non-trivial request body take -d, --data, which accepts:

  • inline JSON-d '{"versionId":"123"}'
  • @file-d @body.json (reads JSON from a file)
  • --d - (reads JSON from stdin)

The value must be a JSON object. It is sent as the request body (or, for some read endpoints, as the query parameters — noted per command).


Command reference

Workflows

High-level commands that chain the individual API calls for the two common tasks. Both prepare by default and only submit with --submit — submitting sends the build to Huawei review. For the full step-by-step reference (step→API mapping, output shape, SDK equivalents, troubleshooting) see WORKFLOWS.md.

Each workflow runs two ways: from a typed config file (recommended) or from imperative flags.

# Config-driven: define once in .agc/release.ts, then:
agc release run                 # loads .agc/release.{ts,mjs,js,json}
agc release run --submit        # override the config
agc test run --submit

# Imperative (one-offs / CI):
agc release create <appId> <file> [flags]
agc test create <appId> <file> [flags]
// .agc/release.ts — typed config (autocomplete via defineRelease)
import { defineRelease } from '@byterm/agc-cli';
export default defineRelease({
  packageName: 'com.example.app',
  file: './build/entry.app',
  languages: {
    'zh-CN': { newFeatures: '修复若干问题' },
    'en-US': { newFeatures: 'Bug fixes', appDesc: '…' }, // appName/briefInfo also supported
  },
  review: { remark: 'Test guide for the review team…' }, // + testUserName/testUserPassword
  materials: { screenshots: { 'zh-CN': { 4: ['./store/1.png', './store/2.png'] } } },
  submit: false,
});

agc release run accepts --config <path>, --submit / --skip-submit, --release-time, and the compile-wait flags (--no-wait, --poll-interval, --wait-timeout, --initial-delay); agc test run accepts --config, --submit / --skip-submit. See WORKFLOWS.md → Config files for the full schema.

release create <appId> <file> — production release

Runs: upload the package → bind it to the app → (config) update language/review/material infowait for AGC to compile/parse it → (with --submit) submit for review.

| Option | Description | | --- | --- | | --submit | Submit for review (otherwise stop after compile) | | --notes <text> / --notes-file <path> | Set update notes (newFeatures) before submit | | --notes-lang <lang> | Language for the notes (default: the app's default language) | | --release-time <t> | Scheduled release time (UTC yyyy-MM-ddTHH:mm:ssZZ) | | --name <fileName> | Override the uploaded file name | | --cn-mainland <0\|1> | chineseMainlandFlag | | --no-wait | Skip the compile wait | | --initial-delay <s> | Wait before the first compile poll (AGC advises ~120) | | --poll-interval <s> | Seconds between polls (default 15) | | --wait-timeout <s> | Max seconds to wait (default 600) | | -d, --data <json> | Extra fields merged into the submit body |

# Prepare only (upload + bind + wait for compile) — safe, no review submission:
agc release create 1234567890 ./app.app --initial-delay 120

# Full release, scheduled:
agc release create 1234567890 ./app.app --submit \
  --release-time 2026-08-01T00:00:00+0800

Progress streams to stderr; the final summary (objectId, packageId, compileState, submitted) prints to stdout — one JSON object under --json. On a compile failure or timeout the command exits non-zero.

test create <appId> <file> — test version

Runs: create a test version → upload the package → attach it → (with --submit) submit the test version.

| Option | Description | | --- | --- | | --submit | Submit the test version (otherwise stop after attaching) | | --release-type <n> | Release type (default 6 = HarmonyOS test) | | --test-type <n> | Test type (e.g. 3) | | --desc <text> | Version description | | --distribute-mode <n> | Package distribution mode (default 1) | | --name <fileName> / --cn-mainland <0\|1> | Upload overrides |

agc test create 1234567890 ./app.app --desc "beta build"
agc test create 1234567890 ./app.app --desc "beta build" --submit

Inviting testers is a separate step — see test below.

Permissions: the Testing API requires the Service Account's role to grant testing access. If calls return 205524993 "client token auth failed", the account has API access but not for that API group — grant the role in AGC.

auth

Authentication helpers. No network calls except as noted.

| Command | Description | | --- | --- | | auth whoami | Print the sub-account the current credentials authenticate as | | auth token | Generate and print a Service Account JWT bearer token (human mode prints just the token; --json adds the sub-account) |

agc auth whoami
agc auth token                     # e.g. for use with curl
export TOKEN=$(agc auth token)

app (Publishing)

Manage app info, versions, packages and the release lifecycle.

| Command | Args / Options | Description | | --- | --- | --- | | app info <appId> | -l, --lang <lang>, --release-type <n>, --version-id <id> | Query app information (basic info + languages) | | app appid <packageName> | --types <types> | Resolve package name(s) → appId (comma-separated, max 50) | | app versions | -a, --app-id <id>, -p, --package-name <name>, -s, --state <state> | List versions across all types (appId and/or packageName) | | app package-info <appId> <packageId> | | Query package information | | app compile-status <appId> <pkgIds> | | Poll compile status for package id(s) (comma-separated) | | app submit <appId> | --release-time <t>, -d, --data <json>, -y, --yes | Submit the app for review / publishing | | app cancel-review <appId> <versionId> | -y, --yes | Withdraw a version from review | | app off-shelf <appId> | -r, --reason <r>, --release-type <n>, -y, --yes | Take a published app off the shelf | | app update-info <appId> | -d, --data <json> (required) | Update basic app info | | app update-package <appId> | -d, --data <json> (required) | Bind an uploaded package file to the app version | | app update-language <appId> | -l, --lang, --notes, --notes-file, --name, --desc, --brief, -d, --data | Update localized info / update notes (newFeatures) for one language | | app update-files <appId> | -d, --data <json> (required) | Bind uploaded images/videos (icon, screenshots, …) to the listing |

# Read-only
agc app info 1234567890 --lang zh-CN
agc app appid com.example.app,com.example.other
agc app versions --app-id 1234567890 --state 1
agc app compile-status 1234567890 10004151

# Release lifecycle
agc app submit 1234567890 --release-time 2026-01-01T00:00:00+0800
agc app cancel-review 1234567890 155236000
agc app off-shelf 1234567890 --reason "temporary maintenance"

# Updates (body from --data)
agc app update-info 1234567890 -d '{"defaultLang":"zh-CN"}'
agc app update-package 1234567890 -d '{"fileName":"app.app","objectId":"CN/…/x.app"}'

release-type values: 1 = full network, 6 = HarmonyOS test (requires --version-id).

upload

Upload a file to the AGC file server and print its objectId (which you then pass to app update-package). The bytes are streamed to a pre-signed URL AGC returns; the SDK computes the SHA-256 and sends the correct length.

agc upload <appId> <filePath> [--name <fileName>] [--cn-mainland <0|1>]

agc upload 1234567890 ./app.app
# ✓ File uploaded.
# objectId  CN/2026…/xxxx.app

--cn-mainland sets chineseMainlandFlag (required when the developer is registered outside mainland China).

Provisioning

Devices, signing certificates, provisioning profiles, and app-id creation.

| Command | Args / Options | Description | | --- | --- | --- | | device list | -n, --name, --from <n>, --max <n>, --order <n> | List registered test devices | | device add | -d, --data <json> (required) | Batch add devices | | cert list | -d, --data <json> | Query certificates | | cert apply | -d, --data <json> (required) | Apply for a signing certificate | | profile list | -d, --data <json> | List provisioning profiles | | profile apply | -d, --data <json> (required) | Apply for a provisioning profile | | create-appid | --project-id, --name, --package-name, --parent-type, --installation-free | Create an APP ID under a project |

agc device list --name test --max 50 --order 1
agc create-appid --project-id 10059 --name "My App" \
  --package-name com.example.app --parent-type 13 --installation-free 0

device list sort --order: 1 = create-time desc, 2 = name asc. create-appid: --parent-type 2=game / 13=app; --installation-free 0=HarmonyOS app / 1=atomic service.

test (Testing)

Test versions, groups and invitation codes. All take <appId>. For the end-to-end version flow use the test create workflow above; the commands here are the individual steps.

| Command | Args / Options | Description | | --- | --- | --- | | test create <appId> <file> | (workflow — see above) | Create version + upload + attach [+ submit] | | test new-version <appId> | -d, --data <json> (required) | Create a test version (returns versionId) | | test add-package <appId> <file> | --distribute-mode <n>, --name, --cn-mainland | Upload a package and attach it to the draft version | | test submit <appId> <versionId> | -y, --yes | Submit a test version for review | | test stop <appId> | -d, --data <json> (required), -y, --yes | Stop a test version | | test groups <appId> | -d, --data <json> | List test groups | | test create-group <appId> | -d, --data <json> (required) | Create a test group | | test invite <appId> | -d, --data <json> (required) | Generate an invitation code |

# Manual, step by step:
vid=$(agc test new-version 1234567890 -d '{"releaseType":6,"testType":3,"testDesc":"beta"}' --json | jq -r .versionId)
agc test add-package 1234567890 ./app.app --distribute-mode 1
agc test submit 1234567890 "$vid" --yes

# …or all at once with the workflow:
agc test create 1234567890 ./app.app --desc beta --submit

report (Reports)

Quality metrics and analytics export files.

| Command | Args / Options | Description | | --- | --- | --- | | report crash <appId> | --start <t>, --end <t>, --start-version, --end-version, -d, --data | Crash metrics | | report freeze <appId> | (same as crash) | AppFreeze (ANR) metrics | | report analytics | -d, --data <json> (required) | Analytics report metric data | | report export <kind> | -d, --data <json> (required) | Export file URL; <kind> = user | download | install-failed | distribute | voc |

--start / --end accept epoch milliseconds or an ISO date (e.g. 2026-01-01 or 2026-01-01T00:00:00Z), which the CLI converts to epoch ms. Add filters via --data, e.g. {"filters":{"appVersions":["26.0.4"]}}.

agc report crash 1234567890 --start 2026-01-01 --end 2026-01-08 --json
agc report crash 1234567890 --start 2026-01-01 --end 2026-01-08 \
  -d '{"filters":{"appVersions":["26.0.4"],"osVersions":["5.0.0"]}}'
agc report export download -d '{"appId":"1234567890","startDate":"20260101","endDate":"20260108"}'

domain (Domain Management)

Business-domain configuration for atomic services (元服务).

| Command | Options | Description | | --- | --- | --- | | domain get | -d, --data <json> (required) | Query domain configuration (params via --data) | | domain set | -d, --data <json> (required) | Create/update domain configuration | | domain pre-check | -d, --data <json> (required) | Pre-check a business-domain configuration | | domain limits | -d, --data <json> (required) | Query modification count / config limits |

agc domain get -d '{"appId":"1234567890"}'
agc domain set -d @domain-config.json

Scripting & agents

--json guarantees a single parseable document per invocation, and the exit code reflects success. A robust pattern:

# Resolve a package name to a bare appId
appId=$(agc app appid com.example.app --json | jq -r '.appids[0].value')

# Fetch app info, failing the script on any error
if ! info=$(agc app info "$appId" --json); then
  echo "lookup failed" >&2; exit 1
fi
echo "$info" | jq -r '.appInfo.versionNumber'

Notes for agents:

  • Always pass --json; parse stdout as one JSON object.
  • Check the process exit code (0 = ok). On failure, stdout holds { "error", "message", … } and (for API errors) a numeric code.
  • Even on success, inspect ret.code when present — the CLI already treats a non-zero ret.code as an error, but downstream data lives alongside ret.
  • Use --verbose to trace the exact HTTP requests on stderr (does not pollute the JSON on stdout).
  • Destructive commands (app submit, app cancel-review, app off-shelf, test submit, test stop) ask for confirmation on a TTY; in --json or other non-interactive use they refuse unless you pass -y, --yes.
  • Output is masked by default. Identifiers used for scripting (appId, versionId, packageId, projectId, states, timestamps) are not masked, so most pipelines work unchanged. Add --no-mask only when you specifically need a redacted value (a developer email, a signed download URL, etc.).

See also

  • @byterm/agc-sdk — the programmatic client the CLI is built on.
  • Workspace README — overview and build instructions.