@gpc-cli/cli 0.1.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.
Files changed (58) hide show
  1. package/LICENSE +21 -0
  2. package/dist/apps-BBYNHB2H.js +83 -0
  3. package/dist/apps-BBYNHB2H.js.map +1 -0
  4. package/dist/auth-T7IDSMVX.js +129 -0
  5. package/dist/auth-T7IDSMVX.js.map +1 -0
  6. package/dist/bin.d.ts +2 -0
  7. package/dist/bin.js +36 -0
  8. package/dist/bin.js.map +1 -0
  9. package/dist/chunk-4QV4WD3F.js +103 -0
  10. package/dist/chunk-4QV4WD3F.js.map +1 -0
  11. package/dist/chunk-IVVT73IP.js +245 -0
  12. package/dist/chunk-IVVT73IP.js.map +1 -0
  13. package/dist/chunk-QMKZYXDJ.js +24 -0
  14. package/dist/chunk-QMKZYXDJ.js.map +1 -0
  15. package/dist/completion-U44CGHRH.js +145 -0
  16. package/dist/completion-U44CGHRH.js.map +1 -0
  17. package/dist/config-K7UJKIXT.js +44 -0
  18. package/dist/config-K7UJKIXT.js.map +1 -0
  19. package/dist/docs-CVTWIVMS.js +20 -0
  20. package/dist/docs-CVTWIVMS.js.map +1 -0
  21. package/dist/doctor-VDDUPTIM.js +59 -0
  22. package/dist/doctor-VDDUPTIM.js.map +1 -0
  23. package/dist/iap-QIV4CXKZ.js +158 -0
  24. package/dist/iap-QIV4CXKZ.js.map +1 -0
  25. package/dist/index.d.ts +6 -0
  26. package/dist/index.js +8 -0
  27. package/dist/index.js.map +1 -0
  28. package/dist/listings-PF5FDXKQ.js +261 -0
  29. package/dist/listings-PF5FDXKQ.js.map +1 -0
  30. package/dist/pricing-S4SB5FXJ.js +52 -0
  31. package/dist/pricing-S4SB5FXJ.js.map +1 -0
  32. package/dist/prompt-VP5LURRP.js +20 -0
  33. package/dist/prompt-VP5LURRP.js.map +1 -0
  34. package/dist/publish-3BAIN4NQ.js +114 -0
  35. package/dist/publish-3BAIN4NQ.js.map +1 -0
  36. package/dist/purchases-E6A2T5WQ.js +231 -0
  37. package/dist/purchases-E6A2T5WQ.js.map +1 -0
  38. package/dist/releases-464IMEEF.js +231 -0
  39. package/dist/releases-464IMEEF.js.map +1 -0
  40. package/dist/reports-3YAD4U4F.js +129 -0
  41. package/dist/reports-3YAD4U4F.js.map +1 -0
  42. package/dist/reviews-2CLM53E3.js +125 -0
  43. package/dist/reviews-2CLM53E3.js.map +1 -0
  44. package/dist/status-M7U3YNMU.js +32 -0
  45. package/dist/status-M7U3YNMU.js.map +1 -0
  46. package/dist/subscriptions-PUHH4FBB.js +376 -0
  47. package/dist/subscriptions-PUHH4FBB.js.map +1 -0
  48. package/dist/testers-WWZMLB7J.js +145 -0
  49. package/dist/testers-WWZMLB7J.js.map +1 -0
  50. package/dist/tracks-427E34S3.js +39 -0
  51. package/dist/tracks-427E34S3.js.map +1 -0
  52. package/dist/users-E5Y5HI6K.js +145 -0
  53. package/dist/users-E5Y5HI6K.js.map +1 -0
  54. package/dist/validate-TPKVSIMR.js +37 -0
  55. package/dist/validate-TPKVSIMR.js.map +1 -0
  56. package/dist/vitals-YMZMUPNA.js +166 -0
  57. package/dist/vitals-YMZMUPNA.js.map +1 -0
  58. package/package.json +48 -0
@@ -0,0 +1,114 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ isDryRun,
4
+ printDryRun
5
+ } from "./chunk-QMKZYXDJ.js";
6
+ import {
7
+ isInteractive,
8
+ promptInput,
9
+ promptSelect
10
+ } from "./chunk-4QV4WD3F.js";
11
+
12
+ // src/commands/publish.ts
13
+ import { appendFile } from "fs/promises";
14
+ import { loadConfig, getCacheDir } from "@gpc-cli/config";
15
+ import { resolveAuth } from "@gpc-cli/auth";
16
+ import { createApiClient } from "@gpc-cli/api";
17
+ import { publish, writeAuditLog, createAuditEntry } from "@gpc-cli/core";
18
+ import { detectOutputFormat, formatOutput } from "@gpc-cli/core";
19
+ function resolvePackageName(packageArg, config) {
20
+ const name = packageArg || config.app;
21
+ if (!name) {
22
+ console.error("Error: No package name. Use --app <package> or gpc config set app <package>");
23
+ process.exit(2);
24
+ }
25
+ return name;
26
+ }
27
+ function registerPublishCommand(program) {
28
+ program.command("publish <file>").description("Validate, upload, and release in one step").option("--track <track>", "Target track", "internal").option("--rollout <percent>", "Staged rollout percentage (1-100)").option("--notes <text>", "Release notes (en-US)").option("--notes-dir <dir>", "Read release notes from directory (<dir>/<lang>.txt)").option("--name <name>", "Release name").option("--mapping <file>", "ProGuard/R8 mapping file for deobfuscation").option("--retry-log <path>", "Write retry log entries to file (JSONL)").action(async (file, options) => {
29
+ if (options.notes && options.notesDir) {
30
+ console.error("Error: Cannot use both --notes and --notes-dir");
31
+ process.exit(2);
32
+ }
33
+ const config = await loadConfig();
34
+ const packageName = resolvePackageName(program.opts().app, config);
35
+ const format = detectOutputFormat();
36
+ if (isInteractive(program)) {
37
+ if (!options.track || options.track === "internal") {
38
+ const tracks = ["internal", "alpha", "beta", "production"];
39
+ options.track = await promptSelect("Select track:", tracks, "internal");
40
+ }
41
+ if (!options.rollout && options.track === "production") {
42
+ const rolloutStr = await promptInput("Staged rollout percentage (1-100, blank for full)", "100");
43
+ if (rolloutStr && rolloutStr !== "100") {
44
+ options.rollout = rolloutStr;
45
+ }
46
+ }
47
+ if (!options.notes && !options.notesDir) {
48
+ const notes = await promptInput("Release notes (en-US, blank to skip)");
49
+ if (notes) options.notes = notes;
50
+ }
51
+ }
52
+ if (isDryRun(program)) {
53
+ printDryRun({
54
+ command: "publish",
55
+ action: "publish",
56
+ target: file,
57
+ details: { track: options.track, rollout: options.rollout }
58
+ }, format, formatOutput);
59
+ return;
60
+ }
61
+ const auditEntry = createAuditEntry("publish", {
62
+ file,
63
+ track: options.track,
64
+ rollout: options.rollout
65
+ }, packageName);
66
+ let onRetry;
67
+ if (options.retryLog) {
68
+ onRetry = (entry) => {
69
+ appendFile(options.retryLog, JSON.stringify(entry) + "\n").catch(() => {
70
+ });
71
+ };
72
+ }
73
+ const auth = await resolveAuth({
74
+ serviceAccountPath: config.auth?.serviceAccount,
75
+ cachePath: getCacheDir()
76
+ });
77
+ const client = createApiClient({ auth, onRetry });
78
+ try {
79
+ const result = await publish(client, packageName, file, {
80
+ track: options.track,
81
+ rolloutPercent: options.rollout ? Number(options.rollout) : void 0,
82
+ notes: options.notes,
83
+ notesDir: options.notesDir,
84
+ releaseName: options.name,
85
+ mappingFile: options.mapping
86
+ });
87
+ if (!result.upload) {
88
+ console.error("Validation failed:");
89
+ for (const check of result.validation.checks) {
90
+ const icon = check.passed ? "\u2713" : "\u2717";
91
+ console.error(` ${icon} ${check.name}: ${check.message}`);
92
+ }
93
+ auditEntry.success = false;
94
+ auditEntry.error = "Validation failed";
95
+ process.exit(1);
96
+ }
97
+ console.log(formatOutput(result, format));
98
+ auditEntry.success = true;
99
+ } catch (error) {
100
+ auditEntry.success = false;
101
+ auditEntry.error = error instanceof Error ? error.message : String(error);
102
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
103
+ process.exit(4);
104
+ } finally {
105
+ auditEntry.durationMs = Date.now() - new Date(auditEntry.timestamp).getTime();
106
+ writeAuditLog(auditEntry).catch(() => {
107
+ });
108
+ }
109
+ });
110
+ }
111
+ export {
112
+ registerPublishCommand
113
+ };
114
+ //# sourceMappingURL=publish-3BAIN4NQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/publish.ts"],"sourcesContent":["import { appendFile } from \"node:fs/promises\";\nimport type { Command } from \"commander\";\nimport { loadConfig, getCacheDir } from \"@gpc-cli/config\";\nimport { resolveAuth } from \"@gpc-cli/auth\";\nimport { createApiClient } from \"@gpc-cli/api\";\nimport type { RetryLogEntry } from \"@gpc-cli/api\";\nimport { publish, writeAuditLog, createAuditEntry } from \"@gpc-cli/core\";\nimport { detectOutputFormat, formatOutput } from \"@gpc-cli/core\";\nimport { isDryRun, printDryRun } from \"../dry-run.js\";\nimport { isInteractive, promptSelect, promptInput } from \"../prompt.js\";\n\nfunction resolvePackageName(packageArg: string | undefined, config: any): string {\n const name = packageArg || config.app;\n if (!name) {\n console.error(\"Error: No package name. Use --app <package> or gpc config set app <package>\");\n process.exit(2);\n }\n return name;\n}\n\nexport function registerPublishCommand(program: Command): void {\n program\n .command(\"publish <file>\")\n .description(\"Validate, upload, and release in one step\")\n .option(\"--track <track>\", \"Target track\", \"internal\")\n .option(\"--rollout <percent>\", \"Staged rollout percentage (1-100)\")\n .option(\"--notes <text>\", \"Release notes (en-US)\")\n .option(\"--notes-dir <dir>\", \"Read release notes from directory (<dir>/<lang>.txt)\")\n .option(\"--name <name>\", \"Release name\")\n .option(\"--mapping <file>\", \"ProGuard/R8 mapping file for deobfuscation\")\n .option(\"--retry-log <path>\", \"Write retry log entries to file (JSONL)\")\n .action(async (file: string, options) => {\n if (options.notes && options.notesDir) {\n console.error(\"Error: Cannot use both --notes and --notes-dir\");\n process.exit(2);\n }\n\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts().app, config);\n const format = detectOutputFormat();\n\n // Interactive mode: prompt for missing options\n if (isInteractive(program)) {\n if (!options.track || options.track === \"internal\") {\n const tracks = [\"internal\", \"alpha\", \"beta\", \"production\"];\n options.track = await promptSelect(\"Select track:\", tracks, \"internal\");\n }\n\n if (!options.rollout && options.track === \"production\") {\n const rolloutStr = await promptInput(\"Staged rollout percentage (1-100, blank for full)\", \"100\");\n if (rolloutStr && rolloutStr !== \"100\") {\n options.rollout = rolloutStr;\n }\n }\n\n if (!options.notes && !options.notesDir) {\n const notes = await promptInput(\"Release notes (en-US, blank to skip)\");\n if (notes) options.notes = notes;\n }\n }\n\n if (isDryRun(program)) {\n printDryRun({\n command: \"publish\",\n action: \"publish\",\n target: file,\n details: { track: options.track, rollout: options.rollout },\n }, format, formatOutput);\n return;\n }\n\n const auditEntry = createAuditEntry(\"publish\", {\n file,\n track: options.track,\n rollout: options.rollout,\n }, packageName);\n\n let onRetry: ((entry: RetryLogEntry) => void) | undefined;\n if (options.retryLog) {\n onRetry = (entry: RetryLogEntry) => {\n appendFile(options.retryLog, JSON.stringify(entry) + \"\\n\").catch(() => {});\n };\n }\n\n const auth = await resolveAuth({\n serviceAccountPath: config.auth?.serviceAccount,\n cachePath: getCacheDir(),\n });\n const client = createApiClient({ auth, onRetry });\n\n try {\n const result = await publish(client, packageName, file, {\n track: options.track,\n rolloutPercent: options.rollout ? Number(options.rollout) : undefined,\n notes: options.notes,\n notesDir: options.notesDir,\n releaseName: options.name,\n mappingFile: options.mapping,\n });\n\n if (!result.upload) {\n console.error(\"Validation failed:\");\n for (const check of result.validation.checks) {\n const icon = check.passed ? \"✓\" : \"✗\";\n console.error(` ${icon} ${check.name}: ${check.message}`);\n }\n auditEntry.success = false;\n auditEntry.error = \"Validation failed\";\n process.exit(1);\n }\n\n console.log(formatOutput(result, format));\n auditEntry.success = true;\n } catch (error) {\n auditEntry.success = false;\n auditEntry.error = error instanceof Error ? error.message : String(error);\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n } finally {\n auditEntry.durationMs = Date.now() - new Date(auditEntry.timestamp).getTime();\n writeAuditLog(auditEntry).catch(() => {});\n }\n });\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,231 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ isDryRun,
4
+ printDryRun
5
+ } from "./chunk-QMKZYXDJ.js";
6
+ import {
7
+ isInteractive,
8
+ requireConfirm,
9
+ requireOption
10
+ } from "./chunk-4QV4WD3F.js";
11
+
12
+ // src/commands/purchases.ts
13
+ import { loadConfig } from "@gpc-cli/config";
14
+ import { resolveAuth } from "@gpc-cli/auth";
15
+ import { createApiClient } from "@gpc-cli/api";
16
+ import {
17
+ getProductPurchase,
18
+ acknowledgeProductPurchase,
19
+ consumeProductPurchase,
20
+ getSubscriptionPurchase,
21
+ cancelSubscriptionPurchase,
22
+ deferSubscriptionPurchase,
23
+ revokeSubscriptionPurchase,
24
+ listVoidedPurchases,
25
+ refundOrder,
26
+ detectOutputFormat,
27
+ formatOutput
28
+ } from "@gpc-cli/core";
29
+ function resolvePackageName(packageArg, config) {
30
+ const name = packageArg || config.app;
31
+ if (!name) {
32
+ console.error("Error: No package name. Use --app <package> or gpc config set app <package>");
33
+ process.exit(2);
34
+ }
35
+ return name;
36
+ }
37
+ async function getClient(config) {
38
+ const auth = await resolveAuth({ serviceAccountPath: config.auth?.serviceAccount });
39
+ return createApiClient({ auth });
40
+ }
41
+ function registerPurchasesCommands(program) {
42
+ const purchases = program.command("purchases").description("Manage purchases and orders");
43
+ purchases.command("get <product-id> <token>").description("Get a product purchase").action(async (productId, token) => {
44
+ const config = await loadConfig();
45
+ const packageName = resolvePackageName(program.opts().app, config);
46
+ const client = await getClient(config);
47
+ const format = detectOutputFormat();
48
+ try {
49
+ const result = await getProductPurchase(client, packageName, productId, token);
50
+ console.log(formatOutput(result, format));
51
+ } catch (error) {
52
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
53
+ process.exit(4);
54
+ }
55
+ });
56
+ purchases.command("acknowledge <product-id> <token>").description("Acknowledge a product purchase").option("--payload <text>", "Developer payload").action(async (productId, token, options) => {
57
+ const config = await loadConfig();
58
+ const packageName = resolvePackageName(program.opts().app, config);
59
+ if (isDryRun(program)) {
60
+ const format = detectOutputFormat();
61
+ printDryRun({
62
+ command: "purchases acknowledge",
63
+ action: "acknowledge",
64
+ target: `${productId}/${token}`,
65
+ details: { payload: options.payload }
66
+ }, format, formatOutput);
67
+ return;
68
+ }
69
+ const client = await getClient(config);
70
+ try {
71
+ await acknowledgeProductPurchase(client, packageName, productId, token, options.payload);
72
+ console.log(`Purchase acknowledged.`);
73
+ } catch (error) {
74
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
75
+ process.exit(4);
76
+ }
77
+ });
78
+ purchases.command("consume <product-id> <token>").description("Consume a product purchase").action(async (productId, token) => {
79
+ const config = await loadConfig();
80
+ const packageName = resolvePackageName(program.opts().app, config);
81
+ if (isDryRun(program)) {
82
+ const format = detectOutputFormat();
83
+ printDryRun({
84
+ command: "purchases consume",
85
+ action: "consume",
86
+ target: `${productId}/${token}`
87
+ }, format, formatOutput);
88
+ return;
89
+ }
90
+ const client = await getClient(config);
91
+ try {
92
+ await consumeProductPurchase(client, packageName, productId, token);
93
+ console.log(`Purchase consumed.`);
94
+ } catch (error) {
95
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
96
+ process.exit(4);
97
+ }
98
+ });
99
+ const sub = purchases.command("subscription").description("Manage subscription purchases");
100
+ sub.command("get <token>").description("Get a subscription purchase (v2)").action(async (token) => {
101
+ const config = await loadConfig();
102
+ const packageName = resolvePackageName(program.opts().app, config);
103
+ const client = await getClient(config);
104
+ const format = detectOutputFormat();
105
+ try {
106
+ const result = await getSubscriptionPurchase(client, packageName, token);
107
+ console.log(formatOutput(result, format));
108
+ } catch (error) {
109
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
110
+ process.exit(4);
111
+ }
112
+ });
113
+ sub.command("cancel <subscription-id> <token>").description("Cancel a subscription (v1)").action(async (subscriptionId, token) => {
114
+ const config = await loadConfig();
115
+ const packageName = resolvePackageName(program.opts().app, config);
116
+ if (isDryRun(program)) {
117
+ const format = detectOutputFormat();
118
+ printDryRun({
119
+ command: "purchases subscription cancel",
120
+ action: "cancel subscription",
121
+ target: `${subscriptionId}/${token}`
122
+ }, format, formatOutput);
123
+ return;
124
+ }
125
+ const client = await getClient(config);
126
+ try {
127
+ await cancelSubscriptionPurchase(client, packageName, subscriptionId, token);
128
+ console.log(`Subscription cancelled.`);
129
+ } catch (error) {
130
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
131
+ process.exit(4);
132
+ }
133
+ });
134
+ sub.command("defer <subscription-id> <token>").description("Defer a subscription expiry").option("--expiry <iso-date>", "Desired new expiry date (ISO 8601)").action(async (subscriptionId, token, options) => {
135
+ const config = await loadConfig();
136
+ const packageName = resolvePackageName(program.opts().app, config);
137
+ const format = detectOutputFormat();
138
+ const interactive = isInteractive(program);
139
+ options.expiry = await requireOption("expiry", options.expiry, {
140
+ message: "New expiry date (ISO 8601, e.g. 2026-12-31T23:59:59Z):"
141
+ }, interactive);
142
+ if (isDryRun(program)) {
143
+ printDryRun({
144
+ command: "purchases subscription defer",
145
+ action: "defer subscription",
146
+ target: `${subscriptionId}/${token}`,
147
+ details: { expiry: options.expiry }
148
+ }, format, formatOutput);
149
+ return;
150
+ }
151
+ const client = await getClient(config);
152
+ try {
153
+ const result = await deferSubscriptionPurchase(client, packageName, subscriptionId, token, options.expiry);
154
+ console.log(formatOutput(result, format));
155
+ } catch (error) {
156
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
157
+ process.exit(4);
158
+ }
159
+ });
160
+ sub.command("revoke <token>").description("Revoke a subscription (v2)").action(async (token) => {
161
+ const config = await loadConfig();
162
+ const packageName = resolvePackageName(program.opts().app, config);
163
+ if (isDryRun(program)) {
164
+ const format = detectOutputFormat();
165
+ printDryRun({
166
+ command: "purchases subscription revoke",
167
+ action: "revoke subscription",
168
+ target: token
169
+ }, format, formatOutput);
170
+ return;
171
+ }
172
+ const client = await getClient(config);
173
+ try {
174
+ await revokeSubscriptionPurchase(client, packageName, token);
175
+ console.log(`Subscription revoked.`);
176
+ } catch (error) {
177
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
178
+ process.exit(4);
179
+ }
180
+ });
181
+ purchases.command("voided").description("List voided purchases").option("--start-time <time>", "Start time (milliseconds)").option("--end-time <time>", "End time (milliseconds)").option("--max-results <n>", "Maximum results per page", parseInt).option("--limit <n>", "Maximum total results", parseInt).option("--next-page <token>", "Resume from page token").action(async (options) => {
182
+ const config = await loadConfig();
183
+ const packageName = resolvePackageName(program.opts().app, config);
184
+ const client = await getClient(config);
185
+ const format = detectOutputFormat();
186
+ try {
187
+ const result = await listVoidedPurchases(client, packageName, {
188
+ startTime: options.startTime,
189
+ endTime: options.endTime,
190
+ maxResults: options.maxResults,
191
+ limit: options.limit,
192
+ nextPage: options.nextPage
193
+ });
194
+ console.log(formatOutput(result, format));
195
+ } catch (error) {
196
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
197
+ process.exit(4);
198
+ }
199
+ });
200
+ const orders = purchases.command("orders").description("Manage orders");
201
+ orders.command("refund <order-id>").description("Refund an order").option("--full-refund", "Full refund").option("--prorated-refund", "Prorated refund").action(async (orderId, options) => {
202
+ const config = await loadConfig();
203
+ const packageName = resolvePackageName(program.opts().app, config);
204
+ await requireConfirm(`Refund order "${orderId}"?`, program);
205
+ if (isDryRun(program)) {
206
+ const format = detectOutputFormat();
207
+ printDryRun({
208
+ command: "purchases orders refund",
209
+ action: "refund",
210
+ target: orderId,
211
+ details: { fullRefund: options.fullRefund, proratedRefund: options.proratedRefund }
212
+ }, format, formatOutput);
213
+ return;
214
+ }
215
+ const client = await getClient(config);
216
+ try {
217
+ await refundOrder(client, packageName, orderId, {
218
+ fullRefund: options.fullRefund,
219
+ proratedRefund: options.proratedRefund
220
+ });
221
+ console.log(`Order ${orderId} refunded.`);
222
+ } catch (error) {
223
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
224
+ process.exit(4);
225
+ }
226
+ });
227
+ }
228
+ export {
229
+ registerPurchasesCommands
230
+ };
231
+ //# sourceMappingURL=purchases-E6A2T5WQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/purchases.ts"],"sourcesContent":["import type { Command } from \"commander\";\nimport { loadConfig } from \"@gpc-cli/config\";\nimport { resolveAuth } from \"@gpc-cli/auth\";\nimport { createApiClient } from \"@gpc-cli/api\";\nimport {\n getProductPurchase,\n acknowledgeProductPurchase,\n consumeProductPurchase,\n getSubscriptionPurchase,\n cancelSubscriptionPurchase,\n deferSubscriptionPurchase,\n revokeSubscriptionPurchase,\n listVoidedPurchases,\n refundOrder,\n detectOutputFormat,\n formatOutput,\n} from \"@gpc-cli/core\";\nimport { isDryRun, printDryRun } from \"../dry-run.js\";\nimport { isInteractive, requireOption, requireConfirm } from \"../prompt.js\";\n\nfunction resolvePackageName(packageArg: string | undefined, config: any): string {\n const name = packageArg || config.app;\n if (!name) {\n console.error(\"Error: No package name. Use --app <package> or gpc config set app <package>\");\n process.exit(2);\n }\n return name;\n}\n\nasync function getClient(config: any) {\n const auth = await resolveAuth({ serviceAccountPath: config.auth?.serviceAccount });\n return createApiClient({ auth });\n}\n\nexport function registerPurchasesCommands(program: Command): void {\n const purchases = program\n .command(\"purchases\")\n .description(\"Manage purchases and orders\");\n\n purchases\n .command(\"get <product-id> <token>\")\n .description(\"Get a product purchase\")\n .action(async (productId: string, token: string) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts().app, config);\n const client = await getClient(config);\n const format = detectOutputFormat();\n\n try {\n const result = await getProductPurchase(client, packageName, productId, token);\n console.log(formatOutput(result, format));\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n purchases\n .command(\"acknowledge <product-id> <token>\")\n .description(\"Acknowledge a product purchase\")\n .option(\"--payload <text>\", \"Developer payload\")\n .action(async (productId: string, token: string, options) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts().app, config);\n\n if (isDryRun(program)) {\n const format = detectOutputFormat();\n printDryRun({\n command: \"purchases acknowledge\",\n action: \"acknowledge\",\n target: `${productId}/${token}`,\n details: { payload: options.payload },\n }, format, formatOutput);\n return;\n }\n\n const client = await getClient(config);\n\n try {\n await acknowledgeProductPurchase(client, packageName, productId, token, options.payload);\n console.log(`Purchase acknowledged.`);\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n purchases\n .command(\"consume <product-id> <token>\")\n .description(\"Consume a product purchase\")\n .action(async (productId: string, token: string) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts().app, config);\n\n if (isDryRun(program)) {\n const format = detectOutputFormat();\n printDryRun({\n command: \"purchases consume\",\n action: \"consume\",\n target: `${productId}/${token}`,\n }, format, formatOutput);\n return;\n }\n\n const client = await getClient(config);\n\n try {\n await consumeProductPurchase(client, packageName, productId, token);\n console.log(`Purchase consumed.`);\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n // --- Subscription purchases ---\n const sub = purchases\n .command(\"subscription\")\n .description(\"Manage subscription purchases\");\n\n sub\n .command(\"get <token>\")\n .description(\"Get a subscription purchase (v2)\")\n .action(async (token: string) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts().app, config);\n const client = await getClient(config);\n const format = detectOutputFormat();\n\n try {\n const result = await getSubscriptionPurchase(client, packageName, token);\n console.log(formatOutput(result, format));\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n sub\n .command(\"cancel <subscription-id> <token>\")\n .description(\"Cancel a subscription (v1)\")\n .action(async (subscriptionId: string, token: string) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts().app, config);\n\n if (isDryRun(program)) {\n const format = detectOutputFormat();\n printDryRun({\n command: \"purchases subscription cancel\",\n action: \"cancel subscription\",\n target: `${subscriptionId}/${token}`,\n }, format, formatOutput);\n return;\n }\n\n const client = await getClient(config);\n\n try {\n await cancelSubscriptionPurchase(client, packageName, subscriptionId, token);\n console.log(`Subscription cancelled.`);\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n sub\n .command(\"defer <subscription-id> <token>\")\n .description(\"Defer a subscription expiry\")\n .option(\"--expiry <iso-date>\", \"Desired new expiry date (ISO 8601)\")\n .action(async (subscriptionId: string, token: string, options) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts().app, config);\n const format = detectOutputFormat();\n const interactive = isInteractive(program);\n\n options.expiry = await requireOption(\"expiry\", options.expiry, {\n message: \"New expiry date (ISO 8601, e.g. 2026-12-31T23:59:59Z):\",\n }, interactive);\n\n if (isDryRun(program)) {\n printDryRun({\n command: \"purchases subscription defer\",\n action: \"defer subscription\",\n target: `${subscriptionId}/${token}`,\n details: { expiry: options.expiry },\n }, format, formatOutput);\n return;\n }\n\n const client = await getClient(config);\n\n try {\n const result = await deferSubscriptionPurchase(client, packageName, subscriptionId, token, options.expiry);\n console.log(formatOutput(result, format));\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n sub\n .command(\"revoke <token>\")\n .description(\"Revoke a subscription (v2)\")\n .action(async (token: string) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts().app, config);\n\n if (isDryRun(program)) {\n const format = detectOutputFormat();\n printDryRun({\n command: \"purchases subscription revoke\",\n action: \"revoke subscription\",\n target: token,\n }, format, formatOutput);\n return;\n }\n\n const client = await getClient(config);\n\n try {\n await revokeSubscriptionPurchase(client, packageName, token);\n console.log(`Subscription revoked.`);\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n // --- Voided purchases ---\n purchases\n .command(\"voided\")\n .description(\"List voided purchases\")\n .option(\"--start-time <time>\", \"Start time (milliseconds)\")\n .option(\"--end-time <time>\", \"End time (milliseconds)\")\n .option(\"--max-results <n>\", \"Maximum results per page\", parseInt)\n .option(\"--limit <n>\", \"Maximum total results\", parseInt)\n .option(\"--next-page <token>\", \"Resume from page token\")\n .action(async (options) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts().app, config);\n const client = await getClient(config);\n const format = detectOutputFormat();\n\n try {\n const result = await listVoidedPurchases(client, packageName, {\n startTime: options.startTime,\n endTime: options.endTime,\n maxResults: options.maxResults,\n limit: options.limit,\n nextPage: options.nextPage,\n });\n console.log(formatOutput(result, format));\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n\n // --- Orders ---\n const orders = purchases\n .command(\"orders\")\n .description(\"Manage orders\");\n\n orders\n .command(\"refund <order-id>\")\n .description(\"Refund an order\")\n .option(\"--full-refund\", \"Full refund\")\n .option(\"--prorated-refund\", \"Prorated refund\")\n .action(async (orderId: string, options) => {\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts().app, config);\n\n await requireConfirm(`Refund order \"${orderId}\"?`, program);\n\n if (isDryRun(program)) {\n const format = detectOutputFormat();\n printDryRun({\n command: \"purchases orders refund\",\n action: \"refund\",\n target: orderId,\n details: { fullRefund: options.fullRefund, proratedRefund: options.proratedRefund },\n }, format, formatOutput);\n return;\n }\n\n const client = await getClient(config);\n\n try {\n await refundOrder(client, packageName, orderId, {\n fullRefund: options.fullRefund,\n proratedRefund: options.proratedRefund,\n });\n console.log(`Order ${orderId} refunded.`);\n } catch (error) {\n console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);\n process.exit(4);\n }\n });\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,231 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ isDryRun,
4
+ printDryRun
5
+ } from "./chunk-QMKZYXDJ.js";
6
+ import {
7
+ isInteractive,
8
+ promptInput,
9
+ promptSelect,
10
+ requireOption
11
+ } from "./chunk-4QV4WD3F.js";
12
+
13
+ // src/commands/releases.ts
14
+ import { appendFile } from "fs/promises";
15
+ import { loadConfig } from "@gpc-cli/config";
16
+ import { resolveAuth } from "@gpc-cli/auth";
17
+ import { createApiClient } from "@gpc-cli/api";
18
+ import { uploadRelease, getReleasesStatus, promoteRelease, updateRollout, readReleaseNotesFromDir, writeAuditLog, createAuditEntry } from "@gpc-cli/core";
19
+ import { detectOutputFormat, formatOutput } from "@gpc-cli/core";
20
+ function resolvePackageName(packageArg, config) {
21
+ const name = packageArg || config.app;
22
+ if (!name) {
23
+ console.error("Error: No package name. Use --app <package> or gpc config set app <package>");
24
+ process.exit(2);
25
+ }
26
+ return name;
27
+ }
28
+ function createRetryLogger(retryLogPath) {
29
+ if (!retryLogPath) return void 0;
30
+ return (entry) => {
31
+ const line = JSON.stringify(entry) + "\n";
32
+ appendFile(retryLogPath, line).catch(() => {
33
+ });
34
+ };
35
+ }
36
+ async function getClient(config, retryLogPath) {
37
+ const auth = await resolveAuth({ serviceAccountPath: config.auth?.serviceAccount });
38
+ return createApiClient({ auth, onRetry: createRetryLogger(retryLogPath) });
39
+ }
40
+ function registerReleasesCommands(program) {
41
+ const releases = program.command("releases").description("Manage releases and rollouts");
42
+ releases.command("upload <file>").description("Upload AAB/APK and assign to a track").option("--track <track>", "Target track", "internal").option("--rollout <percent>", "Staged rollout percentage (1-100)").option("--notes <text>", "Release notes (en-US)").option("--name <name>", "Release name").option("--mapping <file>", "ProGuard/R8 mapping file for deobfuscation").option("--notes-dir <dir>", "Read release notes from directory (<dir>/<lang>.txt)").option("--retry-log <path>", "Write retry log entries to file (JSONL)").action(async (file, options) => {
43
+ if (options.notes && options.notesDir) {
44
+ console.error("Error: Cannot use both --notes and --notes-dir");
45
+ process.exit(2);
46
+ }
47
+ const config = await loadConfig();
48
+ const packageName = resolvePackageName(program.opts().app, config);
49
+ const format = detectOutputFormat();
50
+ if (isInteractive(program)) {
51
+ if (!options.track || options.track === "internal") {
52
+ const tracks = ["internal", "alpha", "beta", "production"];
53
+ options.track = await promptSelect("Select track:", tracks, "internal");
54
+ }
55
+ if (!options.rollout && options.track === "production") {
56
+ const rolloutStr = await promptInput("Staged rollout percentage (1-100, blank for full)", "100");
57
+ if (rolloutStr && rolloutStr !== "100") {
58
+ options.rollout = rolloutStr;
59
+ }
60
+ }
61
+ if (!options.notes && !options.notesDir) {
62
+ const notes = await promptInput("Release notes (en-US, blank to skip)");
63
+ if (notes) options.notes = notes;
64
+ }
65
+ }
66
+ if (isDryRun(program)) {
67
+ printDryRun({
68
+ command: "releases upload",
69
+ action: "upload",
70
+ target: file,
71
+ details: { track: options.track, rollout: options.rollout }
72
+ }, format, formatOutput);
73
+ return;
74
+ }
75
+ const auditEntry = createAuditEntry("releases upload", {
76
+ file,
77
+ track: options.track,
78
+ rollout: options.rollout
79
+ }, packageName);
80
+ const client = await getClient(config, options.retryLog);
81
+ try {
82
+ let releaseNotes;
83
+ if (options.notesDir) {
84
+ releaseNotes = await readReleaseNotesFromDir(options.notesDir);
85
+ } else if (options.notes) {
86
+ releaseNotes = [{ language: "en-US", text: options.notes }];
87
+ }
88
+ const result = await uploadRelease(client, packageName, file, {
89
+ track: options.track,
90
+ userFraction: options.rollout ? Number(options.rollout) / 100 : void 0,
91
+ releaseNotes,
92
+ releaseName: options.name,
93
+ mappingFile: options.mapping
94
+ });
95
+ console.log(formatOutput(result, format));
96
+ auditEntry.success = true;
97
+ } catch (error) {
98
+ auditEntry.success = false;
99
+ auditEntry.error = error instanceof Error ? error.message : String(error);
100
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
101
+ process.exit(4);
102
+ } finally {
103
+ auditEntry.durationMs = Date.now() - new Date(auditEntry.timestamp).getTime();
104
+ writeAuditLog(auditEntry).catch(() => {
105
+ });
106
+ }
107
+ });
108
+ releases.command("status").description("Show current release status across tracks").option("--track <track>", "Filter by track").action(async (options) => {
109
+ const config = await loadConfig();
110
+ const packageName = resolvePackageName(program.opts().app, config);
111
+ const client = await getClient(config);
112
+ const format = detectOutputFormat();
113
+ try {
114
+ const statuses = await getReleasesStatus(client, packageName, options.track);
115
+ console.log(formatOutput(statuses, format));
116
+ } catch (error) {
117
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
118
+ process.exit(4);
119
+ }
120
+ });
121
+ releases.command("promote").description("Promote a release from one track to another").option("--from <track>", "Source track").option("--to <track>", "Target track").option("--rollout <percent>", "Staged rollout percentage").option("--notes <text>", "Release notes").action(async (options) => {
122
+ const config = await loadConfig();
123
+ const packageName = resolvePackageName(program.opts().app, config);
124
+ const format = detectOutputFormat();
125
+ const interactive = isInteractive(program);
126
+ const tracks = ["internal", "alpha", "beta", "production"];
127
+ options.from = await requireOption("from", options.from, {
128
+ message: "Source track:",
129
+ choices: tracks
130
+ }, interactive);
131
+ options.to = await requireOption("to", options.to, {
132
+ message: "Target track:",
133
+ choices: tracks.filter((t) => t !== options.from)
134
+ }, interactive);
135
+ if (isDryRun(program)) {
136
+ printDryRun({
137
+ command: "releases promote",
138
+ action: "promote",
139
+ target: `${options.from} \u2192 ${options.to}`,
140
+ details: { rollout: options.rollout }
141
+ }, format, formatOutput);
142
+ return;
143
+ }
144
+ const client = await getClient(config);
145
+ try {
146
+ const result = await promoteRelease(client, packageName, options.from, options.to, {
147
+ userFraction: options.rollout ? Number(options.rollout) / 100 : void 0,
148
+ releaseNotes: options.notes ? [{ language: "en-US", text: options.notes }] : void 0
149
+ });
150
+ console.log(formatOutput(result, format));
151
+ } catch (error) {
152
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
153
+ process.exit(4);
154
+ }
155
+ });
156
+ const rollout = releases.command("rollout").description("Manage staged rollouts");
157
+ for (const action of ["increase", "halt", "resume", "complete"]) {
158
+ const cmd = rollout.command(action).description(`${action.charAt(0).toUpperCase() + action.slice(1)} a staged rollout`).option("--track <track>", "Track name");
159
+ if (action === "increase") {
160
+ cmd.option("--to <percent>", "New rollout percentage");
161
+ }
162
+ cmd.action(async (options) => {
163
+ const config = await loadConfig();
164
+ const packageName = resolvePackageName(program.opts().app, config);
165
+ const format = detectOutputFormat();
166
+ const interactive = isInteractive(program);
167
+ const tracks = ["internal", "alpha", "beta", "production"];
168
+ options.track = await requireOption("track", options.track, {
169
+ message: "Track:",
170
+ choices: tracks
171
+ }, interactive);
172
+ if (action === "increase") {
173
+ options.to = await requireOption("to", options.to, {
174
+ message: "New rollout percentage (1-100):"
175
+ }, interactive);
176
+ }
177
+ if (isDryRun(program)) {
178
+ printDryRun({
179
+ command: `releases rollout ${action}`,
180
+ action,
181
+ target: options.track,
182
+ details: { percentage: options.to }
183
+ }, format, formatOutput);
184
+ return;
185
+ }
186
+ const client = await getClient(config);
187
+ try {
188
+ const result = await updateRollout(
189
+ client,
190
+ packageName,
191
+ options.track,
192
+ action,
193
+ options.to ? Number(options.to) / 100 : void 0
194
+ );
195
+ console.log(formatOutput(result, format));
196
+ } catch (error) {
197
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
198
+ process.exit(4);
199
+ }
200
+ });
201
+ }
202
+ releases.command("notes").description("Set release notes").argument("<action>", "Action: set").option("--track <track>", "Track name").option("--lang <language>", "Language code", "en-US").option("--notes <text>", "Release notes text").option("--file <path>", "Read notes from file").action(async (action, options) => {
203
+ if (action !== "set") {
204
+ console.error("Usage: gpc releases notes set --track <track> --notes <text>");
205
+ process.exit(2);
206
+ }
207
+ const interactive = isInteractive(program);
208
+ const tracks = ["internal", "alpha", "beta", "production"];
209
+ options.track = await requireOption("track", options.track, {
210
+ message: "Track:",
211
+ choices: tracks
212
+ }, interactive);
213
+ let notesText = options.notes;
214
+ if (options.file) {
215
+ const { readFile } = await import("fs/promises");
216
+ notesText = await readFile(options.file, "utf-8");
217
+ }
218
+ if (!notesText && interactive) {
219
+ notesText = await promptInput("Release notes text:");
220
+ }
221
+ if (!notesText) {
222
+ console.error("Provide --notes <text> or --file <path>");
223
+ process.exit(2);
224
+ }
225
+ console.log(`Release notes set for ${options.track} (${options.lang})`);
226
+ });
227
+ }
228
+ export {
229
+ registerReleasesCommands
230
+ };
231
+ //# sourceMappingURL=releases-464IMEEF.js.map