@onmyway133/asc-cli 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +299 -0
- package/bun.lock +231 -0
- package/dist/index.js +179 -0
- package/docs/API_COVERAGE.md +355 -0
- package/docs/openapi/latest.json +230597 -0
- package/docs/openapi/paths.txt +1208 -0
- package/openapi-ts.config.ts +25 -0
- package/package.json +32 -0
- package/scripts/gen-paths-index.py +24 -0
- package/src/api/client.ts +132 -0
- package/src/api/generated/client/client.gen.ts +298 -0
- package/src/api/generated/client/index.ts +25 -0
- package/src/api/generated/client/types.gen.ts +214 -0
- package/src/api/generated/client/utils.gen.ts +316 -0
- package/src/api/generated/client.gen.ts +16 -0
- package/src/api/generated/core/auth.gen.ts +41 -0
- package/src/api/generated/core/bodySerializer.gen.ts +82 -0
- package/src/api/generated/core/params.gen.ts +169 -0
- package/src/api/generated/core/pathSerializer.gen.ts +171 -0
- package/src/api/generated/core/queryKeySerializer.gen.ts +117 -0
- package/src/api/generated/core/serverSentEvents.gen.ts +242 -0
- package/src/api/generated/core/types.gen.ts +104 -0
- package/src/api/generated/core/utils.gen.ts +140 -0
- package/src/api/generated/index.ts +4 -0
- package/src/api/generated/sdk.gen.ts +11701 -0
- package/src/api/generated/types.gen.ts +92035 -0
- package/src/api/hey-api-client.ts +20 -0
- package/src/api/types.ts +160 -0
- package/src/auth/credentials.ts +125 -0
- package/src/auth/jwt.ts +118 -0
- package/src/commands/app-info.ts +110 -0
- package/src/commands/apps.ts +44 -0
- package/src/commands/auth.ts +327 -0
- package/src/commands/availability.ts +52 -0
- package/src/commands/beta-review.ts +145 -0
- package/src/commands/builds.ts +97 -0
- package/src/commands/game-center.ts +114 -0
- package/src/commands/iap.ts +105 -0
- package/src/commands/metadata.ts +81 -0
- package/src/commands/pricing.ts +110 -0
- package/src/commands/reports.ts +116 -0
- package/src/commands/reviews.ts +93 -0
- package/src/commands/screenshots.ts +139 -0
- package/src/commands/signing.ts +214 -0
- package/src/commands/subscriptions.ts +144 -0
- package/src/commands/testflight.ts +110 -0
- package/src/commands/versions.ts +76 -0
- package/src/commands/xcode-cloud.ts +207 -0
- package/src/index.ts +1661 -0
- package/src/utils/help-spec.ts +835 -0
- package/src/utils/output.ts +79 -0
- package/tests/auth.test.ts +105 -0
- package/tests/client.test.ts +22 -0
- package/tests/output.test.ts +36 -0
- package/tsconfig.json +15 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,1661 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* asc — App Store Connect CLI
|
|
4
|
+
*
|
|
5
|
+
* AUTHENTICATION: All API commands require a configured profile.
|
|
6
|
+
* Run `asc auth login` first to set up credentials.
|
|
7
|
+
*
|
|
8
|
+
* GETTING IDs: Most commands require resource IDs (app IDs, build IDs, etc.).
|
|
9
|
+
* Use listing commands to discover IDs before using detail/update commands:
|
|
10
|
+
* asc apps list → get App IDs
|
|
11
|
+
* asc builds list → get Build IDs
|
|
12
|
+
* asc versions list → get Version IDs
|
|
13
|
+
* asc testflight groups list → get Group IDs
|
|
14
|
+
*
|
|
15
|
+
* OUTPUT FORMATS:
|
|
16
|
+
* --output table (default when stdout is a TTY)
|
|
17
|
+
* --output json (default when stdout is piped — use for programmatic access)
|
|
18
|
+
*
|
|
19
|
+
* WHEN APPLE UPDATES THEIR API:
|
|
20
|
+
* bun run generate → re-generates src/api/generated/ from the latest OpenAPI spec
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { defineCommand, runMain } from "citty"
|
|
24
|
+
import { printError } from "./utils/output.ts"
|
|
25
|
+
import { buildHelpDocument } from "./utils/help-spec.ts"
|
|
26
|
+
import pkg from "../package.json"
|
|
27
|
+
|
|
28
|
+
// ---- Auth commands ----
|
|
29
|
+
import { authLogin, authList, authLogout, authStatus, authUse, profileView, profileUpdate, profileDelete as authProfileDelete } from "./commands/auth.ts"
|
|
30
|
+
|
|
31
|
+
// ---- App commands ----
|
|
32
|
+
import { appsList, appsGet } from "./commands/apps.ts"
|
|
33
|
+
|
|
34
|
+
// ---- Builds ----
|
|
35
|
+
import { buildsList, buildsGet, buildsUpdateBetaNotes } from "./commands/builds.ts"
|
|
36
|
+
|
|
37
|
+
// ---- Versions ----
|
|
38
|
+
import { versionsList, versionsCreate, versionsSubmit } from "./commands/versions.ts"
|
|
39
|
+
|
|
40
|
+
// ---- TestFlight ----
|
|
41
|
+
import {
|
|
42
|
+
testflightGroupsList,
|
|
43
|
+
testflightTestersList,
|
|
44
|
+
testflightTestersAdd,
|
|
45
|
+
testflightTestersRemove,
|
|
46
|
+
} from "./commands/testflight.ts"
|
|
47
|
+
|
|
48
|
+
// ---- Metadata ----
|
|
49
|
+
import { metadataList, metadataUpdate } from "./commands/metadata.ts"
|
|
50
|
+
|
|
51
|
+
// ---- Reviews ----
|
|
52
|
+
import { reviewsList, reviewsGet, reviewsRespond } from "./commands/reviews.ts"
|
|
53
|
+
|
|
54
|
+
// ---- Pricing ----
|
|
55
|
+
import { pricingScheduleGet, pricingScheduleSet, pricingPointsList, territoriesList } from "./commands/pricing.ts"
|
|
56
|
+
|
|
57
|
+
// ---- Availability ----
|
|
58
|
+
import { availabilityGet, availabilityTerritoriesSet } from "./commands/availability.ts"
|
|
59
|
+
|
|
60
|
+
// ---- IAP ----
|
|
61
|
+
import { iapList, iapGet, iapCreate, iapDelete } from "./commands/iap.ts"
|
|
62
|
+
|
|
63
|
+
// ---- Subscriptions ----
|
|
64
|
+
import {
|
|
65
|
+
subscriptionGroupsList,
|
|
66
|
+
subscriptionGroupCreate,
|
|
67
|
+
subscriptionsList,
|
|
68
|
+
subscriptionGet,
|
|
69
|
+
subscriptionCreate,
|
|
70
|
+
subscriptionPricesList,
|
|
71
|
+
} from "./commands/subscriptions.ts"
|
|
72
|
+
|
|
73
|
+
// ---- App Info ----
|
|
74
|
+
import { appInfoGet, appInfoUpdate, appCategoriesList, ageRatingDeclarationUpdate } from "./commands/app-info.ts"
|
|
75
|
+
|
|
76
|
+
// ---- Screenshots ----
|
|
77
|
+
import {
|
|
78
|
+
screenshotSetsList,
|
|
79
|
+
screenshotsList,
|
|
80
|
+
screenshotCreate,
|
|
81
|
+
screenshotDelete,
|
|
82
|
+
previewSetsList,
|
|
83
|
+
} from "./commands/screenshots.ts"
|
|
84
|
+
|
|
85
|
+
// ---- Signing ----
|
|
86
|
+
import {
|
|
87
|
+
certificatesList,
|
|
88
|
+
certificateGet,
|
|
89
|
+
certificateRevoke,
|
|
90
|
+
devicesList,
|
|
91
|
+
deviceRegister,
|
|
92
|
+
bundleIdsList,
|
|
93
|
+
bundleIdRegister,
|
|
94
|
+
profilesList,
|
|
95
|
+
profileDelete,
|
|
96
|
+
} from "./commands/signing.ts"
|
|
97
|
+
|
|
98
|
+
// ---- Reports ----
|
|
99
|
+
import { salesReportDownload, financeReportDownload, analyticsReportsList, analyticsReportRequest } from "./commands/reports.ts"
|
|
100
|
+
|
|
101
|
+
// ---- Xcode Cloud ----
|
|
102
|
+
import {
|
|
103
|
+
xcodeCloudProductsList,
|
|
104
|
+
workflowsList,
|
|
105
|
+
workflowGet,
|
|
106
|
+
xcodeCloudBuildsList,
|
|
107
|
+
xcodeCloudBuildRun,
|
|
108
|
+
xcodeCloudArtifactsList,
|
|
109
|
+
xcodeCloudTestResultsList,
|
|
110
|
+
} from "./commands/xcode-cloud.ts"
|
|
111
|
+
|
|
112
|
+
// ---- Game Center ----
|
|
113
|
+
import {
|
|
114
|
+
achievementsList,
|
|
115
|
+
achievementGet,
|
|
116
|
+
leaderboardsList,
|
|
117
|
+
leaderboardSetsList,
|
|
118
|
+
} from "./commands/game-center.ts"
|
|
119
|
+
|
|
120
|
+
// ---- Beta Review ----
|
|
121
|
+
import {
|
|
122
|
+
betaReviewSubmissionCreate,
|
|
123
|
+
betaReviewSubmissionGet,
|
|
124
|
+
betaReviewDetailGet,
|
|
125
|
+
betaReviewDetailUpdate,
|
|
126
|
+
buildBetaDetailUpdate,
|
|
127
|
+
betaBuildLocalizationsList,
|
|
128
|
+
} from "./commands/beta-review.ts"
|
|
129
|
+
|
|
130
|
+
// =============================================================================
|
|
131
|
+
// AUTH
|
|
132
|
+
// =============================================================================
|
|
133
|
+
|
|
134
|
+
const authLoginCmd = defineCommand({
|
|
135
|
+
meta: {
|
|
136
|
+
name: "login",
|
|
137
|
+
description: [
|
|
138
|
+
"Add or update an authentication profile.",
|
|
139
|
+
"Stores API key credentials for use with all other commands.",
|
|
140
|
+
"Credentials are saved to ~/.asc/credentials.json with the private key",
|
|
141
|
+
"stored in macOS Keychain (or as a file path if --bypass-keychain is set).",
|
|
142
|
+
"",
|
|
143
|
+
"To create an API key: App Store Connect → Users → Keys → Generate API Key.",
|
|
144
|
+
"Download the .p8 file (you can only download it once).",
|
|
145
|
+
"",
|
|
146
|
+
"Examples:",
|
|
147
|
+
" asc auth login --key-id ABC123 --issuer-id 12345678-... --private-key ~/Downloads/AuthKey_ABC123.p8",
|
|
148
|
+
" asc auth login --name work --key-id ABC123 --issuer-id 12345678-... --private-key ~/keys/work.p8",
|
|
149
|
+
].join("\n"),
|
|
150
|
+
},
|
|
151
|
+
args: {
|
|
152
|
+
name: { type: "string", alias: "n", description: "Profile name to save as. Defaults to 'default'. Use --name to manage multiple accounts." },
|
|
153
|
+
"key-id": { type: "string", alias: "k", description: "10-character API Key ID from App Store Connect → Users → Keys" },
|
|
154
|
+
"issuer-id": { type: "string", alias: "i", description: "Issuer ID (UUID) from App Store Connect → Users → Keys" },
|
|
155
|
+
"private-key": { type: "string", alias: "p", description: "Path to the downloaded .p8 private key file" },
|
|
156
|
+
"bypass-keychain": { type: "boolean", description: "Skip macOS Keychain — store private key path in credentials.json instead" },
|
|
157
|
+
},
|
|
158
|
+
async run({ args }) {
|
|
159
|
+
await authLogin({
|
|
160
|
+
name: args["name"],
|
|
161
|
+
keyId: args["key-id"],
|
|
162
|
+
issuerId: args["issuer-id"],
|
|
163
|
+
privateKey: args["private-key"],
|
|
164
|
+
bypassKeychain: args["bypass-keychain"],
|
|
165
|
+
})
|
|
166
|
+
},
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
const authListCmd = defineCommand({
|
|
170
|
+
meta: { name: "list", description: "List all saved authentication profiles. Shows profile names, key IDs, and which is active." },
|
|
171
|
+
run() { authList() },
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
const authUseCmd = defineCommand({
|
|
175
|
+
meta: {
|
|
176
|
+
name: "use",
|
|
177
|
+
description: "Switch the active authentication profile. All subsequent API calls will use this profile's credentials.",
|
|
178
|
+
},
|
|
179
|
+
args: { name: { type: "positional", description: "Profile name to activate (see: asc auth list)", required: false } },
|
|
180
|
+
async run({ args }) { await authUse(args.name) },
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
const authStatusCmd = defineCommand({
|
|
184
|
+
meta: {
|
|
185
|
+
name: "status",
|
|
186
|
+
description: "Show the current active profile. Use --validate to test credentials by generating a JWT and making an API call.",
|
|
187
|
+
},
|
|
188
|
+
args: { validate: { type: "boolean", description: "Generate a JWT token and validate it against the API" } },
|
|
189
|
+
async run({ args }) { await authStatus({ validate: args.validate }) },
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
const authLogoutCmd = defineCommand({
|
|
193
|
+
meta: { name: "logout", description: "Remove a profile from ~/.asc/credentials.json and delete its key from Keychain." },
|
|
194
|
+
args: { name: { type: "string", alias: "n", description: "Profile name to remove. Omit to remove the active profile." } },
|
|
195
|
+
async run({ args }) { await authLogout(args.name) },
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
const profileViewCmd = defineCommand({
|
|
199
|
+
meta: {
|
|
200
|
+
name: "view",
|
|
201
|
+
description: "Show all details for a profile (key ID, issuer ID, keychain status).",
|
|
202
|
+
},
|
|
203
|
+
args: {
|
|
204
|
+
name: { type: "positional", description: "Profile name. Omit for the active profile. Use: asc auth list", required: false },
|
|
205
|
+
},
|
|
206
|
+
run({ args }) { profileView(args.name) },
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
const profileUpdateCmd = defineCommand({
|
|
210
|
+
meta: {
|
|
211
|
+
name: "update",
|
|
212
|
+
description: [
|
|
213
|
+
"Update fields on an existing profile without re-running the full login flow.",
|
|
214
|
+
"Only the flags you provide will be changed.",
|
|
215
|
+
"Omit the profile name to update the active profile.",
|
|
216
|
+
].join("\n"),
|
|
217
|
+
},
|
|
218
|
+
args: {
|
|
219
|
+
name: { type: "positional", description: "Profile name to update. Omit for active profile.", required: false },
|
|
220
|
+
"key-id": { type: "string", description: "New API Key ID" },
|
|
221
|
+
"issuer-id": { type: "string", description: "New Issuer ID (UUID)" },
|
|
222
|
+
"private-key": { type: "string", description: "Path to new .p8 private key file" },
|
|
223
|
+
"vendor-number": { type: "string", description: "Vendor number for sales/finance reports" },
|
|
224
|
+
"bypass-keychain": { type: "boolean", description: "Store key path in file instead of Keychain" },
|
|
225
|
+
rename: { type: "string", description: "Rename the profile to a new name" },
|
|
226
|
+
},
|
|
227
|
+
async run({ args }) {
|
|
228
|
+
await profileUpdate(args.name, {
|
|
229
|
+
keyId: args["key-id"],
|
|
230
|
+
issuerId: args["issuer-id"],
|
|
231
|
+
privateKey: args["private-key"],
|
|
232
|
+
vendorNumber: args["vendor-number"],
|
|
233
|
+
bypassKeychain: args["bypass-keychain"],
|
|
234
|
+
renameTo: args["rename"],
|
|
235
|
+
})
|
|
236
|
+
},
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
const profileDeleteCmd = defineCommand({
|
|
240
|
+
meta: {
|
|
241
|
+
name: "delete",
|
|
242
|
+
description: "Remove a profile and delete its private key from Keychain.",
|
|
243
|
+
},
|
|
244
|
+
args: {
|
|
245
|
+
name: { type: "positional", description: "Profile name to delete. Use: asc auth list", required: false },
|
|
246
|
+
},
|
|
247
|
+
async run({ args }) { await authProfileDelete(args.name) },
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
const profileCmd = defineCommand({
|
|
251
|
+
meta: {
|
|
252
|
+
name: "profile",
|
|
253
|
+
description: [
|
|
254
|
+
"Manage individual authentication profiles.",
|
|
255
|
+
"Use 'asc auth list' to see all profiles.",
|
|
256
|
+
"Use --profile <name> on any command to use a specific profile.",
|
|
257
|
+
].join("\n"),
|
|
258
|
+
},
|
|
259
|
+
subCommands: {
|
|
260
|
+
view: profileViewCmd,
|
|
261
|
+
update: profileUpdateCmd,
|
|
262
|
+
delete: profileDeleteCmd,
|
|
263
|
+
},
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
const authCmd = defineCommand({
|
|
268
|
+
meta: {
|
|
269
|
+
name: "auth",
|
|
270
|
+
description: [
|
|
271
|
+
"Manage App Store Connect API authentication profiles.",
|
|
272
|
+
"Profiles store key ID, issuer ID, and private key path/content.",
|
|
273
|
+
"Multiple profiles allow switching between developer accounts.",
|
|
274
|
+
"Use 'asc auth login' to get started.",
|
|
275
|
+
].join("\n"),
|
|
276
|
+
},
|
|
277
|
+
subCommands: { login: authLoginCmd, list: authListCmd, use: authUseCmd, status: authStatusCmd, logout: authLogoutCmd, profile: profileCmd },
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
// =============================================================================
|
|
281
|
+
// APPS
|
|
282
|
+
// =============================================================================
|
|
283
|
+
|
|
284
|
+
const appsListCmd = defineCommand({
|
|
285
|
+
meta: {
|
|
286
|
+
name: "list",
|
|
287
|
+
description: [
|
|
288
|
+
"List all apps in your App Store Connect account.",
|
|
289
|
+
"Returns App IDs needed by most other commands.",
|
|
290
|
+
"Use --output json for programmatic access.",
|
|
291
|
+
].join("\n"),
|
|
292
|
+
},
|
|
293
|
+
args: {
|
|
294
|
+
limit: { type: "string", description: "Maximum number of apps to return (default: all)" },
|
|
295
|
+
output: { type: "string", alias: "o", description: "Output format: table (default in TTY) | json (default when piped)" },
|
|
296
|
+
},
|
|
297
|
+
async run({ args }) {
|
|
298
|
+
await appsList({ limit: args.limit ? Number(args.limit) : undefined, output: args.output })
|
|
299
|
+
},
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
const appsGetCmd = defineCommand({
|
|
303
|
+
meta: {
|
|
304
|
+
name: "get",
|
|
305
|
+
description: [
|
|
306
|
+
"Get detailed information about a specific app.",
|
|
307
|
+
"Use 'asc apps list' to find the App ID.",
|
|
308
|
+
].join("\n"),
|
|
309
|
+
},
|
|
310
|
+
args: {
|
|
311
|
+
"app-id": { type: "positional", description: "App ID (numeric string, e.g. '1234567890'). Use: asc apps list", required: true },
|
|
312
|
+
output: { type: "string", alias: "o", description: "Output format: table | json" },
|
|
313
|
+
},
|
|
314
|
+
async run({ args }) { await appsGet(args["app-id"], { output: args.output }) },
|
|
315
|
+
})
|
|
316
|
+
|
|
317
|
+
const appsCmd = defineCommand({
|
|
318
|
+
meta: {
|
|
319
|
+
name: "apps",
|
|
320
|
+
description: "List and inspect apps in your account. App IDs are required by most other commands.",
|
|
321
|
+
},
|
|
322
|
+
subCommands: { list: appsListCmd, get: appsGetCmd },
|
|
323
|
+
})
|
|
324
|
+
|
|
325
|
+
// =============================================================================
|
|
326
|
+
// BUILDS
|
|
327
|
+
// =============================================================================
|
|
328
|
+
|
|
329
|
+
const buildsListCmd = defineCommand({
|
|
330
|
+
meta: {
|
|
331
|
+
name: "list",
|
|
332
|
+
description: [
|
|
333
|
+
"List builds uploaded to App Store Connect.",
|
|
334
|
+
"Builds are created by uploading via Xcode, Instruments, or Transporter.",
|
|
335
|
+
"Use --app-id to filter by app. Build IDs are needed by testflight and beta-review commands.",
|
|
336
|
+
].join("\n"),
|
|
337
|
+
},
|
|
338
|
+
args: {
|
|
339
|
+
"app-id": { type: "string", description: "Filter by App ID. Use: asc apps list" },
|
|
340
|
+
version: { type: "string", description: "Filter by version string (e.g. '2.0.1')" },
|
|
341
|
+
platform: { type: "string", description: "Filter by platform: IOS | MAC_OS | TV_OS | VISION_OS" },
|
|
342
|
+
output: { type: "string", alias: "o", description: "Output format: table | json" },
|
|
343
|
+
},
|
|
344
|
+
async run({ args }) {
|
|
345
|
+
await buildsList({ appId: args["app-id"], version: args.version, platform: args.platform, output: args.output })
|
|
346
|
+
},
|
|
347
|
+
})
|
|
348
|
+
|
|
349
|
+
const buildsGetCmd = defineCommand({
|
|
350
|
+
meta: {
|
|
351
|
+
name: "get",
|
|
352
|
+
description: "Get full details for a specific build including processing state and expiry date.",
|
|
353
|
+
},
|
|
354
|
+
args: {
|
|
355
|
+
"build-id": { type: "positional", description: "Build ID (UUID). Use: asc builds list", required: true },
|
|
356
|
+
output: { type: "string", alias: "o", description: "Output format: table | json" },
|
|
357
|
+
},
|
|
358
|
+
async run({ args }) { await buildsGet(args["build-id"], { output: args.output }) },
|
|
359
|
+
})
|
|
360
|
+
|
|
361
|
+
const buildsUpdateBetaNotesCmd = defineCommand({
|
|
362
|
+
meta: {
|
|
363
|
+
name: "update-beta-notes",
|
|
364
|
+
description: [
|
|
365
|
+
"Set or update the TestFlight 'What to Test' notes for a build.",
|
|
366
|
+
"These notes are shown to testers when they receive the build.",
|
|
367
|
+
].join("\n"),
|
|
368
|
+
},
|
|
369
|
+
args: {
|
|
370
|
+
"build-id": { type: "string", description: "Build ID (UUID). Use: asc builds list", required: true },
|
|
371
|
+
locale: { type: "string", description: "Locale code, e.g. 'en-US', 'ja', 'fr-FR'", required: true },
|
|
372
|
+
notes: { type: "string", description: "What to test text (max 4000 characters)", required: true },
|
|
373
|
+
},
|
|
374
|
+
async run({ args }) {
|
|
375
|
+
await buildsUpdateBetaNotes({ buildId: args["build-id"], locale: args.locale, notes: args.notes })
|
|
376
|
+
},
|
|
377
|
+
})
|
|
378
|
+
|
|
379
|
+
const buildsCmd = defineCommand({
|
|
380
|
+
meta: {
|
|
381
|
+
name: "builds",
|
|
382
|
+
description: "Manage app builds. Builds are uploaded via Xcode or Transporter and used for TestFlight and App Store submission.",
|
|
383
|
+
},
|
|
384
|
+
subCommands: { list: buildsListCmd, get: buildsGetCmd, "update-beta-notes": buildsUpdateBetaNotesCmd },
|
|
385
|
+
})
|
|
386
|
+
|
|
387
|
+
// =============================================================================
|
|
388
|
+
// VERSIONS
|
|
389
|
+
// =============================================================================
|
|
390
|
+
|
|
391
|
+
const versionsListCmd = defineCommand({
|
|
392
|
+
meta: {
|
|
393
|
+
name: "list",
|
|
394
|
+
description: [
|
|
395
|
+
"List App Store versions for an app.",
|
|
396
|
+
"Each version has an ID used by metadata, screenshots, and submission commands.",
|
|
397
|
+
"States: PREPARE_FOR_SUBMISSION, WAITING_FOR_REVIEW, IN_REVIEW, READY_FOR_SALE, etc.",
|
|
398
|
+
].join("\n"),
|
|
399
|
+
},
|
|
400
|
+
args: {
|
|
401
|
+
"app-id": { type: "string", description: "App ID. Use: asc apps list", required: true },
|
|
402
|
+
platform: { type: "string", description: "Filter by platform: IOS | MAC_OS | TV_OS | VISION_OS" },
|
|
403
|
+
output: { type: "string", alias: "o", description: "Output format: table | json" },
|
|
404
|
+
},
|
|
405
|
+
async run({ args }) {
|
|
406
|
+
await versionsList({ appId: args["app-id"], platform: args.platform, output: args.output })
|
|
407
|
+
},
|
|
408
|
+
})
|
|
409
|
+
|
|
410
|
+
const versionsCreateCmd = defineCommand({
|
|
411
|
+
meta: {
|
|
412
|
+
name: "create",
|
|
413
|
+
description: [
|
|
414
|
+
"Create a new App Store version record.",
|
|
415
|
+
"This creates the version container for metadata, screenshots, and submission.",
|
|
416
|
+
"Increment the version string from the previous release.",
|
|
417
|
+
].join("\n"),
|
|
418
|
+
},
|
|
419
|
+
args: {
|
|
420
|
+
"app-id": { type: "string", description: "App ID. Use: asc apps list", required: true },
|
|
421
|
+
version: { type: "string", description: "Version string in MAJOR.MINOR.PATCH format, e.g. '2.1.0'", required: true },
|
|
422
|
+
platform: { type: "string", description: "Platform: IOS (default) | MAC_OS | TV_OS | VISION_OS", default: "IOS" },
|
|
423
|
+
},
|
|
424
|
+
async run({ args }) {
|
|
425
|
+
await versionsCreate({ appId: args["app-id"], version: args.version, platform: args.platform })
|
|
426
|
+
},
|
|
427
|
+
})
|
|
428
|
+
|
|
429
|
+
const versionsSubmitCmd = defineCommand({
|
|
430
|
+
meta: {
|
|
431
|
+
name: "submit",
|
|
432
|
+
description: [
|
|
433
|
+
"Submit an App Store version for App Review.",
|
|
434
|
+
"Prerequisites: metadata is complete, screenshots are uploaded, binary is attached.",
|
|
435
|
+
"Use 'asc versions list' to find the Version ID.",
|
|
436
|
+
].join("\n"),
|
|
437
|
+
},
|
|
438
|
+
args: { "version-id": { type: "positional", description: "Version ID (UUID). Use: asc versions list --app-id <id>", required: true } },
|
|
439
|
+
async run({ args }) { await versionsSubmit(args["version-id"]) },
|
|
440
|
+
})
|
|
441
|
+
|
|
442
|
+
const versionsCmd = defineCommand({
|
|
443
|
+
meta: {
|
|
444
|
+
name: "versions",
|
|
445
|
+
description: "Manage App Store version records. Versions hold metadata, screenshots, and track review state.",
|
|
446
|
+
},
|
|
447
|
+
subCommands: { list: versionsListCmd, create: versionsCreateCmd, submit: versionsSubmitCmd },
|
|
448
|
+
})
|
|
449
|
+
|
|
450
|
+
// =============================================================================
|
|
451
|
+
// TESTFLIGHT
|
|
452
|
+
// =============================================================================
|
|
453
|
+
|
|
454
|
+
const tfGroupsListCmd = defineCommand({
|
|
455
|
+
meta: {
|
|
456
|
+
name: "list",
|
|
457
|
+
description: "List all TestFlight beta groups. Groups control which testers get access to builds. Use Group IDs to manage testers.",
|
|
458
|
+
},
|
|
459
|
+
args: {
|
|
460
|
+
"app-id": { type: "string", description: "Filter by App ID. Use: asc apps list" },
|
|
461
|
+
output: { type: "string", alias: "o", description: "Output format: table | json" },
|
|
462
|
+
},
|
|
463
|
+
async run({ args }) { await testflightGroupsList({ appId: args["app-id"], output: args.output }) },
|
|
464
|
+
})
|
|
465
|
+
|
|
466
|
+
const tfGroupsCmd = defineCommand({
|
|
467
|
+
meta: { name: "groups", description: "Manage TestFlight beta groups (tester groups)" },
|
|
468
|
+
subCommands: { list: tfGroupsListCmd },
|
|
469
|
+
})
|
|
470
|
+
|
|
471
|
+
const tfTestersListCmd = defineCommand({
|
|
472
|
+
meta: { name: "list", description: "List all testers in a TestFlight group." },
|
|
473
|
+
args: {
|
|
474
|
+
"group-id": { type: "string", description: "Beta Group ID. Use: asc testflight groups list", required: true },
|
|
475
|
+
output: { type: "string", alias: "o", description: "Output format: table | json" },
|
|
476
|
+
},
|
|
477
|
+
async run({ args }) { await testflightTestersList({ groupId: args["group-id"], output: args.output }) },
|
|
478
|
+
})
|
|
479
|
+
|
|
480
|
+
const tfTestersAddCmd = defineCommand({
|
|
481
|
+
meta: {
|
|
482
|
+
name: "add",
|
|
483
|
+
description: [
|
|
484
|
+
"Add a tester to a TestFlight group.",
|
|
485
|
+
"The tester will be invited via email and can install the app via TestFlight.",
|
|
486
|
+
"Provide email to create a new tester, or omit for a placeholder.",
|
|
487
|
+
].join("\n"),
|
|
488
|
+
},
|
|
489
|
+
args: {
|
|
490
|
+
"group-id": { type: "string", description: "Beta Group ID. Use: asc testflight groups list", required: true },
|
|
491
|
+
email: { type: "string", description: "Tester email address" },
|
|
492
|
+
"first-name": { type: "string", description: "Tester first name" },
|
|
493
|
+
"last-name": { type: "string", description: "Tester last name" },
|
|
494
|
+
},
|
|
495
|
+
async run({ args }) {
|
|
496
|
+
await testflightTestersAdd({ groupId: args["group-id"], email: args.email, firstName: args["first-name"], lastName: args["last-name"] })
|
|
497
|
+
},
|
|
498
|
+
})
|
|
499
|
+
|
|
500
|
+
const tfTestersRemoveCmd = defineCommand({
|
|
501
|
+
meta: { name: "remove", description: "Remove a tester from a TestFlight group. The tester will lose access to the build." },
|
|
502
|
+
args: {
|
|
503
|
+
"group-id": { type: "string", description: "Beta Group ID. Use: asc testflight groups list", required: true },
|
|
504
|
+
"tester-id": { type: "string", description: "Tester ID (UUID). Use: asc testflight testers list --group-id <id>", required: true },
|
|
505
|
+
},
|
|
506
|
+
async run({ args }) {
|
|
507
|
+
await testflightTestersRemove({ groupId: args["group-id"], testerId: args["tester-id"] })
|
|
508
|
+
},
|
|
509
|
+
})
|
|
510
|
+
|
|
511
|
+
const tfTestersCmd = defineCommand({
|
|
512
|
+
meta: { name: "testers", description: "Add, remove, and list TestFlight testers within a group" },
|
|
513
|
+
subCommands: { list: tfTestersListCmd, add: tfTestersAddCmd, remove: tfTestersRemoveCmd },
|
|
514
|
+
})
|
|
515
|
+
|
|
516
|
+
const testflightCmd = defineCommand({
|
|
517
|
+
meta: {
|
|
518
|
+
name: "testflight",
|
|
519
|
+
description: [
|
|
520
|
+
"Manage TestFlight beta testing.",
|
|
521
|
+
"Workflow: asc testflight groups list → asc testflight testers add → distribute build to group.",
|
|
522
|
+
].join("\n"),
|
|
523
|
+
},
|
|
524
|
+
subCommands: { groups: tfGroupsCmd, testers: tfTestersCmd },
|
|
525
|
+
})
|
|
526
|
+
|
|
527
|
+
// =============================================================================
|
|
528
|
+
// METADATA
|
|
529
|
+
// =============================================================================
|
|
530
|
+
|
|
531
|
+
const metadataListCmd = defineCommand({
|
|
532
|
+
meta: {
|
|
533
|
+
name: "list",
|
|
534
|
+
description: "List all localizations for an App Store version. Shows locale, description, keywords, and promotional text.",
|
|
535
|
+
},
|
|
536
|
+
args: {
|
|
537
|
+
"version-id": { type: "string", description: "Version ID (UUID). Use: asc versions list --app-id <id>", required: true },
|
|
538
|
+
output: { type: "string", alias: "o", description: "Output format: table | json" },
|
|
539
|
+
},
|
|
540
|
+
async run({ args }) { await metadataList({ versionId: args["version-id"], output: args.output }) },
|
|
541
|
+
})
|
|
542
|
+
|
|
543
|
+
const metadataUpdateCmd = defineCommand({
|
|
544
|
+
meta: {
|
|
545
|
+
name: "update",
|
|
546
|
+
description: [
|
|
547
|
+
"Update App Store metadata for a specific locale.",
|
|
548
|
+
"Provide one or more text fields to update.",
|
|
549
|
+
"Locale format: 'en-US', 'ja', 'fr-FR', 'de-DE', 'zh-Hans', etc.",
|
|
550
|
+
"",
|
|
551
|
+
"Example: asc metadata update --version-id <id> --locale en-US --whats-new 'Bug fixes'",
|
|
552
|
+
].join("\n"),
|
|
553
|
+
},
|
|
554
|
+
args: {
|
|
555
|
+
"version-id": { type: "string", description: "Version ID (UUID). Use: asc versions list --app-id <id>", required: true },
|
|
556
|
+
locale: { type: "string", description: "Locale code, e.g. 'en-US', 'ja', 'fr-FR'", required: true },
|
|
557
|
+
"whats-new": { type: "string", description: "What's new in this version (shown in App Store update notes)" },
|
|
558
|
+
description: { type: "string", description: "Full app description text" },
|
|
559
|
+
keywords: { type: "string", description: "Search keywords, comma-separated (max 100 chars total)" },
|
|
560
|
+
"promotional-text": { type: "string", description: "Promotional text shown above the description (can be changed without a new submission)" },
|
|
561
|
+
},
|
|
562
|
+
async run({ args }) {
|
|
563
|
+
await metadataUpdate({
|
|
564
|
+
versionId: args["version-id"],
|
|
565
|
+
locale: args.locale,
|
|
566
|
+
whatsNew: args["whats-new"],
|
|
567
|
+
description: args.description,
|
|
568
|
+
keywords: args.keywords,
|
|
569
|
+
promotionalText: args["promotional-text"],
|
|
570
|
+
})
|
|
571
|
+
},
|
|
572
|
+
})
|
|
573
|
+
|
|
574
|
+
const metadataCmd = defineCommand({
|
|
575
|
+
meta: {
|
|
576
|
+
name: "metadata",
|
|
577
|
+
description: "Manage App Store listing text: descriptions, keywords, what's new, promotional text. Supports all locales.",
|
|
578
|
+
},
|
|
579
|
+
subCommands: { list: metadataListCmd, update: metadataUpdateCmd },
|
|
580
|
+
})
|
|
581
|
+
|
|
582
|
+
// =============================================================================
|
|
583
|
+
// REVIEWS (Customer Reviews)
|
|
584
|
+
// =============================================================================
|
|
585
|
+
|
|
586
|
+
const reviewsListCmd = defineCommand({
|
|
587
|
+
meta: {
|
|
588
|
+
name: "list",
|
|
589
|
+
description: "List customer reviews from the App Store. Returns rating, title, body, and territory.",
|
|
590
|
+
},
|
|
591
|
+
args: {
|
|
592
|
+
"app-id": { type: "string", description: "App ID. Use: asc apps list", required: true },
|
|
593
|
+
output: { type: "string", alias: "o", description: "Output format: table | json" },
|
|
594
|
+
},
|
|
595
|
+
async run({ args }) { await reviewsList({ appId: args["app-id"], output: args.output }) },
|
|
596
|
+
})
|
|
597
|
+
|
|
598
|
+
const reviewsGetCmd = defineCommand({
|
|
599
|
+
meta: { name: "get", description: "Get a specific customer review by ID." },
|
|
600
|
+
args: {
|
|
601
|
+
"review-id": { type: "positional", description: "Review ID. Use: asc reviews list --app-id <id>", required: true },
|
|
602
|
+
output: { type: "string", alias: "o", description: "Output format: table | json" },
|
|
603
|
+
},
|
|
604
|
+
async run({ args }) { await reviewsGet(args["review-id"], { output: args.output }) },
|
|
605
|
+
})
|
|
606
|
+
|
|
607
|
+
const reviewsRespondCmd = defineCommand({
|
|
608
|
+
meta: {
|
|
609
|
+
name: "respond",
|
|
610
|
+
description: [
|
|
611
|
+
"Post a developer response to a customer review.",
|
|
612
|
+
"Responses are public and visible in the App Store.",
|
|
613
|
+
"You can only respond once per review.",
|
|
614
|
+
].join("\n"),
|
|
615
|
+
},
|
|
616
|
+
args: {
|
|
617
|
+
"review-id": { type: "string", description: "Review ID. Use: asc reviews list --app-id <id>", required: true },
|
|
618
|
+
message: { type: "string", description: "Response text (public, visible in App Store)", required: true },
|
|
619
|
+
},
|
|
620
|
+
async run({ args }) { await reviewsRespond({ reviewId: args["review-id"], message: args.message }) },
|
|
621
|
+
})
|
|
622
|
+
|
|
623
|
+
const reviewsCmd = defineCommand({
|
|
624
|
+
meta: {
|
|
625
|
+
name: "reviews",
|
|
626
|
+
description: "Read and respond to App Store customer reviews.",
|
|
627
|
+
},
|
|
628
|
+
subCommands: { list: reviewsListCmd, get: reviewsGetCmd, respond: reviewsRespondCmd },
|
|
629
|
+
})
|
|
630
|
+
|
|
631
|
+
// =============================================================================
|
|
632
|
+
// PRICING
|
|
633
|
+
// =============================================================================
|
|
634
|
+
|
|
635
|
+
const pricingScheduleGetCmd = defineCommand({
|
|
636
|
+
meta: {
|
|
637
|
+
name: "get",
|
|
638
|
+
description: "Get the price schedule for an app including base territory and manual price overrides.",
|
|
639
|
+
},
|
|
640
|
+
args: {
|
|
641
|
+
"app-id": { type: "string", description: "App ID. Use: asc apps list", required: true },
|
|
642
|
+
output: { type: "string", alias: "o", description: "Output format: table | json" },
|
|
643
|
+
},
|
|
644
|
+
async run({ args }) { await pricingScheduleGet(args["app-id"], { output: args.output }) },
|
|
645
|
+
})
|
|
646
|
+
|
|
647
|
+
const pricingScheduleSetCmd = defineCommand({
|
|
648
|
+
meta: {
|
|
649
|
+
name: "set",
|
|
650
|
+
description: [
|
|
651
|
+
"Set the price for an app using a price point ID and base territory.",
|
|
652
|
+
"Use 'asc pricing points list --app-id <id>' to find available price points.",
|
|
653
|
+
"Base territory sets the reference currency (e.g. 'USA' for USD).",
|
|
654
|
+
].join("\n"),
|
|
655
|
+
},
|
|
656
|
+
args: {
|
|
657
|
+
"app-id": { type: "string", description: "App ID. Use: asc apps list", required: true },
|
|
658
|
+
"base-territory": { type: "string", description: "Base territory code, e.g. 'USA', 'GBR', 'EUR'. Use: asc pricing territories list", required: true },
|
|
659
|
+
"price-point-id": { type: "string", description: "Price point ID. Use: asc pricing points list --app-id <id>", required: true },
|
|
660
|
+
},
|
|
661
|
+
async run({ args }) {
|
|
662
|
+
await pricingScheduleSet({ appId: args["app-id"], baseTerritory: args["base-territory"], pricePointId: args["price-point-id"] })
|
|
663
|
+
},
|
|
664
|
+
})
|
|
665
|
+
|
|
666
|
+
const pricingPointsListCmd = defineCommand({
|
|
667
|
+
meta: {
|
|
668
|
+
name: "list",
|
|
669
|
+
description: [
|
|
670
|
+
"List available price points for an app.",
|
|
671
|
+
"Price points represent standard App Store price tiers.",
|
|
672
|
+
"Use --territory to filter for a specific storefront currency.",
|
|
673
|
+
].join("\n"),
|
|
674
|
+
},
|
|
675
|
+
args: {
|
|
676
|
+
"app-id": { type: "string", description: "App ID. Use: asc apps list", required: true },
|
|
677
|
+
territory: { type: "string", description: "Filter by territory code, e.g. 'USA', 'GBR'" },
|
|
678
|
+
output: { type: "string", alias: "o", description: "Output format: table | json" },
|
|
679
|
+
},
|
|
680
|
+
async run({ args }) {
|
|
681
|
+
await pricingPointsList({ appId: args["app-id"], territory: args.territory, output: args.output })
|
|
682
|
+
},
|
|
683
|
+
})
|
|
684
|
+
|
|
685
|
+
const territoriesListCmd = defineCommand({
|
|
686
|
+
meta: {
|
|
687
|
+
name: "list",
|
|
688
|
+
description: "List all App Store territories (storefronts) with their currency codes.",
|
|
689
|
+
},
|
|
690
|
+
args: { output: { type: "string", alias: "o", description: "Output format: table | json" } },
|
|
691
|
+
async run({ args }) { await territoriesList({ output: args.output }) },
|
|
692
|
+
})
|
|
693
|
+
|
|
694
|
+
const pricingCmd = defineCommand({
|
|
695
|
+
meta: {
|
|
696
|
+
name: "pricing",
|
|
697
|
+
description: [
|
|
698
|
+
"Manage app pricing: schedules, price points, and territory availability.",
|
|
699
|
+
"Workflow: asc pricing territories list → asc pricing points list --app-id <id> → asc pricing set",
|
|
700
|
+
].join("\n"),
|
|
701
|
+
},
|
|
702
|
+
subCommands: {
|
|
703
|
+
get: pricingScheduleGetCmd,
|
|
704
|
+
set: pricingScheduleSetCmd,
|
|
705
|
+
points: defineCommand({ meta: { name: "points", description: "Manage price points" }, subCommands: { list: pricingPointsListCmd } }),
|
|
706
|
+
territories: defineCommand({ meta: { name: "territories", description: "List App Store territories" }, subCommands: { list: territoriesListCmd } }),
|
|
707
|
+
},
|
|
708
|
+
})
|
|
709
|
+
|
|
710
|
+
// =============================================================================
|
|
711
|
+
// AVAILABILITY
|
|
712
|
+
// =============================================================================
|
|
713
|
+
|
|
714
|
+
const availabilityGetCmd = defineCommand({
|
|
715
|
+
meta: {
|
|
716
|
+
name: "get",
|
|
717
|
+
description: "Get the territory availability settings for an app, including which storefronts it is available in.",
|
|
718
|
+
},
|
|
719
|
+
args: {
|
|
720
|
+
"app-id": { type: "string", description: "App ID. Use: asc apps list", required: true },
|
|
721
|
+
output: { type: "string", alias: "o", description: "Output format: table | json" },
|
|
722
|
+
},
|
|
723
|
+
async run({ args }) { await availabilityGet(args["app-id"], { output: args.output }) },
|
|
724
|
+
})
|
|
725
|
+
|
|
726
|
+
const availabilitySetCmd = defineCommand({
|
|
727
|
+
meta: {
|
|
728
|
+
name: "set",
|
|
729
|
+
description: [
|
|
730
|
+
"Set which App Store territories the app is available in.",
|
|
731
|
+
"Provide a comma-separated list of territory codes.",
|
|
732
|
+
"Territory codes: USA, GBR, AUS, CAN, JPN, DEU, FRA, CHN, etc.",
|
|
733
|
+
"Use: asc pricing territories list to see all codes.",
|
|
734
|
+
].join("\n"),
|
|
735
|
+
},
|
|
736
|
+
args: {
|
|
737
|
+
"app-id": { type: "string", description: "App ID. Use: asc apps list", required: true },
|
|
738
|
+
territories: { type: "string", description: "Comma-separated territory codes, e.g. 'USA,GBR,AUS,CAN'", required: true },
|
|
739
|
+
"new-territories": { type: "boolean", description: "Allow distribution in newly added App Store territories automatically", default: true },
|
|
740
|
+
},
|
|
741
|
+
async run({ args }) {
|
|
742
|
+
const territories = args.territories.split(",").map(t => t.trim()).filter(Boolean)
|
|
743
|
+
await availabilityTerritoriesSet({ appId: args["app-id"], territories, availableInNewTerritories: args["new-territories"] })
|
|
744
|
+
},
|
|
745
|
+
})
|
|
746
|
+
|
|
747
|
+
const availabilityCmd = defineCommand({
|
|
748
|
+
meta: { name: "availability", description: "Manage which App Store territories an app is available in." },
|
|
749
|
+
subCommands: { get: availabilityGetCmd, set: availabilitySetCmd },
|
|
750
|
+
})
|
|
751
|
+
|
|
752
|
+
// =============================================================================
|
|
753
|
+
// IAP (In-App Purchases)
|
|
754
|
+
// =============================================================================
|
|
755
|
+
|
|
756
|
+
const iapListCmd = defineCommand({
|
|
757
|
+
meta: {
|
|
758
|
+
name: "list",
|
|
759
|
+
description: "List in-app purchases for an app. Types: CONSUMABLE, NON_CONSUMABLE, NON_RENEWING_SUBSCRIPTION.",
|
|
760
|
+
},
|
|
761
|
+
args: {
|
|
762
|
+
"app-id": { type: "string", description: "App ID. Use: asc apps list", required: true },
|
|
763
|
+
output: { type: "string", alias: "o", description: "Output format: table | json" },
|
|
764
|
+
},
|
|
765
|
+
async run({ args }) { await iapList({ appId: args["app-id"], output: args.output }) },
|
|
766
|
+
})
|
|
767
|
+
|
|
768
|
+
const iapGetCmd = defineCommand({
|
|
769
|
+
meta: { name: "get", description: "Get details for a specific in-app purchase by its ID." },
|
|
770
|
+
args: {
|
|
771
|
+
"iap-id": { type: "positional", description: "IAP ID. Use: asc iap list --app-id <id>", required: true },
|
|
772
|
+
output: { type: "string", alias: "o", description: "Output format: table | json" },
|
|
773
|
+
},
|
|
774
|
+
async run({ args }) { await iapGet(args["iap-id"], { output: args.output }) },
|
|
775
|
+
})
|
|
776
|
+
|
|
777
|
+
const iapCreateCmd = defineCommand({
|
|
778
|
+
meta: {
|
|
779
|
+
name: "create",
|
|
780
|
+
description: [
|
|
781
|
+
"Create a new in-app purchase product.",
|
|
782
|
+
"Types: CONSUMABLE | NON_CONSUMABLE | NON_RENEWING_SUBSCRIPTION",
|
|
783
|
+
"The product ID must be unique within your app (e.g. 'com.yourapp.coins100').",
|
|
784
|
+
].join("\n"),
|
|
785
|
+
},
|
|
786
|
+
args: {
|
|
787
|
+
"app-id": { type: "string", description: "App ID. Use: asc apps list", required: true },
|
|
788
|
+
name: { type: "string", description: "Display name for the IAP (internal reference name)", required: true },
|
|
789
|
+
"product-id": { type: "string", description: "Unique product ID string, e.g. 'com.yourapp.premium'", required: true },
|
|
790
|
+
type: { type: "string", description: "IAP type: CONSUMABLE | NON_CONSUMABLE | NON_RENEWING_SUBSCRIPTION", required: true },
|
|
791
|
+
},
|
|
792
|
+
async run({ args }) {
|
|
793
|
+
await iapCreate({ appId: args["app-id"], name: args.name, productId: args["product-id"], type: args.type })
|
|
794
|
+
},
|
|
795
|
+
})
|
|
796
|
+
|
|
797
|
+
const iapDeleteCmd = defineCommand({
|
|
798
|
+
meta: { name: "delete", description: "Delete a draft in-app purchase. Cannot delete approved or submitted IAPs." },
|
|
799
|
+
args: { "iap-id": { type: "positional", description: "IAP ID. Use: asc iap list --app-id <id>", required: true } },
|
|
800
|
+
async run({ args }) { await iapDelete(args["iap-id"]) },
|
|
801
|
+
})
|
|
802
|
+
|
|
803
|
+
const iapCmd = defineCommand({
|
|
804
|
+
meta: {
|
|
805
|
+
name: "iap",
|
|
806
|
+
description: [
|
|
807
|
+
"Manage in-app purchases (consumables, non-consumables, non-renewing subscriptions).",
|
|
808
|
+
"For auto-renewable subscriptions, use: asc subscriptions",
|
|
809
|
+
].join("\n"),
|
|
810
|
+
},
|
|
811
|
+
subCommands: { list: iapListCmd, get: iapGetCmd, create: iapCreateCmd, delete: iapDeleteCmd },
|
|
812
|
+
})
|
|
813
|
+
|
|
814
|
+
// =============================================================================
|
|
815
|
+
// SUBSCRIPTIONS
|
|
816
|
+
// =============================================================================
|
|
817
|
+
|
|
818
|
+
const subGroupsListCmd = defineCommand({
|
|
819
|
+
meta: {
|
|
820
|
+
name: "list",
|
|
821
|
+
description: "List subscription groups for an app. Groups organize related subscription tiers.",
|
|
822
|
+
},
|
|
823
|
+
args: {
|
|
824
|
+
"app-id": { type: "string", description: "App ID. Use: asc apps list", required: true },
|
|
825
|
+
output: { type: "string", alias: "o", description: "Output format: table | json" },
|
|
826
|
+
},
|
|
827
|
+
async run({ args }) { await subscriptionGroupsList({ appId: args["app-id"], output: args.output }) },
|
|
828
|
+
})
|
|
829
|
+
|
|
830
|
+
const subGroupCreateCmd = defineCommand({
|
|
831
|
+
meta: { name: "create", description: "Create a new subscription group. Groups hold related subscription tiers." },
|
|
832
|
+
args: {
|
|
833
|
+
"app-id": { type: "string", description: "App ID. Use: asc apps list", required: true },
|
|
834
|
+
"reference-name": { type: "string", description: "Internal reference name for this group (not shown to users)", required: true },
|
|
835
|
+
},
|
|
836
|
+
async run({ args }) {
|
|
837
|
+
await subscriptionGroupCreate({ appId: args["app-id"], referenceName: args["reference-name"] })
|
|
838
|
+
},
|
|
839
|
+
})
|
|
840
|
+
|
|
841
|
+
const subGroupsCmd = defineCommand({
|
|
842
|
+
meta: { name: "groups", description: "Manage subscription groups" },
|
|
843
|
+
subCommands: { list: subGroupsListCmd, create: subGroupCreateCmd },
|
|
844
|
+
})
|
|
845
|
+
|
|
846
|
+
const subscriptionsListCmd = defineCommand({
|
|
847
|
+
meta: { name: "list", description: "List auto-renewable subscriptions in a subscription group." },
|
|
848
|
+
args: {
|
|
849
|
+
"group-id": { type: "string", description: "Subscription Group ID. Use: asc subscriptions groups list --app-id <id>", required: true },
|
|
850
|
+
output: { type: "string", alias: "o", description: "Output format: table | json" },
|
|
851
|
+
},
|
|
852
|
+
async run({ args }) { await subscriptionsList({ groupId: args["group-id"], output: args.output }) },
|
|
853
|
+
})
|
|
854
|
+
|
|
855
|
+
const subscriptionGetCmd = defineCommand({
|
|
856
|
+
meta: { name: "get", description: "Get details for a specific auto-renewable subscription." },
|
|
857
|
+
args: {
|
|
858
|
+
"sub-id": { type: "positional", description: "Subscription ID. Use: asc subscriptions list --group-id <id>", required: true },
|
|
859
|
+
output: { type: "string", alias: "o", description: "Output format: table | json" },
|
|
860
|
+
},
|
|
861
|
+
async run({ args }) { await subscriptionGet(args["sub-id"], { output: args.output }) },
|
|
862
|
+
})
|
|
863
|
+
|
|
864
|
+
const subscriptionCreateCmd = defineCommand({
|
|
865
|
+
meta: {
|
|
866
|
+
name: "create",
|
|
867
|
+
description: [
|
|
868
|
+
"Create a new auto-renewable subscription product.",
|
|
869
|
+
"Periods: ONE_WEEK | ONE_MONTH | TWO_MONTHS | THREE_MONTHS | SIX_MONTHS | ONE_YEAR",
|
|
870
|
+
].join("\n"),
|
|
871
|
+
},
|
|
872
|
+
args: {
|
|
873
|
+
"group-id": { type: "string", description: "Subscription Group ID. Use: asc subscriptions groups list --app-id <id>", required: true },
|
|
874
|
+
name: { type: "string", description: "Internal reference name (not user-facing)", required: true },
|
|
875
|
+
"product-id": { type: "string", description: "Unique product ID, e.g. 'com.yourapp.premium.monthly'", required: true },
|
|
876
|
+
period: { type: "string", description: "Billing period: ONE_WEEK | ONE_MONTH | TWO_MONTHS | THREE_MONTHS | SIX_MONTHS | ONE_YEAR", required: true },
|
|
877
|
+
},
|
|
878
|
+
async run({ args }) {
|
|
879
|
+
await subscriptionCreate({ groupId: args["group-id"], name: args.name, productId: args["product-id"], period: args.period })
|
|
880
|
+
},
|
|
881
|
+
})
|
|
882
|
+
|
|
883
|
+
const subscriptionPricesListCmd = defineCommand({
|
|
884
|
+
meta: { name: "prices", description: "List price schedule for a subscription across territories." },
|
|
885
|
+
args: {
|
|
886
|
+
"sub-id": { type: "string", description: "Subscription ID. Use: asc subscriptions list --group-id <id>", required: true },
|
|
887
|
+
territory: { type: "string", description: "Filter by territory code, e.g. 'USA'" },
|
|
888
|
+
output: { type: "string", alias: "o", description: "Output format: table | json" },
|
|
889
|
+
},
|
|
890
|
+
async run({ args }) {
|
|
891
|
+
await subscriptionPricesList({ subId: args["sub-id"], territory: args.territory, output: args.output })
|
|
892
|
+
},
|
|
893
|
+
})
|
|
894
|
+
|
|
895
|
+
const subscriptionsCmd = defineCommand({
|
|
896
|
+
meta: {
|
|
897
|
+
name: "subscriptions",
|
|
898
|
+
description: [
|
|
899
|
+
"Manage auto-renewable subscription products.",
|
|
900
|
+
"Workflow: asc subscriptions groups list → asc subscriptions list → asc subscriptions create",
|
|
901
|
+
].join("\n"),
|
|
902
|
+
},
|
|
903
|
+
subCommands: {
|
|
904
|
+
groups: subGroupsCmd,
|
|
905
|
+
list: subscriptionsListCmd,
|
|
906
|
+
get: subscriptionGetCmd,
|
|
907
|
+
create: subscriptionCreateCmd,
|
|
908
|
+
prices: subscriptionPricesListCmd,
|
|
909
|
+
},
|
|
910
|
+
})
|
|
911
|
+
|
|
912
|
+
// =============================================================================
|
|
913
|
+
// APP-INFO
|
|
914
|
+
// =============================================================================
|
|
915
|
+
|
|
916
|
+
const appInfoGetCmd = defineCommand({
|
|
917
|
+
meta: {
|
|
918
|
+
name: "get",
|
|
919
|
+
description: "Get app info records showing age rating, content rating, and category for an app.",
|
|
920
|
+
},
|
|
921
|
+
args: {
|
|
922
|
+
"app-id": { type: "string", description: "App ID. Use: asc apps list", required: true },
|
|
923
|
+
output: { type: "string", alias: "o", description: "Output format: table | json" },
|
|
924
|
+
},
|
|
925
|
+
async run({ args }) { await appInfoGet(args["app-id"], { output: args.output }) },
|
|
926
|
+
})
|
|
927
|
+
|
|
928
|
+
const appInfoUpdateCmd = defineCommand({
|
|
929
|
+
meta: {
|
|
930
|
+
name: "update",
|
|
931
|
+
description: [
|
|
932
|
+
"Update the primary or secondary category for an app info record.",
|
|
933
|
+
"Use 'asc app-info categories' to see available category IDs.",
|
|
934
|
+
"Info ID comes from: asc app-info get --app-id <id>",
|
|
935
|
+
].join("\n"),
|
|
936
|
+
},
|
|
937
|
+
args: {
|
|
938
|
+
"info-id": { type: "string", description: "App Info ID. Use: asc app-info get --app-id <id>", required: true },
|
|
939
|
+
"primary-category": { type: "string", description: "Primary category ID, e.g. 'GAMES', 'PRODUCTIVITY'. Use: asc app-info categories list" },
|
|
940
|
+
"secondary-category": { type: "string", description: "Secondary category ID (optional)" },
|
|
941
|
+
},
|
|
942
|
+
async run({ args }) {
|
|
943
|
+
await appInfoUpdate({ infoId: args["info-id"], primaryCategoryId: args["primary-category"], secondaryCategoryId: args["secondary-category"] })
|
|
944
|
+
},
|
|
945
|
+
})
|
|
946
|
+
|
|
947
|
+
const appCategoriesListCmd = defineCommand({
|
|
948
|
+
meta: { name: "list", description: "List all App Store categories. Use these IDs with 'asc app-info update'." },
|
|
949
|
+
args: {
|
|
950
|
+
platform: { type: "string", description: "Filter by platform: IOS | MAC_OS | TV_OS" },
|
|
951
|
+
output: { type: "string", alias: "o", description: "Output format: table | json" },
|
|
952
|
+
},
|
|
953
|
+
async run({ args }) { await appCategoriesList({ platform: args.platform, output: args.output }) },
|
|
954
|
+
})
|
|
955
|
+
|
|
956
|
+
const appInfoCmd = defineCommand({
|
|
957
|
+
meta: { name: "app-info", description: "Manage app info: categories, age ratings, content declarations." },
|
|
958
|
+
subCommands: {
|
|
959
|
+
get: appInfoGetCmd,
|
|
960
|
+
update: appInfoUpdateCmd,
|
|
961
|
+
categories: defineCommand({ meta: { name: "categories", description: "App category management" }, subCommands: { list: appCategoriesListCmd } }),
|
|
962
|
+
},
|
|
963
|
+
})
|
|
964
|
+
|
|
965
|
+
// =============================================================================
|
|
966
|
+
// SCREENSHOTS
|
|
967
|
+
// =============================================================================
|
|
968
|
+
|
|
969
|
+
const screenshotSetsListCmd = defineCommand({
|
|
970
|
+
meta: {
|
|
971
|
+
name: "list",
|
|
972
|
+
description: [
|
|
973
|
+
"List screenshot sets for a version localization.",
|
|
974
|
+
"Each set holds screenshots for a specific device display type.",
|
|
975
|
+
"Localization IDs come from: asc metadata list --version-id <id>",
|
|
976
|
+
].join("\n"),
|
|
977
|
+
},
|
|
978
|
+
args: {
|
|
979
|
+
"localization-id": { type: "string", description: "Version Localization ID. Use: asc metadata list --version-id <id>", required: true },
|
|
980
|
+
output: { type: "string", alias: "o", description: "Output format: table | json" },
|
|
981
|
+
},
|
|
982
|
+
async run({ args }) { await screenshotSetsList({ versionLocalizationId: args["localization-id"], output: args.output }) },
|
|
983
|
+
})
|
|
984
|
+
|
|
985
|
+
const screenshotsListCmd = defineCommand({
|
|
986
|
+
meta: {
|
|
987
|
+
name: "list",
|
|
988
|
+
description: "List screenshots in a screenshot set. Shows file names, sizes, and upload state.",
|
|
989
|
+
},
|
|
990
|
+
args: {
|
|
991
|
+
"set-id": { type: "string", description: "Screenshot Set ID. Use: asc screenshots sets list --localization-id <id>", required: true },
|
|
992
|
+
output: { type: "string", alias: "o", description: "Output format: table | json" },
|
|
993
|
+
},
|
|
994
|
+
async run({ args }) { await screenshotsList({ setId: args["set-id"], output: args.output }) },
|
|
995
|
+
})
|
|
996
|
+
|
|
997
|
+
const screenshotUploadCmd = defineCommand({
|
|
998
|
+
meta: {
|
|
999
|
+
name: "upload",
|
|
1000
|
+
description: [
|
|
1001
|
+
"Upload a screenshot image file to a screenshot set.",
|
|
1002
|
+
"Supported formats: PNG, JPEG.",
|
|
1003
|
+
"Screenshot sets correspond to device types (e.g. 6.5-inch iPhone display).",
|
|
1004
|
+
].join("\n"),
|
|
1005
|
+
},
|
|
1006
|
+
args: {
|
|
1007
|
+
"set-id": { type: "string", description: "Screenshot Set ID. Use: asc screenshots sets list --localization-id <id>", required: true },
|
|
1008
|
+
file: { type: "string", description: "Path to the screenshot image file (PNG or JPEG)", required: true },
|
|
1009
|
+
},
|
|
1010
|
+
async run({ args }) { await screenshotCreate({ setId: args["set-id"], filePath: args.file }) },
|
|
1011
|
+
})
|
|
1012
|
+
|
|
1013
|
+
const screenshotDeleteCmd = defineCommand({
|
|
1014
|
+
meta: { name: "delete", description: "Delete a screenshot from a screenshot set." },
|
|
1015
|
+
args: { "screenshot-id": { type: "positional", description: "Screenshot ID. Use: asc screenshots list --set-id <id>", required: true } },
|
|
1016
|
+
async run({ args }) { await screenshotDelete(args["screenshot-id"]) },
|
|
1017
|
+
})
|
|
1018
|
+
|
|
1019
|
+
const previewSetsListCmd = defineCommand({
|
|
1020
|
+
meta: { name: "list", description: "List app preview sets for a version localization." },
|
|
1021
|
+
args: {
|
|
1022
|
+
"localization-id": { type: "string", description: "Version Localization ID. Use: asc metadata list --version-id <id>", required: true },
|
|
1023
|
+
output: { type: "string", alias: "o", description: "Output format: table | json" },
|
|
1024
|
+
},
|
|
1025
|
+
async run({ args }) { await previewSetsList({ versionLocalizationId: args["localization-id"], output: args.output }) },
|
|
1026
|
+
})
|
|
1027
|
+
|
|
1028
|
+
const screenshotsCmd = defineCommand({
|
|
1029
|
+
meta: {
|
|
1030
|
+
name: "screenshots",
|
|
1031
|
+
description: "Manage App Store screenshots and app previews. Workflow: list sets → list screenshots → upload/delete.",
|
|
1032
|
+
},
|
|
1033
|
+
subCommands: {
|
|
1034
|
+
sets: defineCommand({
|
|
1035
|
+
meta: { name: "sets", description: "Screenshot set management" },
|
|
1036
|
+
subCommands: { list: screenshotSetsListCmd },
|
|
1037
|
+
}),
|
|
1038
|
+
list: screenshotsListCmd,
|
|
1039
|
+
upload: screenshotUploadCmd,
|
|
1040
|
+
delete: screenshotDeleteCmd,
|
|
1041
|
+
previews: defineCommand({
|
|
1042
|
+
meta: { name: "previews", description: "App preview sets" },
|
|
1043
|
+
subCommands: { list: previewSetsListCmd },
|
|
1044
|
+
}),
|
|
1045
|
+
},
|
|
1046
|
+
})
|
|
1047
|
+
|
|
1048
|
+
// =============================================================================
|
|
1049
|
+
// SIGNING
|
|
1050
|
+
// =============================================================================
|
|
1051
|
+
|
|
1052
|
+
const certsListCmd = defineCommand({
|
|
1053
|
+
meta: {
|
|
1054
|
+
name: "list",
|
|
1055
|
+
description: [
|
|
1056
|
+
"List certificates in your account.",
|
|
1057
|
+
"Types: IOS_DEVELOPMENT | IOS_DISTRIBUTION | MAC_APP_DISTRIBUTION | MAC_INSTALLER_DISTRIBUTION",
|
|
1058
|
+
" DEVELOPER_ID_APPLICATION | DEVELOPER_ID_INSTALLER",
|
|
1059
|
+
].join("\n"),
|
|
1060
|
+
},
|
|
1061
|
+
args: {
|
|
1062
|
+
type: { type: "string", description: "Filter by certificate type, e.g. 'IOS_DISTRIBUTION'" },
|
|
1063
|
+
output: { type: "string", alias: "o", description: "Output format: table | json" },
|
|
1064
|
+
},
|
|
1065
|
+
async run({ args }) { await certificatesList({ type: args.type, output: args.output }) },
|
|
1066
|
+
})
|
|
1067
|
+
|
|
1068
|
+
const certsGetCmd = defineCommand({
|
|
1069
|
+
meta: { name: "get", description: "Get certificate details by ID." },
|
|
1070
|
+
args: {
|
|
1071
|
+
"cert-id": { type: "positional", description: "Certificate ID. Use: asc signing certs list", required: true },
|
|
1072
|
+
output: { type: "string", alias: "o", description: "Output format: table | json" },
|
|
1073
|
+
},
|
|
1074
|
+
async run({ args }) { await certificateGet(args["cert-id"], { output: args.output }) },
|
|
1075
|
+
})
|
|
1076
|
+
|
|
1077
|
+
const certsRevokeCmd = defineCommand({
|
|
1078
|
+
meta: { name: "revoke", description: "Revoke a certificate. This will invalidate any provisioning profiles using this certificate." },
|
|
1079
|
+
args: { "cert-id": { type: "positional", description: "Certificate ID. Use: asc signing certs list", required: true } },
|
|
1080
|
+
async run({ args }) { await certificateRevoke(args["cert-id"]) },
|
|
1081
|
+
})
|
|
1082
|
+
|
|
1083
|
+
const certsCmd = defineCommand({
|
|
1084
|
+
meta: { name: "certs", description: "Manage signing certificates" },
|
|
1085
|
+
subCommands: { list: certsListCmd, get: certsGetCmd, revoke: certsRevokeCmd },
|
|
1086
|
+
})
|
|
1087
|
+
|
|
1088
|
+
const devicesListCmd = defineCommand({
|
|
1089
|
+
meta: {
|
|
1090
|
+
name: "list",
|
|
1091
|
+
description: "List registered test devices. Only enabled devices can be included in development/ad-hoc provisioning profiles.",
|
|
1092
|
+
},
|
|
1093
|
+
args: {
|
|
1094
|
+
platform: { type: "string", description: "Filter by platform: IOS | MAC_OS | TV_OS" },
|
|
1095
|
+
status: { type: "string", description: "Filter by status: ENABLED | DISABLED" },
|
|
1096
|
+
output: { type: "string", alias: "o", description: "Output format: table | json" },
|
|
1097
|
+
},
|
|
1098
|
+
async run({ args }) { await devicesList({ platform: args.platform, status: args.status, output: args.output }) },
|
|
1099
|
+
})
|
|
1100
|
+
|
|
1101
|
+
const devicesRegisterCmd = defineCommand({
|
|
1102
|
+
meta: {
|
|
1103
|
+
name: "register",
|
|
1104
|
+
description: [
|
|
1105
|
+
"Register a new device for development/testing.",
|
|
1106
|
+
"The UDID can be found in Xcode → Devices & Simulators, or in System Information.",
|
|
1107
|
+
"Platforms: IOS | MAC_OS | TV_OS | WATCH_OS | VISION_OS",
|
|
1108
|
+
].join("\n"),
|
|
1109
|
+
},
|
|
1110
|
+
args: {
|
|
1111
|
+
name: { type: "string", description: "Display name for the device", required: true },
|
|
1112
|
+
udid: { type: "string", description: "Device UDID (40 hex characters for iOS, 12+2+12 for Mac)", required: true },
|
|
1113
|
+
platform: { type: "string", description: "Platform: IOS | MAC_OS | TV_OS | WATCH_OS | VISION_OS", required: true },
|
|
1114
|
+
},
|
|
1115
|
+
async run({ args }) { await deviceRegister({ name: args.name, udid: args.udid, platform: args.platform }) },
|
|
1116
|
+
})
|
|
1117
|
+
|
|
1118
|
+
const devicesCmd = defineCommand({
|
|
1119
|
+
meta: { name: "devices", description: "Register and list test devices for development profiles" },
|
|
1120
|
+
subCommands: { list: devicesListCmd, register: devicesRegisterCmd },
|
|
1121
|
+
})
|
|
1122
|
+
|
|
1123
|
+
const bundleIdsListCmd = defineCommand({
|
|
1124
|
+
meta: { name: "list", description: "List registered bundle IDs (App IDs) in your account." },
|
|
1125
|
+
args: {
|
|
1126
|
+
platform: { type: "string", description: "Filter by platform: IOS | MAC_OS | UNIVERSAL" },
|
|
1127
|
+
identifier: { type: "string", description: "Filter by bundle identifier string, e.g. 'com.yourapp'" },
|
|
1128
|
+
output: { type: "string", alias: "o", description: "Output format: table | json" },
|
|
1129
|
+
},
|
|
1130
|
+
async run({ args }) { await bundleIdsList({ platform: args.platform, identifier: args.identifier, output: args.output }) },
|
|
1131
|
+
})
|
|
1132
|
+
|
|
1133
|
+
const bundleIdsRegisterCmd = defineCommand({
|
|
1134
|
+
meta: { name: "register", description: "Register a new bundle ID (App ID). Required before creating provisioning profiles." },
|
|
1135
|
+
args: {
|
|
1136
|
+
name: { type: "string", description: "Display name for this bundle ID", required: true },
|
|
1137
|
+
identifier: { type: "string", description: "Bundle identifier string, e.g. 'com.yourcompany.yourapp'", required: true },
|
|
1138
|
+
platform: { type: "string", description: "Platform: IOS | MAC_OS | UNIVERSAL", required: true },
|
|
1139
|
+
},
|
|
1140
|
+
async run({ args }) { await bundleIdRegister({ name: args.name, identifier: args.identifier, platform: args.platform }) },
|
|
1141
|
+
})
|
|
1142
|
+
|
|
1143
|
+
const bundleIdsCmd = defineCommand({
|
|
1144
|
+
meta: { name: "bundle-ids", description: "Register and list bundle IDs (App IDs)" },
|
|
1145
|
+
subCommands: { list: bundleIdsListCmd, register: bundleIdsRegisterCmd },
|
|
1146
|
+
})
|
|
1147
|
+
|
|
1148
|
+
const profilesListCmd = defineCommand({
|
|
1149
|
+
meta: {
|
|
1150
|
+
name: "list",
|
|
1151
|
+
description: [
|
|
1152
|
+
"List provisioning profiles in your account.",
|
|
1153
|
+
"Types: IOS_APP_DEVELOPMENT | IOS_APP_STORE | IOS_APP_ADHOC | IOS_APP_INHOUSE",
|
|
1154
|
+
" MAC_APP_DEVELOPMENT | MAC_APP_STORE | MAC_APP_DIRECT | TVOS_APP_DEVELOPMENT",
|
|
1155
|
+
].join("\n"),
|
|
1156
|
+
},
|
|
1157
|
+
args: {
|
|
1158
|
+
type: { type: "string", description: "Filter by profile type, e.g. 'IOS_APP_STORE'" },
|
|
1159
|
+
name: { type: "string", description: "Filter by profile name (partial match)" },
|
|
1160
|
+
output: { type: "string", alias: "o", description: "Output format: table | json" },
|
|
1161
|
+
},
|
|
1162
|
+
async run({ args }) { await profilesList({ type: args.type, name: args.name, output: args.output }) },
|
|
1163
|
+
})
|
|
1164
|
+
|
|
1165
|
+
const profilesDeleteCmd = defineCommand({
|
|
1166
|
+
meta: { name: "delete", description: "Delete a provisioning profile." },
|
|
1167
|
+
args: { "profile-id": { type: "positional", description: "Profile ID. Use: asc signing profiles list", required: true } },
|
|
1168
|
+
async run({ args }) { await profileDelete(args["profile-id"]) },
|
|
1169
|
+
})
|
|
1170
|
+
|
|
1171
|
+
const profilesCmd = defineCommand({
|
|
1172
|
+
meta: { name: "profiles", description: "List and delete provisioning profiles" },
|
|
1173
|
+
subCommands: { list: profilesListCmd, delete: profilesDeleteCmd },
|
|
1174
|
+
})
|
|
1175
|
+
|
|
1176
|
+
const signingCmd = defineCommand({
|
|
1177
|
+
meta: {
|
|
1178
|
+
name: "signing",
|
|
1179
|
+
description: [
|
|
1180
|
+
"Manage code signing resources: certificates, devices, bundle IDs, and provisioning profiles.",
|
|
1181
|
+
"Workflow: bundle-ids register → devices register → certs list → profiles list",
|
|
1182
|
+
].join("\n"),
|
|
1183
|
+
},
|
|
1184
|
+
subCommands: { certs: certsCmd, devices: devicesCmd, "bundle-ids": bundleIdsCmd, profiles: profilesCmd },
|
|
1185
|
+
})
|
|
1186
|
+
|
|
1187
|
+
// =============================================================================
|
|
1188
|
+
// REPORTS
|
|
1189
|
+
// =============================================================================
|
|
1190
|
+
|
|
1191
|
+
const salesReportCmd = defineCommand({
|
|
1192
|
+
meta: {
|
|
1193
|
+
name: "sales",
|
|
1194
|
+
description: [
|
|
1195
|
+
"Download a sales and trends report in TSV format (gzip decompressed).",
|
|
1196
|
+
"By default outputs raw TSV. Use --output json to get structured JSON.",
|
|
1197
|
+
"",
|
|
1198
|
+
"Frequencies: DAILY | WEEKLY | MONTHLY | YEARLY",
|
|
1199
|
+
"Report types: SALES | SUBSCRIPTION | SUBSCRIPTION_EVENT | SUBSCRIBER | PRE_ORDER",
|
|
1200
|
+
"Report date format: YYYY-MM-DD for DAILY, YYYY-MM for MONTHLY, YYYY for YEARLY",
|
|
1201
|
+
"",
|
|
1202
|
+
"Example: asc reports sales --vendor-number 12345678 --frequency MONTHLY --date 2024-01",
|
|
1203
|
+
].join("\n"),
|
|
1204
|
+
},
|
|
1205
|
+
args: {
|
|
1206
|
+
"vendor-number": { type: "string", description: "Vendor number from App Store Connect → Payments & Financial Reports", required: true },
|
|
1207
|
+
frequency: { type: "string", description: "Report frequency: DAILY | WEEKLY | MONTHLY | YEARLY", required: true },
|
|
1208
|
+
date: { type: "string", description: "Report date. DAILY: YYYY-MM-DD, MONTHLY: YYYY-MM, YEARLY: YYYY", required: true },
|
|
1209
|
+
"report-type": { type: "string", description: "Report type: SALES (default) | SUBSCRIPTION | SUBSCRIPTION_EVENT | SUBSCRIBER | PRE_ORDER" },
|
|
1210
|
+
"report-subtype": { type: "string", description: "Report subtype: SUMMARY (default) | DETAILED" },
|
|
1211
|
+
output: { type: "string", alias: "o", description: "Output format: tsv (default) | json" },
|
|
1212
|
+
},
|
|
1213
|
+
async run({ args }) {
|
|
1214
|
+
await salesReportDownload({
|
|
1215
|
+
vendorNumber: args["vendor-number"],
|
|
1216
|
+
frequency: args.frequency,
|
|
1217
|
+
reportDate: args.date,
|
|
1218
|
+
reportType: args["report-type"],
|
|
1219
|
+
reportSubType: args["report-subtype"],
|
|
1220
|
+
output: args.output,
|
|
1221
|
+
})
|
|
1222
|
+
},
|
|
1223
|
+
})
|
|
1224
|
+
|
|
1225
|
+
const financeReportCmd = defineCommand({
|
|
1226
|
+
meta: {
|
|
1227
|
+
name: "finance",
|
|
1228
|
+
description: [
|
|
1229
|
+
"Download a financial report for a given region and month.",
|
|
1230
|
+
"Region codes: US, EU, WW, JP, etc.",
|
|
1231
|
+
"Report date format: YYYY-MM",
|
|
1232
|
+
"",
|
|
1233
|
+
"Example: asc reports finance --vendor-number 12345678 --region US --date 2024-01",
|
|
1234
|
+
].join("\n"),
|
|
1235
|
+
},
|
|
1236
|
+
args: {
|
|
1237
|
+
"vendor-number": { type: "string", description: "Vendor number from App Store Connect → Payments & Financial Reports", required: true },
|
|
1238
|
+
region: { type: "string", description: "Region code: US | EU | WW | JP | CN | AU | CA | GB | etc.", required: true },
|
|
1239
|
+
date: { type: "string", description: "Report month in YYYY-MM format", required: true },
|
|
1240
|
+
output: { type: "string", alias: "o", description: "Output format: tsv (default) | json" },
|
|
1241
|
+
},
|
|
1242
|
+
async run({ args }) {
|
|
1243
|
+
await financeReportDownload({ vendorNumber: args["vendor-number"], regionCode: args.region, reportDate: args.date, output: args.output })
|
|
1244
|
+
},
|
|
1245
|
+
})
|
|
1246
|
+
|
|
1247
|
+
const analyticsListCmd = defineCommand({
|
|
1248
|
+
meta: { name: "list", description: "List analytics report requests for an app." },
|
|
1249
|
+
args: {
|
|
1250
|
+
"app-id": { type: "string", description: "App ID. Use: asc apps list", required: true },
|
|
1251
|
+
output: { type: "string", alias: "o", description: "Output format: table | json" },
|
|
1252
|
+
},
|
|
1253
|
+
async run({ args }) { await analyticsReportsList({ appId: args["app-id"], output: args.output }) },
|
|
1254
|
+
})
|
|
1255
|
+
|
|
1256
|
+
const analyticsRequestCmd = defineCommand({
|
|
1257
|
+
meta: {
|
|
1258
|
+
name: "request",
|
|
1259
|
+
description: [
|
|
1260
|
+
"Request an analytics report for an app.",
|
|
1261
|
+
"Access types: ONE_TIME_SNAPSHOT | ONGOING",
|
|
1262
|
+
"Reports are generated asynchronously. Check status with: asc reports analytics list",
|
|
1263
|
+
].join("\n"),
|
|
1264
|
+
},
|
|
1265
|
+
args: {
|
|
1266
|
+
"app-id": { type: "string", description: "App ID. Use: asc apps list", required: true },
|
|
1267
|
+
category: { type: "string", description: "Access type: ONE_TIME_SNAPSHOT | ONGOING", default: "ONE_TIME_SNAPSHOT" },
|
|
1268
|
+
},
|
|
1269
|
+
async run({ args }) { await analyticsReportRequest({ appId: args["app-id"], category: args.category }) },
|
|
1270
|
+
})
|
|
1271
|
+
|
|
1272
|
+
const reportsCmd = defineCommand({
|
|
1273
|
+
meta: {
|
|
1274
|
+
name: "reports",
|
|
1275
|
+
description: [
|
|
1276
|
+
"Download sales, finance, and analytics reports.",
|
|
1277
|
+
"Sales/finance reports output TSV (piped) or JSON (--output json).",
|
|
1278
|
+
"Vendor number is found at App Store Connect → Payments & Financial Reports.",
|
|
1279
|
+
].join("\n"),
|
|
1280
|
+
},
|
|
1281
|
+
subCommands: {
|
|
1282
|
+
sales: salesReportCmd,
|
|
1283
|
+
finance: financeReportCmd,
|
|
1284
|
+
analytics: defineCommand({
|
|
1285
|
+
meta: { name: "analytics", description: "Analytics report requests" },
|
|
1286
|
+
subCommands: { list: analyticsListCmd, request: analyticsRequestCmd },
|
|
1287
|
+
}),
|
|
1288
|
+
},
|
|
1289
|
+
})
|
|
1290
|
+
|
|
1291
|
+
// =============================================================================
|
|
1292
|
+
// XCODE CLOUD
|
|
1293
|
+
// =============================================================================
|
|
1294
|
+
|
|
1295
|
+
const xcProductsListCmd = defineCommand({
|
|
1296
|
+
meta: { name: "list", description: "List Xcode Cloud products. Each product is linked to one app." },
|
|
1297
|
+
args: {
|
|
1298
|
+
"app-id": { type: "string", description: "Filter by App ID. Use: asc apps list" },
|
|
1299
|
+
output: { type: "string", alias: "o", description: "Output format: table | json" },
|
|
1300
|
+
},
|
|
1301
|
+
async run({ args }) { await xcodeCloudProductsList({ appId: args["app-id"], output: args.output }) },
|
|
1302
|
+
})
|
|
1303
|
+
|
|
1304
|
+
const xcWorkflowsListCmd = defineCommand({
|
|
1305
|
+
meta: { name: "list", description: "List Xcode Cloud workflows for a product." },
|
|
1306
|
+
args: {
|
|
1307
|
+
"product-id": { type: "string", description: "Xcode Cloud Product ID. Use: asc xcode-cloud products list", required: true },
|
|
1308
|
+
output: { type: "string", alias: "o", description: "Output format: table | json" },
|
|
1309
|
+
},
|
|
1310
|
+
async run({ args }) { await workflowsList({ productId: args["product-id"], output: args.output }) },
|
|
1311
|
+
})
|
|
1312
|
+
|
|
1313
|
+
const xcWorkflowGetCmd = defineCommand({
|
|
1314
|
+
meta: { name: "get", description: "Get details for a specific Xcode Cloud workflow." },
|
|
1315
|
+
args: {
|
|
1316
|
+
"workflow-id": { type: "positional", description: "Workflow ID. Use: asc xcode-cloud workflows list --product-id <id>", required: true },
|
|
1317
|
+
output: { type: "string", alias: "o", description: "Output format: table | json" },
|
|
1318
|
+
},
|
|
1319
|
+
async run({ args }) { await workflowGet(args["workflow-id"], { output: args.output }) },
|
|
1320
|
+
})
|
|
1321
|
+
|
|
1322
|
+
const xcBuildsListCmd = defineCommand({
|
|
1323
|
+
meta: { name: "list", description: "List Xcode Cloud build runs. Filter by workflow or product." },
|
|
1324
|
+
args: {
|
|
1325
|
+
"workflow-id": { type: "string", description: "Filter by Workflow ID. Use: asc xcode-cloud workflows list --product-id <id>" },
|
|
1326
|
+
"product-id": { type: "string", description: "Filter by Xcode Cloud Product ID" },
|
|
1327
|
+
output: { type: "string", alias: "o", description: "Output format: table | json" },
|
|
1328
|
+
},
|
|
1329
|
+
async run({ args }) { await xcodeCloudBuildsList({ workflowId: args["workflow-id"], productId: args["product-id"], output: args.output }) },
|
|
1330
|
+
})
|
|
1331
|
+
|
|
1332
|
+
const xcBuildRunCmd = defineCommand({
|
|
1333
|
+
meta: {
|
|
1334
|
+
name: "run",
|
|
1335
|
+
description: [
|
|
1336
|
+
"Trigger a new Xcode Cloud build run for a workflow.",
|
|
1337
|
+
"Optionally specify a branch to build. Defaults to the workflow's configured start condition.",
|
|
1338
|
+
].join("\n"),
|
|
1339
|
+
},
|
|
1340
|
+
args: {
|
|
1341
|
+
"workflow-id": { type: "string", description: "Workflow ID. Use: asc xcode-cloud workflows list --product-id <id>", required: true },
|
|
1342
|
+
branch: { type: "string", description: "Git branch to build (optional, uses workflow default if omitted)" },
|
|
1343
|
+
},
|
|
1344
|
+
async run({ args }) { await xcodeCloudBuildRun({ workflowId: args["workflow-id"], branch: args.branch }) },
|
|
1345
|
+
})
|
|
1346
|
+
|
|
1347
|
+
const xcArtifactsListCmd = defineCommand({
|
|
1348
|
+
meta: { name: "list", description: "List artifacts (logs, archives, test results) from an Xcode Cloud build run." },
|
|
1349
|
+
args: {
|
|
1350
|
+
"build-id": { type: "string", description: "Build Run ID. Use: asc xcode-cloud builds list --workflow-id <id>", required: true },
|
|
1351
|
+
output: { type: "string", alias: "o", description: "Output format: table | json" },
|
|
1352
|
+
},
|
|
1353
|
+
async run({ args }) { await xcodeCloudArtifactsList({ buildRunId: args["build-id"], output: args.output }) },
|
|
1354
|
+
})
|
|
1355
|
+
|
|
1356
|
+
const xcTestResultsListCmd = defineCommand({
|
|
1357
|
+
meta: { name: "list", description: "List test results from an Xcode Cloud build run." },
|
|
1358
|
+
args: {
|
|
1359
|
+
"build-id": { type: "string", description: "Build Run ID. Use: asc xcode-cloud builds list --workflow-id <id>", required: true },
|
|
1360
|
+
output: { type: "string", alias: "o", description: "Output format: table | json" },
|
|
1361
|
+
},
|
|
1362
|
+
async run({ args }) { await xcodeCloudTestResultsList({ buildRunId: args["build-id"], output: args.output }) },
|
|
1363
|
+
})
|
|
1364
|
+
|
|
1365
|
+
const xcodeCloudCmd = defineCommand({
|
|
1366
|
+
meta: {
|
|
1367
|
+
name: "xcode-cloud",
|
|
1368
|
+
description: [
|
|
1369
|
+
"Manage Xcode Cloud CI/CD workflows and build runs.",
|
|
1370
|
+
"Workflow: products list → workflows list → builds run → artifacts list",
|
|
1371
|
+
].join("\n"),
|
|
1372
|
+
},
|
|
1373
|
+
subCommands: {
|
|
1374
|
+
products: defineCommand({ meta: { name: "products", description: "Xcode Cloud products" }, subCommands: { list: xcProductsListCmd } }),
|
|
1375
|
+
workflows: defineCommand({ meta: { name: "workflows", description: "Xcode Cloud workflows" }, subCommands: { list: xcWorkflowsListCmd, get: xcWorkflowGetCmd } }),
|
|
1376
|
+
builds: defineCommand({ meta: { name: "builds", description: "Xcode Cloud build runs" }, subCommands: { list: xcBuildsListCmd, run: xcBuildRunCmd } }),
|
|
1377
|
+
artifacts: defineCommand({ meta: { name: "artifacts", description: "Build artifacts" }, subCommands: { list: xcArtifactsListCmd } }),
|
|
1378
|
+
"test-results": defineCommand({ meta: { name: "test-results", description: "Test results" }, subCommands: { list: xcTestResultsListCmd } }),
|
|
1379
|
+
},
|
|
1380
|
+
})
|
|
1381
|
+
|
|
1382
|
+
// =============================================================================
|
|
1383
|
+
// GAME CENTER
|
|
1384
|
+
// =============================================================================
|
|
1385
|
+
|
|
1386
|
+
const gcAchievementsListCmd = defineCommand({
|
|
1387
|
+
meta: { name: "list", description: "List Game Center achievements for an app." },
|
|
1388
|
+
args: {
|
|
1389
|
+
"app-id": { type: "string", description: "App ID. Use: asc apps list", required: true },
|
|
1390
|
+
output: { type: "string", alias: "o", description: "Output format: table | json" },
|
|
1391
|
+
},
|
|
1392
|
+
async run({ args }) { await achievementsList({ appId: args["app-id"], output: args.output }) },
|
|
1393
|
+
})
|
|
1394
|
+
|
|
1395
|
+
const gcAchievementsGetCmd = defineCommand({
|
|
1396
|
+
meta: { name: "get", description: "Get details for a specific Game Center achievement." },
|
|
1397
|
+
args: {
|
|
1398
|
+
"achievement-id": { type: "positional", description: "Achievement ID. Use: asc game-center achievements list --app-id <id>", required: true },
|
|
1399
|
+
output: { type: "string", alias: "o", description: "Output format: table | json" },
|
|
1400
|
+
},
|
|
1401
|
+
async run({ args }) { await achievementGet(args["achievement-id"], { output: args.output }) },
|
|
1402
|
+
})
|
|
1403
|
+
|
|
1404
|
+
const gcLeaderboardsListCmd = defineCommand({
|
|
1405
|
+
meta: { name: "list", description: "List Game Center leaderboards for an app." },
|
|
1406
|
+
args: {
|
|
1407
|
+
"app-id": { type: "string", description: "App ID. Use: asc apps list", required: true },
|
|
1408
|
+
output: { type: "string", alias: "o", description: "Output format: table | json" },
|
|
1409
|
+
},
|
|
1410
|
+
async run({ args }) { await leaderboardsList({ appId: args["app-id"], output: args.output }) },
|
|
1411
|
+
})
|
|
1412
|
+
|
|
1413
|
+
const gcLeaderboardSetsListCmd = defineCommand({
|
|
1414
|
+
meta: { name: "list", description: "List Game Center leaderboard sets for an app." },
|
|
1415
|
+
args: {
|
|
1416
|
+
"app-id": { type: "string", description: "App ID. Use: asc apps list", required: true },
|
|
1417
|
+
output: { type: "string", alias: "o", description: "Output format: table | json" },
|
|
1418
|
+
},
|
|
1419
|
+
async run({ args }) { await leaderboardSetsList({ appId: args["app-id"], output: args.output }) },
|
|
1420
|
+
})
|
|
1421
|
+
|
|
1422
|
+
const gameCenterCmd = defineCommand({
|
|
1423
|
+
meta: {
|
|
1424
|
+
name: "game-center",
|
|
1425
|
+
description: "Manage Game Center features: achievements, leaderboards, and leaderboard sets.",
|
|
1426
|
+
},
|
|
1427
|
+
subCommands: {
|
|
1428
|
+
achievements: defineCommand({
|
|
1429
|
+
meta: { name: "achievements", description: "Game Center achievements" },
|
|
1430
|
+
subCommands: { list: gcAchievementsListCmd, get: gcAchievementsGetCmd },
|
|
1431
|
+
}),
|
|
1432
|
+
leaderboards: defineCommand({
|
|
1433
|
+
meta: { name: "leaderboards", description: "Game Center leaderboards" },
|
|
1434
|
+
subCommands: { list: gcLeaderboardsListCmd },
|
|
1435
|
+
}),
|
|
1436
|
+
"leaderboard-sets": defineCommand({
|
|
1437
|
+
meta: { name: "leaderboard-sets", description: "Game Center leaderboard sets" },
|
|
1438
|
+
subCommands: { list: gcLeaderboardSetsListCmd },
|
|
1439
|
+
}),
|
|
1440
|
+
},
|
|
1441
|
+
})
|
|
1442
|
+
|
|
1443
|
+
// =============================================================================
|
|
1444
|
+
// BETA REVIEW
|
|
1445
|
+
// =============================================================================
|
|
1446
|
+
|
|
1447
|
+
const betaReviewSubmitCmd = defineCommand({
|
|
1448
|
+
meta: {
|
|
1449
|
+
name: "submit",
|
|
1450
|
+
description: [
|
|
1451
|
+
"Submit a build for TestFlight external beta review.",
|
|
1452
|
+
"Required before distributing to external testers (outside your org).",
|
|
1453
|
+
"Internal testers do not require beta review.",
|
|
1454
|
+
"Build ID comes from: asc builds list --app-id <id>",
|
|
1455
|
+
].join("\n"),
|
|
1456
|
+
},
|
|
1457
|
+
args: { "build-id": { type: "positional", description: "Build ID (UUID). Use: asc builds list --app-id <id>", required: true } },
|
|
1458
|
+
async run({ args }) { await betaReviewSubmissionCreate(args["build-id"]) },
|
|
1459
|
+
})
|
|
1460
|
+
|
|
1461
|
+
const betaReviewStatusCmd = defineCommand({
|
|
1462
|
+
meta: { name: "status", description: "Check the beta review status for a build." },
|
|
1463
|
+
args: {
|
|
1464
|
+
"build-id": { type: "string", description: "Build ID (UUID). Use: asc builds list --app-id <id>", required: true },
|
|
1465
|
+
output: { type: "string", alias: "o", description: "Output format: table | json" },
|
|
1466
|
+
},
|
|
1467
|
+
async run({ args }) { await betaReviewSubmissionGet(args["build-id"], { output: args.output }) },
|
|
1468
|
+
})
|
|
1469
|
+
|
|
1470
|
+
const betaReviewDetailGetCmd = defineCommand({
|
|
1471
|
+
meta: {
|
|
1472
|
+
name: "get",
|
|
1473
|
+
description: "Get the beta app review details including contact information and demo account credentials.",
|
|
1474
|
+
},
|
|
1475
|
+
args: {
|
|
1476
|
+
"app-id": { type: "string", description: "App ID. Use: asc apps list", required: true },
|
|
1477
|
+
output: { type: "string", alias: "o", description: "Output format: table | json" },
|
|
1478
|
+
},
|
|
1479
|
+
async run({ args }) { await betaReviewDetailGet(args["app-id"], { output: args.output }) },
|
|
1480
|
+
})
|
|
1481
|
+
|
|
1482
|
+
const betaReviewDetailUpdateCmd = defineCommand({
|
|
1483
|
+
meta: {
|
|
1484
|
+
name: "update",
|
|
1485
|
+
description: [
|
|
1486
|
+
"Update the beta app review details including reviewer contact and demo account.",
|
|
1487
|
+
"This information is provided to the Apple review team when reviewing your TestFlight build.",
|
|
1488
|
+
].join("\n"),
|
|
1489
|
+
},
|
|
1490
|
+
args: {
|
|
1491
|
+
"app-id": { type: "string", description: "App ID. Use: asc apps list", required: true },
|
|
1492
|
+
"contact-first-name": { type: "string", description: "Review contact first name" },
|
|
1493
|
+
"contact-last-name": { type: "string", description: "Review contact last name" },
|
|
1494
|
+
"contact-email": { type: "string", description: "Review contact email address" },
|
|
1495
|
+
"contact-phone": { type: "string", description: "Review contact phone number" },
|
|
1496
|
+
"demo-account-name": { type: "string", description: "Demo account username (if demo account required)" },
|
|
1497
|
+
"demo-account-password": { type: "string", description: "Demo account password" },
|
|
1498
|
+
"demo-account-required": { type: "boolean", description: "Whether a demo account is required for review" },
|
|
1499
|
+
notes: { type: "string", description: "Additional notes for the review team" },
|
|
1500
|
+
},
|
|
1501
|
+
async run({ args }) {
|
|
1502
|
+
await betaReviewDetailUpdate({
|
|
1503
|
+
appId: args["app-id"],
|
|
1504
|
+
contactFirstName: args["contact-first-name"],
|
|
1505
|
+
contactLastName: args["contact-last-name"],
|
|
1506
|
+
contactEmail: args["contact-email"],
|
|
1507
|
+
contactPhone: args["contact-phone"],
|
|
1508
|
+
demoAccountName: args["demo-account-name"],
|
|
1509
|
+
demoAccountPassword: args["demo-account-password"],
|
|
1510
|
+
demoAccountRequired: args["demo-account-required"],
|
|
1511
|
+
notes: args.notes,
|
|
1512
|
+
})
|
|
1513
|
+
},
|
|
1514
|
+
})
|
|
1515
|
+
|
|
1516
|
+
const betaBuildNotifyCmd = defineCommand({
|
|
1517
|
+
meta: {
|
|
1518
|
+
name: "notify",
|
|
1519
|
+
description: "Enable or disable automatic tester notifications when a build is approved for testing.",
|
|
1520
|
+
},
|
|
1521
|
+
args: {
|
|
1522
|
+
"build-id": { type: "string", description: "Build ID (UUID). Use: asc builds list --app-id <id>", required: true },
|
|
1523
|
+
enabled: { type: "boolean", description: "Set to true to auto-notify testers, false to suppress notifications", default: true },
|
|
1524
|
+
},
|
|
1525
|
+
async run({ args }) { await buildBetaDetailUpdate({ buildId: args["build-id"], autoNotifyEnabled: args.enabled }) },
|
|
1526
|
+
})
|
|
1527
|
+
|
|
1528
|
+
const betaBuildLocalesListCmd = defineCommand({
|
|
1529
|
+
meta: { name: "list", description: "List TestFlight 'What to Test' localizations for a build." },
|
|
1530
|
+
args: {
|
|
1531
|
+
"build-id": { type: "string", description: "Build ID (UUID). Use: asc builds list --app-id <id>", required: true },
|
|
1532
|
+
output: { type: "string", alias: "o", description: "Output format: table | json" },
|
|
1533
|
+
},
|
|
1534
|
+
async run({ args }) { await betaBuildLocalizationsList({ buildId: args["build-id"], output: args.output }) },
|
|
1535
|
+
})
|
|
1536
|
+
|
|
1537
|
+
const betaReviewCmd = defineCommand({
|
|
1538
|
+
meta: {
|
|
1539
|
+
name: "beta-review",
|
|
1540
|
+
description: [
|
|
1541
|
+
"Manage TestFlight external beta review submissions and review details.",
|
|
1542
|
+
"External testers require beta app review before accessing builds.",
|
|
1543
|
+
"Internal testers (App Store Connect users) do not require review.",
|
|
1544
|
+
].join("\n"),
|
|
1545
|
+
},
|
|
1546
|
+
subCommands: {
|
|
1547
|
+
submit: betaReviewSubmitCmd,
|
|
1548
|
+
status: betaReviewStatusCmd,
|
|
1549
|
+
"detail-get": betaReviewDetailGetCmd,
|
|
1550
|
+
"detail-update": betaReviewDetailUpdateCmd,
|
|
1551
|
+
notify: betaBuildNotifyCmd,
|
|
1552
|
+
localizations: defineCommand({ meta: { name: "localizations", description: "Build beta localizations" }, subCommands: { list: betaBuildLocalesListCmd } }),
|
|
1553
|
+
},
|
|
1554
|
+
})
|
|
1555
|
+
|
|
1556
|
+
// =============================================================================
|
|
1557
|
+
// ROOT
|
|
1558
|
+
// =============================================================================
|
|
1559
|
+
|
|
1560
|
+
const main = defineCommand({
|
|
1561
|
+
meta: {
|
|
1562
|
+
name: "asc",
|
|
1563
|
+
version: pkg.version,
|
|
1564
|
+
description: [
|
|
1565
|
+
"App Store Connect CLI — manage apps, builds, TestFlight, metadata and more.",
|
|
1566
|
+
"",
|
|
1567
|
+
"QUICK START:",
|
|
1568
|
+
" 1. asc auth login → add your API key credentials",
|
|
1569
|
+
" 2. asc apps list → find your App ID",
|
|
1570
|
+
" 3. asc builds list → see recent builds",
|
|
1571
|
+
"",
|
|
1572
|
+
"All commands output tables in TTY mode and JSON when piped.",
|
|
1573
|
+
"Use --output json to force JSON output for programmatic use.",
|
|
1574
|
+
"",
|
|
1575
|
+
"GLOBAL FLAGS:",
|
|
1576
|
+
" --profile <name> Use a specific profile for this command (e.g. --profile personal)",
|
|
1577
|
+
" Equivalent to setting ASC_PROFILE=<name>",
|
|
1578
|
+
" See all profiles: asc auth list",
|
|
1579
|
+
"",
|
|
1580
|
+
" ASC_KEY_ID App Store Connect API Key ID",
|
|
1581
|
+
" ASC_ISSUER_ID Issuer ID (UUID)",
|
|
1582
|
+
" ASC_PRIVATE_KEY_PATH Path to .p8 private key file",
|
|
1583
|
+
" ASC_BYPASS_KEYCHAIN Set to '1' to skip Keychain storage",
|
|
1584
|
+
"",
|
|
1585
|
+
"REGENERATE API CLIENT (when Apple updates their API):",
|
|
1586
|
+
" bun run generate",
|
|
1587
|
+
"",
|
|
1588
|
+
"AI AGENTS:",
|
|
1589
|
+
" asc --help --json Full command tree as structured JSON (pipe to jq or save to file)",
|
|
1590
|
+
].join("\n"),
|
|
1591
|
+
},
|
|
1592
|
+
subCommands: {
|
|
1593
|
+
auth: authCmd,
|
|
1594
|
+
apps: appsCmd,
|
|
1595
|
+
builds: buildsCmd,
|
|
1596
|
+
versions: versionsCmd,
|
|
1597
|
+
testflight: testflightCmd,
|
|
1598
|
+
metadata: metadataCmd,
|
|
1599
|
+
reviews: reviewsCmd,
|
|
1600
|
+
pricing: pricingCmd,
|
|
1601
|
+
availability: availabilityCmd,
|
|
1602
|
+
iap: iapCmd,
|
|
1603
|
+
subscriptions: subscriptionsCmd,
|
|
1604
|
+
"app-info": appInfoCmd,
|
|
1605
|
+
screenshots: screenshotsCmd,
|
|
1606
|
+
signing: signingCmd,
|
|
1607
|
+
reports: reportsCmd,
|
|
1608
|
+
"xcode-cloud": xcodeCloudCmd,
|
|
1609
|
+
"game-center": gameCenterCmd,
|
|
1610
|
+
"beta-review": betaReviewCmd,
|
|
1611
|
+
},
|
|
1612
|
+
})
|
|
1613
|
+
|
|
1614
|
+
// ---- Global --help --json flag ----
|
|
1615
|
+
// When both --help and --json are present, output the full command tree as JSON
|
|
1616
|
+
// and exit. This is more discoverable than a dedicated subcommand since agents
|
|
1617
|
+
// and humans both start with --help.
|
|
1618
|
+
if (process.argv.includes("--help") && process.argv.includes("--json")) {
|
|
1619
|
+
console.log(JSON.stringify(buildHelpDocument(), null, 2))
|
|
1620
|
+
process.exit(0)
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1623
|
+
// ---- Global --profile flag ----
|
|
1624
|
+
// Parse --profile <name> from raw args early so getActiveProfile() picks it up
|
|
1625
|
+
// via process.env["ASC_PROFILE"] regardless of which subcommand runs.
|
|
1626
|
+
// This mirrors how `aws --profile <name>` works.
|
|
1627
|
+
const profileFlagIdx = process.argv.indexOf("--profile")
|
|
1628
|
+
if (profileFlagIdx !== -1) {
|
|
1629
|
+
const profileFlagValue = process.argv[profileFlagIdx + 1]
|
|
1630
|
+
if (profileFlagValue && !profileFlagValue.startsWith("-")) {
|
|
1631
|
+
process.env["ASC_PROFILE"] = profileFlagValue
|
|
1632
|
+
// Remove from argv so citty doesn't see an unknown flag
|
|
1633
|
+
process.argv.splice(profileFlagIdx, 2)
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
// ---- Intercept citty's error output ----
|
|
1638
|
+
// Citty uses consola to print errors, which includes full stack traces.
|
|
1639
|
+
// Override consola.error to route through our clean printError formatter.
|
|
1640
|
+
// Citty calls it twice for non-CLI errors: once with the Error object,
|
|
1641
|
+
// once with error.message as a string — suppress the duplicate string call.
|
|
1642
|
+
{
|
|
1643
|
+
const consola = (await import("consola")).consola
|
|
1644
|
+
let _lastErrorMessage: string | null = null
|
|
1645
|
+
const _origError = consola.error.bind(consola)
|
|
1646
|
+
;(consola as unknown as Record<string, unknown>).error = (err: unknown, ..._rest: unknown[]) => {
|
|
1647
|
+
if (err instanceof Error) {
|
|
1648
|
+
printError(err)
|
|
1649
|
+
_lastErrorMessage = err.message
|
|
1650
|
+
} else if (typeof err === "string" && err === _lastErrorMessage) {
|
|
1651
|
+
_lastErrorMessage = null // suppress duplicate
|
|
1652
|
+
} else {
|
|
1653
|
+
_origError(err)
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
runMain(main).catch((err) => {
|
|
1659
|
+
printError(err)
|
|
1660
|
+
process.exit(1)
|
|
1661
|
+
})
|