@onmyway133/asc-cli 1.0.2 → 1.0.3

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.
@@ -1,5 +1,10 @@
1
- import { ascFetch, ascFetchAll } from "../api/client.ts"
2
- import type { AppStoreVersion, Platform } from "../api/types.ts"
1
+ import {
2
+ appsAppStoreVersionsGetToManyRelated,
3
+ appStoreVersionsCreateInstance,
4
+ appStoreVersionReleaseRequestsCreateInstance,
5
+ } from "../api/generated/sdk.gen.ts"
6
+ import type { Platform } from "../api/generated/types.gen.ts"
7
+ import { throwOnError, ascFetch } from "../api/client.ts"
3
8
  import { detectFormat, formatDate, printJSON, printSuccess, printTable } from "../utils/output.ts"
4
9
 
5
10
  export async function versionsList(opts: {
@@ -7,15 +12,15 @@ export async function versionsList(opts: {
7
12
  platform?: string
8
13
  output?: string
9
14
  }): Promise<void> {
10
- const params: Record<string, string> = {
11
- "fields[appStoreVersions]": "platform,versionString,appStoreState,createdDate",
12
- }
13
- if (opts.platform) params["filter[platform]"] = opts.platform.toUpperCase()
14
-
15
- const versions = await ascFetchAll<AppStoreVersion>(
16
- `/v1/apps/${opts.appId}/appStoreVersions`,
17
- { params },
18
- )
15
+ const result = await appsAppStoreVersionsGetToManyRelated({
16
+ path: { id: opts.appId },
17
+ query: {
18
+ "fields[appStoreVersions]": ["platform", "versionString", "appStoreState", "createdDate"],
19
+ ...(opts.platform ? { "filter[platform]": [opts.platform.toUpperCase() as Platform] } : {}),
20
+ },
21
+ })
22
+ const data = throwOnError(result)
23
+ const versions = data.data
19
24
 
20
25
  const fmt = detectFormat(opts.output)
21
26
  if (fmt === "json") {
@@ -27,10 +32,10 @@ export async function versionsList(opts: {
27
32
  ["ID", "Version", "Platform", "State", "Created"],
28
33
  versions.map((v) => [
29
34
  v.id,
30
- v.attributes.versionString,
31
- v.attributes.platform,
32
- v.attributes.appStoreState,
33
- formatDate(v.attributes.createdDate),
35
+ v.attributes?.versionString ?? "-",
36
+ v.attributes?.platform ?? "-",
37
+ v.attributes?.appStoreState ?? "-",
38
+ formatDate(v.attributes?.createdDate),
34
39
  ]),
35
40
  `Versions (${versions.length})`,
36
41
  )
@@ -42,8 +47,7 @@ export async function versionsCreate(opts: {
42
47
  platform: string
43
48
  }): Promise<void> {
44
49
  const platform = opts.platform.toUpperCase() as Platform
45
- const res = await ascFetch<AppStoreVersion>("/v1/appStoreVersions", {
46
- method: "POST",
50
+ const result = await appStoreVersionsCreateInstance({
47
51
  body: {
48
52
  data: {
49
53
  type: "appStoreVersions",
@@ -57,9 +61,12 @@ export async function versionsCreate(opts: {
57
61
  },
58
62
  },
59
63
  })
60
- printSuccess(`Created version ${opts.version} (${platform}) — ID: ${res.data.id}`)
64
+ const data = throwOnError(result)
65
+ printSuccess(`Created version ${opts.version} (${platform}) — ID: ${data.data.id}`)
61
66
  }
62
67
 
68
+ // versionsSubmit uses the deprecated appStoreVersionSubmissions API which has no
69
+ // generated create SDK function (only delete). Keep using ascFetch.
63
70
  export async function versionsSubmit(versionId: string): Promise<void> {
64
71
  await ascFetch("/v1/appStoreVersionSubmissions", {
65
72
  method: "POST",
@@ -74,3 +81,18 @@ export async function versionsSubmit(versionId: string): Promise<void> {
74
81
  })
75
82
  printSuccess(`Version ${versionId} submitted for review`)
76
83
  }
84
+
85
+ export async function releaseRequestCreate(versionId: string): Promise<void> {
86
+ const result = await appStoreVersionReleaseRequestsCreateInstance({
87
+ body: {
88
+ data: {
89
+ type: "appStoreVersionReleaseRequests",
90
+ relationships: {
91
+ appStoreVersion: { data: { type: "appStoreVersions", id: versionId } },
92
+ },
93
+ },
94
+ },
95
+ })
96
+ throwOnError(result)
97
+ printSuccess(`Release request created for version ${versionId}`)
98
+ }
@@ -1,4 +1,13 @@
1
- import { ascFetch, ascFetchAll } from "../api/client.ts"
1
+ import {
2
+ appsCiProductGetToOneRelated,
3
+ ciProductsGetCollection,
4
+ ciProductsWorkflowsGetToManyRelated,
5
+ ciWorkflowsGetInstance,
6
+ ciWorkflowsBuildRunsGetToManyRelated,
7
+ ciProductsBuildRunsGetToManyRelated,
8
+ ciBuildRunsCreateInstance,
9
+ } from "../api/generated/sdk.gen.ts"
10
+ import { throwOnError, ascFetchAll } from "../api/client.ts"
2
11
  import { detectFormat, formatDate, printJSON, printSuccess, printTable, truncate } from "../utils/output.ts"
3
12
 
4
13
  // ---- Xcode Cloud Products ----
@@ -7,22 +16,27 @@ export async function xcodeCloudProductsList(opts: {
7
16
  appId?: string
8
17
  output?: string
9
18
  } = {}): Promise<void> {
10
- const url = opts.appId
11
- ? `/v1/apps/${opts.appId}/ciProduct`
12
- : "/v1/ciProducts"
13
- const items = await ascFetchAll<{
14
- id: string
15
- attributes: { name?: string; createdDate?: string; productType?: string }
16
- }>(url)
19
+ let items: Array<{ id: string; attributes?: { name?: string; createdDate?: string; productType?: string } }>
20
+
21
+ if (opts.appId) {
22
+ const result = await appsCiProductGetToOneRelated({ path: { id: opts.appId } })
23
+ const data = throwOnError(result)
24
+ items = [data.data]
25
+ } else {
26
+ const result = await ciProductsGetCollection()
27
+ const data = throwOnError(result)
28
+ items = data.data
29
+ }
30
+
17
31
  const fmt = detectFormat(opts.output)
18
32
  if (fmt === "json") { printJSON(items); return }
19
33
  printTable(
20
34
  ["Product ID", "Name", "Type", "Created"],
21
35
  items.map(p => [
22
36
  p.id,
23
- p.attributes.name ?? "-",
24
- p.attributes.productType ?? "-",
25
- formatDate(p.attributes.createdDate ?? ""),
37
+ p.attributes?.name ?? "-",
38
+ p.attributes?.productType ?? "-",
39
+ formatDate(p.attributes?.createdDate ?? ""),
26
40
  ]),
27
41
  `Xcode Cloud Products (${items.length})`,
28
42
  )
@@ -34,55 +48,34 @@ export async function workflowsList(opts: {
34
48
  productId: string
35
49
  output?: string
36
50
  }): Promise<void> {
37
- const items = await ascFetchAll<{
38
- id: string
39
- attributes: {
40
- name?: string
41
- description?: string
42
- isEnabled?: boolean
43
- isLockedForEditing?: boolean
44
- lastModifiedDate?: string
45
- }
46
- }>(`/v1/ciProducts/${opts.productId}/workflows`)
51
+ const result = await ciProductsWorkflowsGetToManyRelated({ path: { id: opts.productId } })
52
+ const data = throwOnError(result)
53
+ const items = data.data
47
54
  const fmt = detectFormat(opts.output)
48
55
  if (fmt === "json") { printJSON(items); return }
49
56
  printTable(
50
57
  ["Workflow ID", "Name", "Enabled", "Last Modified"],
51
58
  items.map(w => [
52
59
  w.id,
53
- truncate(w.attributes.name ?? "-", 40),
54
- String(w.attributes.isEnabled ?? "-"),
55
- formatDate(w.attributes.lastModifiedDate ?? ""),
60
+ truncate(w.attributes?.name ?? "-", 40),
61
+ String(w.attributes?.isEnabled ?? "-"),
62
+ formatDate(w.attributes?.lastModifiedDate ?? ""),
56
63
  ]),
57
64
  `Workflows (${items.length})`,
58
65
  )
59
66
  }
60
67
 
61
68
  export async function workflowGet(workflowId: string, opts: { output?: string } = {}): Promise<void> {
62
- const result = await ascFetch<{
63
- data: {
64
- id: string
65
- attributes: {
66
- name?: string
67
- description?: string
68
- isEnabled?: boolean
69
- isLockedForEditing?: boolean
70
- lastModifiedDate?: string
71
- branchStartCondition?: unknown
72
- pullRequestStartCondition?: unknown
73
- scheduledStartCondition?: unknown
74
- tagStartCondition?: unknown
75
- }
76
- }
77
- }>(`/v1/ciWorkflows/${workflowId}`)
69
+ const result = await ciWorkflowsGetInstance({ path: { id: workflowId } })
70
+ const data = throwOnError(result)
78
71
  const fmt = detectFormat(opts.output)
79
- if (fmt === "json") { printJSON(result.data.data); return }
80
- const a = result.data.data.attributes
81
- console.log(`ID: ${result.data.data.id}`)
82
- console.log(`Name: ${a.name ?? "-"}`)
83
- console.log(`Description: ${a.description ?? "-"}`)
84
- console.log(`Enabled: ${a.isEnabled ?? "-"}`)
85
- console.log(`Last Modified: ${formatDate(a.lastModifiedDate ?? "")}`)
72
+ if (fmt === "json") { printJSON(data.data); return }
73
+ const a = data.data.attributes
74
+ console.log(`ID: ${data.data.id}`)
75
+ console.log(`Name: ${a?.name ?? "-"}`)
76
+ console.log(`Description: ${a?.description ?? "-"}`)
77
+ console.log(`Enabled: ${a?.isEnabled ?? "-"}`)
78
+ console.log(`Last Modified: ${formatDate(a?.lastModifiedDate ?? "")}`)
86
79
  }
87
80
 
88
81
  // ---- Builds ----
@@ -92,33 +85,41 @@ export async function xcodeCloudBuildsList(opts: {
92
85
  productId?: string
93
86
  output?: string
94
87
  } = {}): Promise<void> {
95
- const url = opts.workflowId
96
- ? `/v1/ciWorkflows/${opts.workflowId}/buildRuns`
97
- : opts.productId
98
- ? `/v1/ciProducts/${opts.productId}/buildRuns`
99
- : "/v1/ciBuildRuns"
100
- const items = await ascFetchAll<{
88
+ let items: Array<{
101
89
  id: string
102
- attributes: {
90
+ attributes?: {
103
91
  number?: number
104
92
  createdDate?: string
105
93
  startedDate?: string
106
94
  finishedDate?: string
107
- sourceCommit?: { commitSha?: string; message?: string }
108
95
  executionProgress?: string
109
96
  completionStatus?: string
110
97
  }
111
- }>(url)
98
+ }>
99
+
100
+ if (opts.workflowId) {
101
+ const result = await ciWorkflowsBuildRunsGetToManyRelated({ path: { id: opts.workflowId } })
102
+ const data = throwOnError(result)
103
+ items = data.data
104
+ } else if (opts.productId) {
105
+ const result = await ciProductsBuildRunsGetToManyRelated({ path: { id: opts.productId } })
106
+ const data = throwOnError(result)
107
+ items = data.data
108
+ } else {
109
+ // No SDK collection for ciBuildRuns without filter — fall back to ascFetchAll
110
+ items = await ascFetchAll("/v1/ciBuildRuns")
111
+ }
112
+
112
113
  const fmt = detectFormat(opts.output)
113
114
  if (fmt === "json") { printJSON(items); return }
114
115
  printTable(
115
116
  ["Build ID", "#", "Status", "Progress", "Started"],
116
117
  items.map(b => [
117
118
  b.id,
118
- String(b.attributes.number ?? "-"),
119
- b.attributes.completionStatus ?? "RUNNING",
120
- b.attributes.executionProgress ?? "-",
121
- formatDate(b.attributes.startedDate ?? b.attributes.createdDate ?? ""),
119
+ String(b.attributes?.number ?? "-"),
120
+ b.attributes?.completionStatus ?? "RUNNING",
121
+ b.attributes?.executionProgress ?? "-",
122
+ formatDate(b.attributes?.startedDate ?? b.attributes?.createdDate ?? ""),
122
123
  ]),
123
124
  `Xcode Cloud Builds (${items.length})`,
124
125
  )
@@ -131,20 +132,20 @@ export async function xcodeCloudBuildRun(opts: {
131
132
  }): Promise<void> {
132
133
  const attributes: Record<string, unknown> = { isPullRequestBuild: false }
133
134
  if (opts.branch) attributes["sourceCommit"] = { branch: opts.branch }
134
- const result = await ascFetch<{ data: { id: string; attributes: { number?: number } } }>("/v1/ciBuildRuns", {
135
- method: "POST",
135
+ const result = await ciBuildRunsCreateInstance({
136
136
  body: {
137
137
  data: {
138
138
  type: "ciBuildRuns",
139
- attributes,
139
+ attributes: attributes as never,
140
140
  relationships: {
141
141
  workflow: { data: { type: "ciWorkflows", id: opts.workflowId } },
142
142
  },
143
143
  },
144
144
  },
145
145
  })
146
- const num = result.data.data.attributes.number
147
- printSuccess(`Build run started: ${result.data.data.id}${num ? ` (#${num})` : ""}`)
146
+ const data = throwOnError(result)
147
+ const num = data.data.attributes?.number
148
+ printSuccess(`Build run started: ${data.data.id}${num ? ` (#${num})` : ""}`)
148
149
  }
149
150
 
150
151
  // ---- Artifacts ----
@@ -153,6 +154,7 @@ export async function xcodeCloudArtifactsList(opts: {
153
154
  buildRunId: string
154
155
  output?: string
155
156
  }): Promise<void> {
157
+ // No SDK function for /v1/ciBuildRuns/{id}/artifacts — keep ascFetchAll
156
158
  const items = await ascFetchAll<{
157
159
  id: string
158
160
  attributes: {
@@ -182,6 +184,7 @@ export async function xcodeCloudTestResultsList(opts: {
182
184
  buildRunId: string
183
185
  output?: string
184
186
  }): Promise<void> {
187
+ // No SDK function for /v1/ciBuildRuns/{id}/testResults — keep ascFetchAll
185
188
  const items = await ascFetchAll<{
186
189
  id: string
187
190
  attributes: {
package/src/index.ts CHANGED
@@ -25,6 +25,9 @@ import { printError } from "./utils/output.ts"
25
25
  import { buildHelpDocument } from "./utils/help-spec.ts"
26
26
  import pkg from "../package.json"
27
27
 
28
+ // ---- hey-api generated client (sets JWT auth interceptor) ----
29
+ import "./api/hey-api-client.ts"
30
+
28
31
  // ---- Auth commands ----
29
32
  import { authLogin, authList, authLogout, authStatus, authUse, profileView, profileUpdate, profileDelete as authProfileDelete } from "./commands/auth.ts"
30
33
 
@@ -35,7 +38,7 @@ import { appsList, appsGet } from "./commands/apps.ts"
35
38
  import { buildsList, buildsGet, buildsUpdateBetaNotes } from "./commands/builds.ts"
36
39
 
37
40
  // ---- Versions ----
38
- import { versionsList, versionsCreate, versionsSubmit } from "./commands/versions.ts"
41
+ import { versionsList, versionsCreate, versionsSubmit, releaseRequestCreate } from "./commands/versions.ts"
39
42
 
40
43
  // ---- TestFlight ----
41
44
  import {
@@ -48,6 +51,27 @@ import {
48
51
  // ---- Metadata ----
49
52
  import { metadataList, metadataUpdate } from "./commands/metadata.ts"
50
53
 
54
+ // ---- Localizations ----
55
+ import { localizationsList, localizationsGet, localizationsCreate, localizationsDelete } from "./commands/localizations.ts"
56
+
57
+ // ---- Review Details ----
58
+ import { reviewDetailsGet, reviewDetailsUpdate } from "./commands/review-details.ts"
59
+
60
+ // ---- Review Submission ----
61
+ import {
62
+ reviewSubmissionList,
63
+ reviewSubmissionCreate,
64
+ reviewSubmissionGet,
65
+ reviewSubmissionSubmit,
66
+ reviewSubmissionCancel,
67
+ reviewSubmissionItemsList,
68
+ reviewSubmissionItemAdd,
69
+ reviewSubmissionItemDelete,
70
+ } from "./commands/review-submission.ts"
71
+
72
+ // ---- Phased Release ----
73
+ import { phasedReleaseGet, phasedReleaseCreate, phasedReleaseUpdate, phasedReleaseDelete } from "./commands/phased-release.ts"
74
+
51
75
  // ---- Reviews ----
52
76
  import { reviewsList, reviewsGet, reviewsRespond } from "./commands/reviews.ts"
53
77
 
@@ -439,6 +463,25 @@ const versionsSubmitCmd = defineCommand({
439
463
  async run({ args }) { await versionsSubmit(args["version-id"]) },
440
464
  })
441
465
 
466
+ const releaseRequestCreateCmd = defineCommand({
467
+ meta: {
468
+ name: "create",
469
+ description: "Manually trigger release of an approved version. Use when auto-release is disabled. Requires the version to be in PENDING_DEVELOPER_RELEASE state.",
470
+ },
471
+ args: {
472
+ "version-id": { type: "string", alias: "v", required: true, description: "Version ID (get from: asc versions list)" },
473
+ },
474
+ async run({ args }) { await releaseRequestCreate(args["version-id"]) },
475
+ })
476
+
477
+ const releaseRequestCmd = defineCommand({
478
+ meta: {
479
+ name: "release-request",
480
+ description: "Manually release an approved version. Use after Apple approves the build and auto-release is off.",
481
+ },
482
+ subCommands: { create: releaseRequestCreateCmd },
483
+ })
484
+
442
485
  const versionsCmd = defineCommand({
443
486
  meta: {
444
487
  name: "versions",
@@ -579,6 +622,270 @@ const metadataCmd = defineCommand({
579
622
  subCommands: { list: metadataListCmd, update: metadataUpdateCmd },
580
623
  })
581
624
 
625
+ // =============================================================================
626
+ // LOCALIZATIONS
627
+ // =============================================================================
628
+
629
+ const localizationsListCmd = defineCommand({
630
+ meta: { name: "list", description: "List all localizations for a version. Returns localization IDs needed for screenshots and metadata updates." },
631
+ args: {
632
+ "version-id": { type: "string", alias: "v", required: true, description: "Version ID. Use: asc versions list --app-id <id>" },
633
+ output: { type: "string", alias: "o", description: "Output format: table | json" },
634
+ },
635
+ async run({ args }) { await localizationsList({ versionId: args["version-id"], output: args.output }) },
636
+ })
637
+
638
+ const localizationsGetCmd = defineCommand({
639
+ meta: { name: "get", description: "Get full details of a localization including all text fields and screenshot set IDs." },
640
+ args: {
641
+ id: { type: "positional", required: true, description: "Localization ID. Use: asc localizations list --version-id <id>" },
642
+ output: { type: "string", alias: "o", description: "Output format: table | json" },
643
+ },
644
+ async run({ args }) { await localizationsGet(args.id, args.output) },
645
+ })
646
+
647
+ const localizationsCreateCmd = defineCommand({
648
+ meta: { name: "create", description: "Add a new locale to a version. Use to support additional languages." },
649
+ args: {
650
+ "version-id": { type: "string", alias: "v", required: true, description: "Version ID. Use: asc versions list" },
651
+ locale: { type: "string", alias: "l", required: true, description: "Locale code, e.g. en-US, fr-FR, ja" },
652
+ description: { type: "string", description: "App description for this locale" },
653
+ "whats-new": { type: "string", description: "What's new text for this locale" },
654
+ keywords: { type: "string", description: "Comma-separated keywords" },
655
+ },
656
+ async run({ args }) {
657
+ await localizationsCreate({
658
+ versionId: args["version-id"],
659
+ locale: args.locale,
660
+ description: args.description,
661
+ whatsNew: args["whats-new"],
662
+ keywords: args.keywords,
663
+ })
664
+ },
665
+ })
666
+
667
+ const localizationsDeleteCmd = defineCommand({
668
+ meta: { name: "delete", description: "Remove a localization from a version." },
669
+ args: {
670
+ id: { type: "positional", required: true, description: "Localization ID. Use: asc localizations list" },
671
+ },
672
+ async run({ args }) { await localizationsDelete(args.id) },
673
+ })
674
+
675
+ const localizationsCmd = defineCommand({
676
+ meta: {
677
+ name: "localizations",
678
+ description: [
679
+ "Manage App Store version localizations.",
680
+ "Each locale has its own metadata and screenshot sets.",
681
+ "Get localization IDs first: asc localizations list --version-id <id>",
682
+ ].join("\n"),
683
+ },
684
+ subCommands: { list: localizationsListCmd, get: localizationsGetCmd, create: localizationsCreateCmd, delete: localizationsDeleteCmd },
685
+ })
686
+
687
+ // =============================================================================
688
+ // REVIEW DETAILS
689
+ // =============================================================================
690
+
691
+ const reviewDetailsGetCmd = defineCommand({
692
+ meta: { name: "get", description: "Show current review contact info, demo account, and reviewer notes for a version." },
693
+ args: {
694
+ "version-id": { type: "string", alias: "v", required: true, description: "Version ID. Use: asc versions list" },
695
+ output: { type: "string", alias: "o", description: "Output format: table | json" },
696
+ },
697
+ async run({ args }) { await reviewDetailsGet(args["version-id"], args.output) },
698
+ })
699
+
700
+ const reviewDetailsUpdateCmd = defineCommand({
701
+ meta: {
702
+ name: "update",
703
+ description: [
704
+ "Set reviewer contact info, demo account credentials, and notes for this version.",
705
+ "Required before submitting for review.",
706
+ "Creates review details if they don't exist yet.",
707
+ ].join("\n"),
708
+ },
709
+ args: {
710
+ "version-id": { type: "string", alias: "v", required: true, description: "Version ID. Use: asc versions list" },
711
+ "contact-first-name": { type: "string", description: "Reviewer contact first name" },
712
+ "contact-last-name": { type: "string", description: "Reviewer contact last name" },
713
+ "contact-email": { type: "string", description: "Reviewer contact email" },
714
+ "contact-phone": { type: "string", description: "Reviewer contact phone" },
715
+ "demo-account-name": { type: "string", description: "Demo account username" },
716
+ "demo-account-password": { type: "string", description: "Demo account password" },
717
+ "demo-account-required": { type: "boolean", description: "Whether a demo account is required" },
718
+ notes: { type: "string", description: "Additional notes for the reviewer (up to 4000 characters)" },
719
+ },
720
+ async run({ args }) {
721
+ await reviewDetailsUpdate({
722
+ versionId: args["version-id"],
723
+ contactFirstName: args["contact-first-name"],
724
+ contactLastName: args["contact-last-name"],
725
+ contactEmail: args["contact-email"],
726
+ contactPhone: args["contact-phone"],
727
+ demoAccountName: args["demo-account-name"],
728
+ demoAccountPassword: args["demo-account-password"],
729
+ demoAccountRequired: args["demo-account-required"],
730
+ notes: args.notes,
731
+ })
732
+ },
733
+ })
734
+
735
+ const reviewDetailsCmd = defineCommand({
736
+ meta: {
737
+ name: "review-details",
738
+ description: "Manage App Store review contact info, demo account, and reviewer notes. Must be set before submission.",
739
+ },
740
+ subCommands: { get: reviewDetailsGetCmd, update: reviewDetailsUpdateCmd },
741
+ })
742
+
743
+ // =============================================================================
744
+ // REVIEW SUBMISSION
745
+ // =============================================================================
746
+
747
+ const reviewSubmissionListCmd = defineCommand({
748
+ meta: { name: "list", description: "List all review submissions for an app." },
749
+ args: {
750
+ "app-id": { type: "string", required: true, description: "App ID. Use: asc apps list" },
751
+ output: { type: "string", alias: "o", description: "Output format: table | json" },
752
+ },
753
+ async run({ args }) { await reviewSubmissionList({ appId: args["app-id"], output: args.output }) },
754
+ })
755
+
756
+ const reviewSubmissionCreateCmd = defineCommand({
757
+ meta: { name: "create", description: "Create a new review submission. Add items (versions) then call submit to trigger review." },
758
+ args: {
759
+ "app-id": { type: "string", required: true, description: "App ID. Use: asc apps list" },
760
+ platform: { type: "string", description: "Platform: IOS (default) | MAC_OS | TV_OS | VISION_OS" },
761
+ },
762
+ async run({ args }) { await reviewSubmissionCreate({ appId: args["app-id"], platform: args.platform }) },
763
+ })
764
+
765
+ const reviewSubmissionGetCmd = defineCommand({
766
+ meta: { name: "get", description: "Get status and details of a review submission." },
767
+ args: {
768
+ id: { type: "positional", required: true, description: "Submission ID. Use: asc review-submission list" },
769
+ output: { type: "string", alias: "o", description: "Output format: table | json" },
770
+ },
771
+ async run({ args }) { await reviewSubmissionGet(args.id, args.output) },
772
+ })
773
+
774
+ const reviewSubmissionSubmitCmd = defineCommand({
775
+ meta: { name: "submit", description: "Submit for review. Sets submitted=true — triggers Apple review. Ensure metadata and screenshots are complete first." },
776
+ args: {
777
+ "submission-id": { type: "string", required: true, description: "Submission ID. Use: asc review-submission create" },
778
+ },
779
+ async run({ args }) { await reviewSubmissionSubmit(args["submission-id"]) },
780
+ })
781
+
782
+ const reviewSubmissionCancelCmd = defineCommand({
783
+ meta: { name: "cancel", description: "Cancel a pending review submission." },
784
+ args: {
785
+ "submission-id": { type: "string", required: true, description: "Submission ID" },
786
+ },
787
+ async run({ args }) { await reviewSubmissionCancel(args["submission-id"]) },
788
+ })
789
+
790
+ const reviewSubmissionItemsListCmd = defineCommand({
791
+ meta: { name: "list", description: "List items (versions) attached to a review submission." },
792
+ args: {
793
+ "submission-id": { type: "string", required: true, description: "Submission ID" },
794
+ output: { type: "string", alias: "o", description: "Output format: table | json" },
795
+ },
796
+ async run({ args }) { await reviewSubmissionItemsList({ submissionId: args["submission-id"], output: args.output }) },
797
+ })
798
+
799
+ const reviewSubmissionItemAddCmd = defineCommand({
800
+ meta: { name: "add", description: "Add an app store version to a review submission." },
801
+ args: {
802
+ "submission-id": { type: "string", required: true, description: "Submission ID" },
803
+ "app-store-version-id": { type: "string", required: true, description: "Version ID. Use: asc versions list" },
804
+ },
805
+ async run({ args }) {
806
+ await reviewSubmissionItemAdd({ submissionId: args["submission-id"], appStoreVersionId: args["app-store-version-id"] })
807
+ },
808
+ })
809
+
810
+ const reviewSubmissionItemDeleteCmd = defineCommand({
811
+ meta: { name: "delete", description: "Remove an item from a review submission." },
812
+ args: {
813
+ id: { type: "positional", required: true, description: "Item ID. Use: asc review-submission items list" },
814
+ },
815
+ async run({ args }) { await reviewSubmissionItemDelete(args.id) },
816
+ })
817
+
818
+ const reviewSubmissionCmd = defineCommand({
819
+ meta: {
820
+ name: "review-submission",
821
+ description: [
822
+ "Manage App Store review submissions (modern API).",
823
+ "Workflow: create → items add → submit",
824
+ " asc review-submission create --app-id <id>",
825
+ " asc review-submission items add --submission-id <id> --app-store-version-id <id>",
826
+ " asc review-submission submit --submission-id <id>",
827
+ ].join("\n"),
828
+ },
829
+ subCommands: {
830
+ list: reviewSubmissionListCmd,
831
+ create: reviewSubmissionCreateCmd,
832
+ get: reviewSubmissionGetCmd,
833
+ submit: reviewSubmissionSubmitCmd,
834
+ cancel: reviewSubmissionCancelCmd,
835
+ items: defineCommand({
836
+ meta: { name: "items", description: "Manage items (versions) in a review submission" },
837
+ subCommands: { list: reviewSubmissionItemsListCmd, add: reviewSubmissionItemAddCmd, delete: reviewSubmissionItemDeleteCmd },
838
+ }),
839
+ },
840
+ })
841
+
842
+ // =============================================================================
843
+ // PHASED RELEASE
844
+ // =============================================================================
845
+
846
+ const phasedReleaseGetCmd = defineCommand({
847
+ meta: { name: "get", description: "Get phased release status for a version. Shows current rollout day and state." },
848
+ args: {
849
+ "version-id": { type: "string", alias: "v", required: true, description: "Version ID. Use: asc versions list" },
850
+ output: { type: "string", alias: "o", description: "Output format: table | json" },
851
+ },
852
+ async run({ args }) { await phasedReleaseGet(args["version-id"], args.output) },
853
+ })
854
+
855
+ const phasedReleaseCreateCmd = defineCommand({
856
+ meta: { name: "create", description: "Enable phased (gradual) rollout for a version. Rolls out over 7 days: 1% → 2% → 5% → 10% → 20% → 50% → 100%." },
857
+ args: {
858
+ "version-id": { type: "string", alias: "v", required: true, description: "Version ID. Use: asc versions list" },
859
+ state: { type: "string", description: "Initial state: INACTIVE (default) | ACTIVE | PAUSED" },
860
+ },
861
+ async run({ args }) { await phasedReleaseCreate({ versionId: args["version-id"], state: args.state }) },
862
+ })
863
+
864
+ const phasedReleaseUpdateCmd = defineCommand({
865
+ meta: { name: "update", description: "Change phased release state. PAUSED halts rollout, ACTIVE resumes, COMPLETE skips to 100%." },
866
+ args: {
867
+ id: { type: "positional", required: true, description: "Phased release ID. Use: asc phased-release get --version-id <id>" },
868
+ state: { type: "string", required: true, description: "New state: INACTIVE | ACTIVE | PAUSED | COMPLETE" },
869
+ },
870
+ async run({ args }) { await phasedReleaseUpdate({ phasedReleaseId: args.id, state: args.state }) },
871
+ })
872
+
873
+ const phasedReleaseDeleteCmd = defineCommand({
874
+ meta: { name: "delete", description: "Remove phased release configuration from a version." },
875
+ args: {
876
+ id: { type: "positional", required: true, description: "Phased release ID. Use: asc phased-release get --version-id <id>" },
877
+ },
878
+ async run({ args }) { await phasedReleaseDelete(args.id) },
879
+ })
880
+
881
+ const phasedReleaseCmd = defineCommand({
882
+ meta: {
883
+ name: "phased-release",
884
+ description: "Manage phased (gradual) rollout for App Store versions. Rolls out over 7 days to increasing percentages of users.",
885
+ },
886
+ subCommands: { get: phasedReleaseGetCmd, create: phasedReleaseCreateCmd, update: phasedReleaseUpdateCmd, delete: phasedReleaseDeleteCmd },
887
+ })
888
+
582
889
  // =============================================================================
583
890
  // REVIEWS (Customer Reviews)
584
891
  // =============================================================================
@@ -1594,8 +1901,13 @@ const main = defineCommand({
1594
1901
  apps: appsCmd,
1595
1902
  builds: buildsCmd,
1596
1903
  versions: versionsCmd,
1904
+ "release-request": releaseRequestCmd,
1597
1905
  testflight: testflightCmd,
1598
1906
  metadata: metadataCmd,
1907
+ localizations: localizationsCmd,
1908
+ "review-details": reviewDetailsCmd,
1909
+ "review-submission": reviewSubmissionCmd,
1910
+ "phased-release": phasedReleaseCmd,
1599
1911
  reviews: reviewsCmd,
1600
1912
  pricing: pricingCmd,
1601
1913
  availability: availabilityCmd,