@byterm/agc-cli
v0.8.1
Published
Command-line interface for the AppGallery Connect API
Maintainers
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
- Authentication
- Global options
- Output modes
- Exit codes
- The
--dataconvention - Command reference
- Scripting & agents
Install
From the workspace root:
pnpm install
pnpm build # builds @byterm/agc-sdk then @byterm/agc-cliRun it via node:
node packages/cli/dist/index.js --helpOr 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-nodeAuthentication
Most commands use Service Account auth. 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):
--credentials <path>$AGC_CREDENTIALS./agc-credentials.json(current directory)~/.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.
pms and comments commands and report crash / report freeze are the
exception: those endpoints reject Service Account JWTs (401
client token auth failed) and require API Client auth (OAuth2 client_credentials token exchange). Create
an API Client in AGC (Users and permissions → API key → Connect API → API
Client). Two hard-won requirements:
- It must be team-level — project = N/A. A project-scoped client gets 403 on every PMS call.
- Its role must be administrator (administrator / account holder / APP administrator / operations per endpoint constraints).
API Client credentials are resolved in this order (first hit wins):
--api-client <path>$AGC_API_CLIENT./api-client.json(current directory)~/.agc/api-client.json
pms, comments and the report crash / report freeze commands load only
the API Client credentials (plus Service Account ones when also present);
every other command needs only the Service Account file.
Keep the credential file secret. The repo
.gitignoreexcludes*.private.json,agc-credentials.json,api-client.json, and.agc/.
Verify your setup:
agc auth whoami # prints the sub-account the credentials authenticate asGlobal 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) |
| --api-client <path> | Path to the API Client credentials JSON (required by pms commands) | (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 |
| --envelope | Wrap JSON output as { ok, data } / { ok, error } (implies --json) | 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.
Envelope mode (--envelope, implies --json) wraps every document in a
stable { ok, … } shape so an agent can branch on one field regardless of which
command ran — success payloads differ per command, but ok never does:
$ agc app appid com.example.app --envelope
{ "ok": true, "data": { "ret": { "code": 0, … }, "appids": [ … ] } }
$ agc app info bad-id --envelope # on failure
{ "ok": false, "error": { "error": "AgcApiError", "code": 204144641, "message": "…" } }Without --envelope, --json keeps the raw contract (the API response as the
top-level document); the exit code remains the definitive success/failure signal
either way.
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 valuesThe 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
The exit code is the definitive success/failure signal — a shell-level agent can branch on it without parsing stdout:
| Code | Meaning |
| --- | --- |
| 0 | Success |
| 1 | Generic error (unexpected failure, or a non-auth HTTP status) |
| 2 | Usage error — unknown command, or a bad/missing argument or option |
| 3 | Authentication error — missing/invalid credentials, or HTTP 401/403 |
| 4 | Network / transport error — the request never got an HTTP response (DNS, connection, timeout) |
| 5 | AGC business error — a 2xx response with a non-zero ret.code |
On error:
- Human mode prints
✗ <label>: <message>to stderr. - JSON mode prints one structured error document to stdout (wrapped in
{ ok: false, error }under--envelope):
{ "error": "AgcApiError", "code": 204144641, "message": "…", "response": { } }Error error is AgcApiError (non-zero ret.code, includes code),
AgcHttpError (network / non-2xx, includes status), an Agc*Error name such
as AgcCredentialsError, or Error. Error response/body payloads are
masked like any other output unless --no-mask is set.
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'] } },
introVideos: { 'zh-CN': { 4: [{ video: './store/v1.mp4', poster: './store/v1.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 validate / test validate — local config check
Validates a .agc/release.* / .agc/test.* config file locally — no AGC
calls, no credentials needed. Catches schema mistakes (unknown keys, wrong
types, malformed materials, mixed screenshot/video orientations, bad
releaseTime format, missing appId/packageName, …) before a run hits the
API.
agc release validate [--config <path>] [--strict]
agc test validate [--config <path>] [--strict]- Errors fail the command (exit 1): unknown keys, wrong types, bad
material shapes, orientation conflicts, invalid
releaseTime, etc. - Warnings don't fail it by default: files that don't exist on disk
(resolved relative to the current working directory, like
rundoes), unknowndeviceTypekeys,submit: truewithout update notes, orappId+packageNameboth set.--strictpromotes warnings to errors. - Keys starting with
_(e.g._comment) are exempt from unknown-key checks.
Human mode prints one line per issue (✗ errors, ! warnings); --json
prints { ok, errors: [{path, message}], warnings: [{path, message}] }.
release create <appId> <file> — production release
Runs: upload the package → bind it to the app → (config) update
language/review/material info → wait 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+0800Progress 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" --submitInviting 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 |
| --- | --- | --- |
| key gen | -a, --alias, -o, --out <p12>, --csr-out, --cn/--ou/--o/--c, --validity <days>, --password, --force | Generate an EC key pair (.p12) + CSR locally via keytool (no AGC call) |
| 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 | --type debug\|release, --ids a,b,c, -d, --data <json> | Query certificates |
| cert apply | --name, --type debug\|release, --csr <file>, --out <file>, --force, -d, --data <json> | Apply for a signing certificate; --out downloads the .cer |
| cert delete | --ids a,b,c (required), -y, --yes | Delete signing certificates (asks for confirmation) |
| profile list | --app-id <id> (required), --id <provisionId>, --from <n>, --max <n>, -d, --data <json> | List provisioning profiles |
| profile apply | --name, --type debug\|release, --app-id, --cert-id, --device-ids a,b, --acl a,b, --out <file>, --force, -d, --data <json> | Apply for a provisioning profile; --out downloads the .p7b |
| profile delete | --id <provisionId> (required), -y, --yes | Delete a provisioning profile (asks for confirmation) |
| profile devices | --id <provisionId>, --device-ids a,b (both required) | Update the test devices bound to a 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 0device 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.
For cert apply / profile apply / cert list / profile list, the typed
flags and -d, --data are mutually exclusive — --data is the raw escape
hatch (its value is sent to the API unchanged).
Signing materials, end to end
The full local-key → certificate → profile flow, ready for a HarmonyOS
build-profile.json5:
# 1. Generate the EC key pair (.p12) and CSR locally (keytool; password via
# --password, $AGC_KEY_PASSWORD, or a hidden prompt):
agc key gen --alias myapp --out myapp.p12 --cn "My App"
# 2. Apply for a certificate and download it:
agc cert apply --name myapp --type release --csr myapp.csr --out myapp.cer
# 3. Apply for a provisioning profile (cert id from `agc cert list`) and
# download it. A `debug` profile also needs `--device-ids` (ids from
# `agc device list`) — the API rejects a debug profile with no devices:
agc profile apply --name myapp --type release --app-id 1234567890 \
--cert-id 123456 --out myapp.p7bThen reference the three files in build-profile.json5 signingConfigs
(certpath → .cer, storeFile → .p12, profile → .p7b).
key gen runs entirely locally — it needs no credentials, only a JDK's
keytool (DevEco Studio and the HarmonyOS command-line tools ship one).
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 update <appId> | --version-id, --pkg-id, --desc, --countries, -d, --data | Update a (non-live) test version |
| test delete-version <appId> | --version-id, -d, --data, -y, --yes | Delete a test version |
| test detect-report <appId> | --version-id (required) | Query the listing self-check report |
| test open-test <appId> | --version-id, --start <ms>, --end <ms>, --install-limit <n>, --group-ids a,b, -d, --data | Update an open test's window / quota / group bindings |
| test promote <appId> | --version-id, --type full\|phased, --release-time <ms>, --phased-start/--phased-end/--phased-percent/--phased-desc, -d, --data, -y, --yes | Promote the active test version to a full/phased release |
| test groups <appId> | -d, --data <json> | List test groups |
| test create-group <appId> | -d, --data <json> (required) | Create a test group |
| test delete-group <appId> | --group <groupId>, -d, --data, -y, --yes | Delete a test group |
| test members <appId> | --group, --account, --nick-name, --add-way, --order-add, --order-install, --page, --page-size, -d, --data | List test group members |
| test add-members <appId> | --group, --account, --nick-name, -d, --data | Add a member to a test group (one per call) |
| test remove-members <appId> | --group, --tester-ids a,b, -d, --data, -y, --yes | Remove members from a test group |
| test notify <appId> | --group, --tester-ids a,b, -d, --data | Send test invitation notifications |
| test invite <appId> | -d, --data <json> (required) | Generate an invitation code |
| test invite-code <appId> | --group <groupId> (required) | Query a group's invitation code(s) |
| test stop-code <appId> | --code-id <id>, -d, --data, -y, --yes | Stop an invitation code |
Typed flags and -d, --data are mutually exclusive — --data is the raw
escape hatch (its value is sent to the API unchanged). Times are epoch
milliseconds unless noted.
# 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 --submitreport (Reports)
Quality metrics and analytics export files.
| Command | Args / Options | Description |
| --- | --- | --- |
| report crash <appId> | --package-name <name>, --start <t>, --end <t>, -d, --data | Crash metrics (API Client auth) |
| report freeze <appId> | (same as crash) | AppFreeze (ANR) metrics (API Client auth) |
| 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"]}}.
The crash / freeze metric endpoints accept API Client auth only
(Service Account JWTs are rejected with 401) and require both the appId
argument and the --package-name header — see
Authentication. They return aggregate
metrics per crash type (JS_ERROR / CPP_CRASH / OOM / PROCESS_KILL)
with per-fingerprint summaries (count, affected devices, first/last time,
version range); stack traces are not exposed by the API. The analytics /
export endpoints use Service Account auth like the rest of the CLI.
agc report crash 1234567890 --package-name com.example.app \
--start 2026-01-01 --end 2026-01-08 --json
agc report crash 1234567890 --package-name com.example.app \
--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"}'comments (Store reviews & ratings)
Query store reviews and rating stats, and reply to reviews. All endpoints
require API Client auth (Service Account JWTs are rejected with
205524993 client token auth failed) — see Authentication.
The caller role must be the account holder or a team member granted
管理评论 / 查看评论.
| Command | Args / Options | Description |
| --- | --- | --- |
| comments list <appId> | --countries (default CN), --start, --end, --keyword, --ratings, --versions, --reply-states, --langs, --sort, --page, --limit, -d, --data | List store reviews |
| comments ratings <appId> | --countries, --start, --end, --page, --limit | Rating stats per country + rating-only entries |
| comments detail <reviewId> | --app-id (required) | Single review detail, incl. reply thread |
| comments reply <reviewId> | --app-id, --country, --lang, --content, --to-reply-id, -d, --data, -y, --yes | Publish a public reply (confirm-guarded) |
--start / --end accept epoch milliseconds or an ISO date and default
to the last ~180 days; the API caps any query window at 180 days
(error 50010034 beyond that). --reply-states: 0 unanswered, 1
answered, 6 user follow-up, 3 follow-up answered.
agc comments list 1234567890 --limit 20
agc comments list 1234567890 --ratings 1,2 --reply-states 0 # bad reviews, unanswered
agc comments ratings 1234567890 --countries CN
agc comments reply 3211... --app-id 1234567890 --country CN --lang zh \
--content "感谢反馈,已修复,敬请期待下个版本!"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.jsonpms (Product Management)
IAP digital-product management for HarmonyOS apps: subscription groups,
products (consumables, auto-renewing subscriptions, …) and promotions (free
trials, introductory prices, …). Every command takes the appId (sent as a
header). PMS requires API Client credentials — Service Account JWTs are
rejected with 401; see Authentication for the resolution
order and the team-level (project = N/A) + administrator-role requirements.
All write commands ask for confirmation (pass -y to skip) and
auto-generate the requestId (override with --request-id). Complex payloads
go through --data (inline JSON, @file.json, or - for stdin).
| Command | Options | Description |
| --- | --- | --- |
| pms group create <appId> | --name <name> · --status <s> · -d · -y | Create a subscription group |
| pms group update <appId> <groupId> | --name <name> · -d · -y | Update a subscription group |
| pms group list <appId> | --page · --size · --order-by <json> | List subscription groups |
| pms group level <appId> <groupId> | -d (required) · -y | Update subscription group levels |
| pms product create <appId> | -d (required) · -y | Create one product (ProductInfo) |
| pms product batch-create <appId> | -d '{"products":[…]}' (required) · -y | Batch-create products |
| pms product get <appId> | --product-no / --product-id · --country | Query one product's details |
| pms product list <appId> | --country (required) · filters · paging | Query products by conditions |
| pms product update <appId> | -d (required) · -y | Update one product |
| pms product batch-update <appId> | -d '{"products":[…]}' (required) · -y | Batch-update products |
| pms product activate <appId> <nos...> | -y | Batch-activate (inactive → online) |
| pms product deactivate <appId> <nos...> | -y | Batch-deactivate (online → off sale) |
| pms product check <appId> | — | Does the app have any active product? |
| pms product review <appId> <ids...> | -y | Submit products for review (≤20 system IDs) |
| pms promotion create <appId> | -d (required) · -y | Create a promotion |
| pms promotion update <appId> | -d (required, needs promotionId) · -y | Update a promotion |
| pms promotion get <appId> <promotionId> | — | Query one promotion's details |
| pms promotion list <appId> | filters · paging | Query promotions by conditions |
| pms promotion activate <appId> <ids...> | -y | Batch-bring promotions online (≤100) |
| pms promotion deactivate <appId> <ids...> | --immediate-end · -y | Batch-take promotions offline (≤100) |
| pms app-types <appId> | --purchase-type <types> (required) | Which product types the app already has |
# Create a subscription group, then a monthly auto-renewing subscription in it
agc pms group create 1234567890 --name vip --yes
agc pms product create 1234567890 -d @monthly.json --yes
# Free-trial promotion for a subscription product
agc pms promotion create 1234567890 -d @trial.json --yes
# List everything on sale, machine-readable
agc pms product list 1234567890 --country CN --status active --jsonA minimal monthly.json (subscription products need subGroupId, subPeriod
and subPeriodUnit, and productNo/purchaseType are immutable after
creation):
{
"productNo": "vip_monthly",
"productName": "VIP 月卡",
"purchaseType": "auto_subscription",
"subGroupId": "<groupId from group create>",
"subPeriod": 1,
"subPeriodUnit": "M",
"currency": "CNY",
"country": "CN",
"defaultLocale": "zh_CN",
"defaultPrice": "1800",
"productDesc": "VIP 月度订阅",
"salesCountry": ["CN"]
}New products must be submitted for review before they take effect:
agc pms product review <appId> <productId> (the system-generated id from
pms product get, not your productNo).
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 numericcode. - Even on success, inspect
ret.codewhen present — the CLI already treats a non-zeroret.codeas an error, but downstream data lives alongsideret. - Use
--verboseto 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,cert delete,profile delete) ask for confirmation on a TTY; in--jsonor 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-maskonly 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.
