@gpc-cli/cli 0.9.44 → 0.9.46
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/dist/{anomalies-NU2IN2GJ.js → anomalies-UDE4NGHJ.js} +19 -24
- package/dist/anomalies-UDE4NGHJ.js.map +1 -0
- package/dist/{apps-J2446UDA.js → apps-FKD3ZG5X.js} +31 -35
- package/dist/apps-FKD3ZG5X.js.map +1 -0
- package/dist/{audit-N2CRHWUN.js → audit-JASSHRWN.js} +47 -62
- package/dist/audit-JASSHRWN.js.map +1 -0
- package/dist/{auth-XGSTT5G5.js → auth-OTA3SV3J.js} +145 -103
- package/dist/auth-OTA3SV3J.js.map +1 -0
- package/dist/bin.js +6 -4
- package/dist/bin.js.map +1 -1
- package/dist/bundle-F7MUVC5J.js +204 -0
- package/dist/bundle-F7MUVC5J.js.map +1 -0
- package/dist/{cache-SLNFRTI2.js → cache-XKPLZYEB.js} +4 -5
- package/dist/cache-XKPLZYEB.js.map +1 -0
- package/dist/changelog-7COFZO7Q.js +48 -0
- package/dist/changelog-7COFZO7Q.js.map +1 -0
- package/dist/{chunk-4O4D5SGL.js → chunk-3SJ6OXCZ.js} +4 -5
- package/dist/chunk-3SJ6OXCZ.js.map +1 -0
- package/dist/{chunk-7LURVNQV.js → chunk-6OWN6S6X.js} +53 -49
- package/dist/{chunk-7LURVNQV.js.map → chunk-6OWN6S6X.js.map} +1 -1
- package/dist/{chunk-U6ZTQ34I.js → chunk-BCBXQC7J.js} +45 -11
- package/dist/chunk-BCBXQC7J.js.map +1 -0
- package/dist/{chunk-AA577WVQ.js → chunk-NQH4G7BI.js} +9 -3
- package/dist/chunk-NQH4G7BI.js.map +1 -0
- package/dist/chunk-SLNJEAMK.js +23 -0
- package/dist/chunk-SLNJEAMK.js.map +1 -0
- package/dist/{chunk-NV75I5VP.js → chunk-YFUBD2XB.js} +10 -8
- package/dist/chunk-YFUBD2XB.js.map +1 -0
- package/dist/{config-222P3MKK.js → config-2FTCYEGD.js} +8 -5
- package/dist/config-2FTCYEGD.js.map +1 -0
- package/dist/{data-safety-Q7FTCEWU.js → data-safety-AFMD6MYI.js} +12 -27
- package/dist/data-safety-AFMD6MYI.js.map +1 -0
- package/dist/{device-tiers-MIOQEXYY.js → device-tiers-AQAMUQXI.js} +23 -38
- package/dist/device-tiers-AQAMUQXI.js.map +1 -0
- package/dist/diff-6EO4ID6W.js +91 -0
- package/dist/diff-6EO4ID6W.js.map +1 -0
- package/dist/{docs-7DUXIKA3.js → docs-4D2SJ4LY.js} +4 -3
- package/dist/docs-4D2SJ4LY.js.map +1 -0
- package/dist/doctor-H4X7Q57B.js +691 -0
- package/dist/doctor-H4X7Q57B.js.map +1 -0
- package/dist/{enterprise-7THXNBTC.js → enterprise-7PWXMSUN.js} +11 -21
- package/dist/enterprise-7PWXMSUN.js.map +1 -0
- package/dist/{external-transactions-2GWIMUVM.js → external-transactions-LCZALS3V.js} +12 -28
- package/dist/external-transactions-LCZALS3V.js.map +1 -0
- package/dist/{feedback-2W2XJGZX.js → feedback-XP765TOO.js} +4 -4
- package/dist/{games-BT777WUO.js → games-ZSNGEI7A.js} +17 -32
- package/dist/games-ZSNGEI7A.js.map +1 -0
- package/dist/{generated-apks-RJWTIX7L.js → generated-apks-RX2IUWSF.js} +30 -38
- package/dist/generated-apks-RX2IUWSF.js.map +1 -0
- package/dist/{grants-TKQJ3IER.js → grants-EBPECI26.js} +22 -40
- package/dist/grants-EBPECI26.js.map +1 -0
- package/dist/{iap-ICAEQLK5.js → iap-OUI5YYN4.js} +30 -51
- package/dist/iap-OUI5YYN4.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/{init-JZ2THPMS.js → init-WSTQTJOD.js} +5 -4
- package/dist/init-WSTQTJOD.js.map +1 -0
- package/dist/{install-skills-OV4HVANW.js → install-skills-6QDUXI5F.js} +5 -6
- package/dist/{install-skills-OV4HVANW.js.map → install-skills-6QDUXI5F.js.map} +1 -1
- package/dist/{internal-sharing-3U2XFHA4.js → internal-sharing-ONNIWIAT.js} +3 -4
- package/dist/{internal-sharing-3U2XFHA4.js.map → internal-sharing-ONNIWIAT.js.map} +1 -1
- package/dist/{listings-77HZW4S5.js → listings-7SGQ4SRX.js} +118 -157
- package/dist/listings-7SGQ4SRX.js.map +1 -0
- package/dist/migrate-ZQCJGQQS.js +138 -0
- package/dist/migrate-ZQCJGQQS.js.map +1 -0
- package/dist/{one-time-products-LHZAXQES.js → one-time-products-MGZTU7OM.js} +65 -120
- package/dist/one-time-products-MGZTU7OM.js.map +1 -0
- package/dist/{preflight-H3HEBYQW.js → preflight-N7ZRG2JI.js} +58 -55
- package/dist/preflight-N7ZRG2JI.js.map +1 -0
- package/dist/{pricing-XQSDTTK5.js → pricing-JJZFICFL.js} +8 -8
- package/dist/{pricing-XQSDTTK5.js.map → pricing-JJZFICFL.js.map} +1 -1
- package/dist/{prompt-BSV22CQZ.js → prompt-GXC2JSLA.js} +2 -2
- package/dist/{publish-Q5ZKEKZ5.js → publish-JPTI4EBT.js} +34 -30
- package/dist/publish-JPTI4EBT.js.map +1 -0
- package/dist/{purchase-options-CKRN4VIW.js → purchase-options-KFWW4JW2.js} +16 -11
- package/dist/purchase-options-KFWW4JW2.js.map +1 -0
- package/dist/purchases-DAWTMXP6.js +383 -0
- package/dist/purchases-DAWTMXP6.js.map +1 -0
- package/dist/{quickstart-4HB62YEL.js → quickstart-Z5Y3FYJU.js} +5 -3
- package/dist/quickstart-Z5Y3FYJU.js.map +1 -0
- package/dist/{quota-UHIQQYOY.js → quota-MZRWYJGR.js} +5 -15
- package/dist/quota-MZRWYJGR.js.map +1 -0
- package/dist/{recovery-5EV2R476.js → recovery-YE3Z7NIN.js} +32 -61
- package/dist/recovery-YE3Z7NIN.js.map +1 -0
- package/dist/{releases-C2WC2K4E.js → releases-2I3WBULC.js} +184 -185
- package/dist/releases-2I3WBULC.js.map +1 -0
- package/dist/{reports-2YX3RDOS.js → reports-CIB2T3XT.js} +19 -21
- package/dist/reports-CIB2T3XT.js.map +1 -0
- package/dist/reviews-BCCXIQ6C.js +188 -0
- package/dist/reviews-BCCXIQ6C.js.map +1 -0
- package/dist/{status-WHGLODGV.js → status-6LH5W4FU.js} +105 -83
- package/dist/status-6LH5W4FU.js.map +1 -0
- package/dist/{subscriptions-CI3JH3VQ.js → subscriptions-DZP3Y7O7.js} +142 -232
- package/dist/subscriptions-DZP3Y7O7.js.map +1 -0
- package/dist/{testers-NZOFA3EF.js → testers-LSMBXCA2.js} +24 -44
- package/dist/testers-LSMBXCA2.js.map +1 -0
- package/dist/tracks-YHMO2A6B.js +98 -0
- package/dist/tracks-YHMO2A6B.js.map +1 -0
- package/dist/{train-XKE4JN3Y.js → train-MDD2EBHS.js} +35 -55
- package/dist/train-MDD2EBHS.js.map +1 -0
- package/dist/{update-QMPRL5Y6.js → update-OMALGIBR.js} +30 -15
- package/dist/update-OMALGIBR.js.map +1 -0
- package/dist/{users-2YTC4Q36.js → users-UKG7VIQH.js} +45 -67
- package/dist/users-UKG7VIQH.js.map +1 -0
- package/dist/{validate-UOVTM6L3.js → validate-QIYSA3N7.js} +8 -10
- package/dist/validate-QIYSA3N7.js.map +1 -0
- package/dist/{version-NK5SJLHJ.js → version-NCSNXNVN.js} +4 -4
- package/dist/{vitals-A4CS4MSS.js → vitals-C23L2Y2E.js} +153 -172
- package/dist/vitals-C23L2Y2E.js.map +1 -0
- package/package.json +6 -6
- package/dist/anomalies-NU2IN2GJ.js.map +0 -1
- package/dist/apps-J2446UDA.js.map +0 -1
- package/dist/audit-N2CRHWUN.js.map +0 -1
- package/dist/auth-XGSTT5G5.js.map +0 -1
- package/dist/bundle-F43TD2BQ.js +0 -218
- package/dist/bundle-F43TD2BQ.js.map +0 -1
- package/dist/cache-SLNFRTI2.js.map +0 -1
- package/dist/changelog-OYUZOCOL.js +0 -53
- package/dist/changelog-OYUZOCOL.js.map +0 -1
- package/dist/chunk-4O4D5SGL.js.map +0 -1
- package/dist/chunk-AA577WVQ.js.map +0 -1
- package/dist/chunk-FWKYRLKY.js +0 -19
- package/dist/chunk-FWKYRLKY.js.map +0 -1
- package/dist/chunk-NV75I5VP.js.map +0 -1
- package/dist/chunk-U6ZTQ34I.js.map +0 -1
- package/dist/config-222P3MKK.js.map +0 -1
- package/dist/data-safety-Q7FTCEWU.js.map +0 -1
- package/dist/device-tiers-MIOQEXYY.js.map +0 -1
- package/dist/diff-V77SMKAQ.js +0 -96
- package/dist/diff-V77SMKAQ.js.map +0 -1
- package/dist/docs-7DUXIKA3.js.map +0 -1
- package/dist/doctor-3Z4ARPM2.js +0 -372
- package/dist/doctor-3Z4ARPM2.js.map +0 -1
- package/dist/enterprise-7THXNBTC.js.map +0 -1
- package/dist/external-transactions-2GWIMUVM.js.map +0 -1
- package/dist/games-BT777WUO.js.map +0 -1
- package/dist/generated-apks-RJWTIX7L.js.map +0 -1
- package/dist/grants-TKQJ3IER.js.map +0 -1
- package/dist/iap-ICAEQLK5.js.map +0 -1
- package/dist/init-JZ2THPMS.js.map +0 -1
- package/dist/listings-77HZW4S5.js.map +0 -1
- package/dist/migrate-SQT6RD6T.js +0 -143
- package/dist/migrate-SQT6RD6T.js.map +0 -1
- package/dist/one-time-products-LHZAXQES.js.map +0 -1
- package/dist/preflight-H3HEBYQW.js.map +0 -1
- package/dist/publish-Q5ZKEKZ5.js.map +0 -1
- package/dist/purchase-options-CKRN4VIW.js.map +0 -1
- package/dist/purchases-HSMCOG4A.js +0 -330
- package/dist/purchases-HSMCOG4A.js.map +0 -1
- package/dist/quickstart-4HB62YEL.js.map +0 -1
- package/dist/quota-UHIQQYOY.js.map +0 -1
- package/dist/recovery-5EV2R476.js.map +0 -1
- package/dist/releases-C2WC2K4E.js.map +0 -1
- package/dist/reports-2YX3RDOS.js.map +0 -1
- package/dist/reviews-2CWOI5CV.js +0 -213
- package/dist/reviews-2CWOI5CV.js.map +0 -1
- package/dist/status-WHGLODGV.js.map +0 -1
- package/dist/subscriptions-CI3JH3VQ.js.map +0 -1
- package/dist/testers-NZOFA3EF.js.map +0 -1
- package/dist/tracks-NERFFEDT.js +0 -107
- package/dist/tracks-NERFFEDT.js.map +0 -1
- package/dist/train-XKE4JN3Y.js.map +0 -1
- package/dist/update-QMPRL5Y6.js.map +0 -1
- package/dist/users-2YTC4Q36.js.map +0 -1
- package/dist/validate-UOVTM6L3.js.map +0 -1
- package/dist/vitals-A4CS4MSS.js.map +0 -1
- /package/dist/{feedback-2W2XJGZX.js.map → feedback-XP765TOO.js.map} +0 -0
- /package/dist/{prompt-BSV22CQZ.js.map → prompt-GXC2JSLA.js.map} +0 -0
- /package/dist/{version-NK5SJLHJ.js.map → version-NCSNXNVN.js.map} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
resolvePackageName
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-NQH4G7BI.js";
|
|
5
5
|
import {
|
|
6
6
|
isDryRun
|
|
7
7
|
} from "./chunk-Y3QZDAKS.js";
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
isInteractive,
|
|
13
13
|
promptInput,
|
|
14
14
|
promptSelect
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-YFUBD2XB.js";
|
|
16
16
|
|
|
17
17
|
// src/commands/publish.ts
|
|
18
18
|
import { appendFile, stat } from "fs/promises";
|
|
@@ -24,7 +24,8 @@ import {
|
|
|
24
24
|
generateNotesFromGit,
|
|
25
25
|
writeAuditLog,
|
|
26
26
|
createAuditEntry,
|
|
27
|
-
formatOutput
|
|
27
|
+
formatOutput,
|
|
28
|
+
GpcError
|
|
28
29
|
} from "@gpc-cli/core";
|
|
29
30
|
var PASS = "\u2713";
|
|
30
31
|
var FAIL = "\u2717";
|
|
@@ -83,15 +84,21 @@ function registerPublishCommand(program) {
|
|
|
83
84
|
try {
|
|
84
85
|
await stat(file);
|
|
85
86
|
} catch {
|
|
86
|
-
|
|
87
|
-
|
|
87
|
+
throw new GpcError(
|
|
88
|
+
`File not found: ${file}`,
|
|
89
|
+
"PUBLISH_USAGE_ERROR",
|
|
90
|
+
2,
|
|
91
|
+
"Check the file path and try again."
|
|
92
|
+
);
|
|
88
93
|
}
|
|
89
94
|
const noteSources = [options.notes, options.notesDir, options.notesFromGit].filter(Boolean);
|
|
90
95
|
if (noteSources.length > 1) {
|
|
91
|
-
|
|
92
|
-
"
|
|
96
|
+
throw new GpcError(
|
|
97
|
+
"Cannot combine --notes, --notes-dir, and --notes-from-git. Use only one.",
|
|
98
|
+
"PUBLISH_USAGE_ERROR",
|
|
99
|
+
2,
|
|
100
|
+
"Pick one release notes source."
|
|
93
101
|
);
|
|
94
|
-
process.exit(2);
|
|
95
102
|
}
|
|
96
103
|
const config = await loadConfig();
|
|
97
104
|
const packageName = resolvePackageName(program.opts()["app"], config);
|
|
@@ -118,10 +125,12 @@ function registerPublishCommand(program) {
|
|
|
118
125
|
if (options.rollout !== void 0) {
|
|
119
126
|
const rollout = Number(options.rollout);
|
|
120
127
|
if (!Number.isFinite(rollout) || rollout < 1 || rollout > 100) {
|
|
121
|
-
|
|
122
|
-
|
|
128
|
+
throw new GpcError(
|
|
129
|
+
`--rollout must be a number between 1 and 100 (got: ${options.rollout})`,
|
|
130
|
+
"PUBLISH_USAGE_ERROR",
|
|
131
|
+
2,
|
|
132
|
+
"Use a percentage between 1 and 100."
|
|
123
133
|
);
|
|
124
|
-
process.exit(2);
|
|
125
134
|
}
|
|
126
135
|
}
|
|
127
136
|
if (options.notesFromGit) {
|
|
@@ -146,21 +155,16 @@ function registerPublishCommand(program) {
|
|
|
146
155
|
});
|
|
147
156
|
const client = createApiClient({ auth, onRetry });
|
|
148
157
|
if (isDryRun(program)) {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
console.log(formatDryRunOutput(result, format));
|
|
160
|
-
} catch (error) {
|
|
161
|
-
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
162
|
-
process.exit(4);
|
|
163
|
-
}
|
|
158
|
+
const result = await publish(client, packageName, file, {
|
|
159
|
+
track: options.track,
|
|
160
|
+
rolloutPercent: options.rollout ? Number(options.rollout) : void 0,
|
|
161
|
+
notes: options.notes,
|
|
162
|
+
notesDir: options.notesDir,
|
|
163
|
+
releaseName: options.name,
|
|
164
|
+
mappingFile: options.mapping,
|
|
165
|
+
dryRun: true
|
|
166
|
+
});
|
|
167
|
+
console.log(formatDryRunOutput(result, format));
|
|
164
168
|
return;
|
|
165
169
|
}
|
|
166
170
|
const auditEntry = createAuditEntry(
|
|
@@ -181,15 +185,15 @@ function registerPublishCommand(program) {
|
|
|
181
185
|
console.log(formatValidationOutput(result, format));
|
|
182
186
|
auditEntry.success = false;
|
|
183
187
|
auditEntry.error = "Validation failed";
|
|
184
|
-
process.
|
|
188
|
+
process.exitCode = 1;
|
|
189
|
+
return;
|
|
185
190
|
}
|
|
186
191
|
console.log(formatPublishOutput(result, format));
|
|
187
192
|
auditEntry.success = true;
|
|
188
193
|
} catch (error) {
|
|
189
194
|
auditEntry.success = false;
|
|
190
195
|
auditEntry.error = error instanceof Error ? error.message : String(error);
|
|
191
|
-
|
|
192
|
-
process.exit(4);
|
|
196
|
+
throw error;
|
|
193
197
|
} finally {
|
|
194
198
|
auditEntry.durationMs = Date.now() - new Date(auditEntry.timestamp).getTime();
|
|
195
199
|
writeAuditLog(auditEntry).catch(() => {
|
|
@@ -200,4 +204,4 @@ function registerPublishCommand(program) {
|
|
|
200
204
|
export {
|
|
201
205
|
registerPublishCommand
|
|
202
206
|
};
|
|
203
|
-
//# sourceMappingURL=publish-
|
|
207
|
+
//# sourceMappingURL=publish-JPTI4EBT.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/publish.ts"],"sourcesContent":["import { resolvePackageName } from \"../resolve.js\";\nimport { appendFile, stat } from \"node:fs/promises\";\nimport type { OutputFormat } from \"@gpc-cli/config\";\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 {\n publish,\n generateNotesFromGit,\n writeAuditLog,\n createAuditEntry,\n formatOutput,\n GpcError,\n} from \"@gpc-cli/core\";\nimport type { PublishResult, DryRunPublishResult } from \"@gpc-cli/core\";\nimport { getOutputFormat } from \"../format.js\";\nimport { isDryRun } from \"../dry-run.js\";\nimport { isInteractive, promptSelect, promptInput } from \"../prompt.js\";\n\nconst PASS = \"\\u2713\";\nconst FAIL = \"\\u2717\";\nconst WARN = \"\\u26A0\";\n\n\n// ---------------------------------------------------------------------------\n// Output formatters\n// ---------------------------------------------------------------------------\n\nfunction formatChecks(checks: { name: string; passed: boolean; message: string }[]): string[] {\n return checks.map((c) => ` ${c.passed ? PASS : FAIL} ${c.message}`);\n}\n\nfunction formatValidationOutput(result: PublishResult, format: OutputFormat): string {\n if (format !== \"table\") {\n return formatOutput({ success: false, validation: result.validation }, format);\n }\n const lines = [\"Validation failed:\\n\", ...formatChecks(result.validation.checks)];\n for (const w of result.validation.warnings) {\n lines.push(` ${WARN} ${w}`);\n }\n return lines.join(\"\\n\");\n}\n\nfunction formatPublishOutput(result: PublishResult, format: OutputFormat): string {\n if (format !== \"table\") return formatOutput(result, format);\n\n const upload = result.upload;\n if (!upload) return formatOutput(result, format);\n const rollout =\n upload.status === \"inProgress\" && \"userFraction\" in upload\n ? ` (${Math.round(Number((upload as Record<string, unknown>)[\"userFraction\"]) * 100)}% rollout)`\n : \"\";\n\n const lines = [\"Published successfully\\n\", ...formatChecks(result.validation.checks)];\n for (const w of result.validation.warnings) {\n lines.push(` ${WARN} ${w}`);\n }\n lines.push(\"\");\n lines.push(` versionCode ${upload.versionCode}`);\n lines.push(` track ${upload.track}`);\n lines.push(` status ${upload.status}${rollout}`);\n return lines.join(\"\\n\");\n}\n\nfunction formatDryRunOutput(result: DryRunPublishResult, format: OutputFormat): string {\n if (format !== \"table\") return formatOutput(result, format);\n\n const lines = [\"Dry run — no changes made\\n\", ...formatChecks(result.validation.checks)];\n for (const w of result.validation.warnings) {\n lines.push(` ${WARN} ${w}`);\n }\n\n const u = result.upload;\n lines.push(\"\");\n lines.push(` Track ${u.track}`);\n\n if (u.currentReleases.length === 0) {\n lines.push(` Current (no releases on this track)`);\n } else {\n for (const r of u.currentReleases) {\n const fraction =\n r.userFraction !== undefined ? ` (${Math.round(r.userFraction * 100)}%)` : \"\";\n lines.push(` Current ${r.versionCodes.join(\", \")} · ${r.status}${fraction}`);\n }\n }\n const plannedFraction =\n u.plannedRelease.userFraction !== undefined\n ? ` (${Math.round(u.plannedRelease.userFraction * 100)}%)`\n : \"\";\n lines.push(` Would be (new bundle) · ${u.plannedRelease.status}${plannedFraction}`);\n return lines.join(\"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// Command\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(\"--notes-from-git\", \"Generate release notes from git commit history\")\n .option(\"--since <ref>\", \"Git ref to start from (tag, SHA) — used with --notes-from-git\")\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 try {\n await stat(file);\n } catch {\n throw new GpcError(\n `File not found: ${file}`,\n \"PUBLISH_USAGE_ERROR\",\n 2,\n \"Check the file path and try again.\",\n );\n }\n\n const noteSources = [options.notes, options.notesDir, options.notesFromGit].filter(Boolean);\n if (noteSources.length > 1) {\n throw new GpcError(\n \"Cannot combine --notes, --notes-dir, and --notes-from-git. Use only one.\",\n \"PUBLISH_USAGE_ERROR\",\n 2,\n \"Pick one release notes source.\",\n );\n }\n\n const config = await loadConfig();\n const packageName = resolvePackageName(program.opts()[\"app\"], config);\n const format = getOutputFormat(program, config);\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(\n \"Staged rollout percentage (1-100, blank for full)\",\n \"100\",\n );\n if (rolloutStr && rolloutStr !== \"100\") {\n options.rollout = rolloutStr;\n }\n }\n\n if (!options.notes && !options.notesDir && !options.notesFromGit) {\n const notes = await promptInput(\"Release notes (en-US, blank to skip)\");\n if (notes) options.notes = notes;\n }\n }\n\n // Rollout range guard\n if (options.rollout !== undefined) {\n const rollout = Number(options.rollout);\n if (!Number.isFinite(rollout) || rollout < 1 || rollout > 100) {\n throw new GpcError(\n `--rollout must be a number between 1 and 100 (got: ${options.rollout})`,\n \"PUBLISH_USAGE_ERROR\",\n 2,\n \"Use a percentage between 1 and 100.\",\n );\n }\n }\n\n // Resolve git-based release notes before calling publish\n if (options.notesFromGit) {\n const gitNotes = await generateNotesFromGit({ since: options.since });\n options.notes = gitNotes.text;\n if (gitNotes.truncated) {\n console.error(\n `${WARN} Release notes truncated to 500 characters (${gitNotes.commitCount} commits from ${gitNotes.since}).`,\n );\n }\n }\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 if (isDryRun(program)) {\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 dryRun: true,\n });\n console.log(formatDryRunOutput(result as DryRunPublishResult, format));\n return;\n }\n\n const auditEntry = createAuditEntry(\n \"publish\",\n { file, track: options.track, rollout: options.rollout },\n packageName,\n );\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.log(formatValidationOutput(result as PublishResult, format));\n auditEntry.success = false;\n auditEntry.error = \"Validation failed\";\n process.exitCode = 1;\n return;\n }\n\n console.log(formatPublishOutput(result as PublishResult, format));\n auditEntry.success = true;\n } catch (error) {\n auditEntry.success = false;\n auditEntry.error = error instanceof Error ? error.message : String(error);\n throw error;\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":[]}
|
|
@@ -14,27 +14,32 @@ See: gpc otp offers --help`;
|
|
|
14
14
|
function registerPurchaseOptionsCommands(program) {
|
|
15
15
|
const po = program.command("purchase-options").description("Manage purchase options (use 'otp offers' commands)");
|
|
16
16
|
po.command("list").description("List purchase options").option("--sort <field>", "Sort by field").action(async () => {
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
const err = new Error(REDIRECT_MESSAGE);
|
|
18
|
+
Object.assign(err, { code: "USAGE_ERROR", exitCode: 2 });
|
|
19
|
+
throw err;
|
|
19
20
|
});
|
|
20
21
|
po.command("get <id>").description("Get a purchase option").action(async () => {
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
const err = new Error(REDIRECT_MESSAGE);
|
|
23
|
+
Object.assign(err, { code: "USAGE_ERROR", exitCode: 2 });
|
|
24
|
+
throw err;
|
|
23
25
|
});
|
|
24
26
|
po.command("create").description("Create a purchase option").option("--file <path>", "JSON file with purchase option data").action(async () => {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
const err = new Error(REDIRECT_MESSAGE);
|
|
28
|
+
Object.assign(err, { code: "USAGE_ERROR", exitCode: 2 });
|
|
29
|
+
throw err;
|
|
27
30
|
});
|
|
28
31
|
po.command("activate <id>").description("Activate a purchase option").action(async () => {
|
|
29
|
-
|
|
30
|
-
|
|
32
|
+
const err = new Error(REDIRECT_MESSAGE);
|
|
33
|
+
Object.assign(err, { code: "USAGE_ERROR", exitCode: 2 });
|
|
34
|
+
throw err;
|
|
31
35
|
});
|
|
32
36
|
po.command("deactivate <id>").description("Deactivate a purchase option").action(async () => {
|
|
33
|
-
|
|
34
|
-
|
|
37
|
+
const err = new Error(REDIRECT_MESSAGE);
|
|
38
|
+
Object.assign(err, { code: "USAGE_ERROR", exitCode: 2 });
|
|
39
|
+
throw err;
|
|
35
40
|
});
|
|
36
41
|
}
|
|
37
42
|
export {
|
|
38
43
|
registerPurchaseOptionsCommands
|
|
39
44
|
};
|
|
40
|
-
//# sourceMappingURL=purchase-options-
|
|
45
|
+
//# sourceMappingURL=purchase-options-KFWW4JW2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/purchase-options.ts"],"sourcesContent":["import type { Command } from \"commander\";\n\nconst REDIRECT_MESSAGE = `Purchase options are managed through one-time product offers.\n\nUse the following commands instead:\n gpc otp offers list <product-id> List purchase options for a product\n gpc otp offers get <product-id> <id> Get a specific purchase option\n gpc otp offers create <product-id> Create a purchase option\n gpc otp offers activate <product-id> <id>\n gpc otp offers deactivate <product-id> <id>\n\nSee: gpc otp offers --help`;\n\nexport function registerPurchaseOptionsCommands(program: Command): void {\n const po = program\n .command(\"purchase-options\")\n .description(\"Manage purchase options (use 'otp offers' commands)\");\n\n po.command(\"list\")\n .description(\"List purchase options\")\n .option(\"--sort <field>\", \"Sort by field\")\n .action(async () => {\n const err = new Error(REDIRECT_MESSAGE);\n Object.assign(err, { code: \"USAGE_ERROR\", exitCode: 2 });\n throw err;\n });\n\n po.command(\"get <id>\")\n .description(\"Get a purchase option\")\n .action(async () => {\n const err = new Error(REDIRECT_MESSAGE);\n Object.assign(err, { code: \"USAGE_ERROR\", exitCode: 2 });\n throw err;\n });\n\n po.command(\"create\")\n .description(\"Create a purchase option\")\n .option(\"--file <path>\", \"JSON file with purchase option data\")\n .action(async () => {\n const err = new Error(REDIRECT_MESSAGE);\n Object.assign(err, { code: \"USAGE_ERROR\", exitCode: 2 });\n throw err;\n });\n\n po.command(\"activate <id>\")\n .description(\"Activate a purchase option\")\n .action(async () => {\n const err = new Error(REDIRECT_MESSAGE);\n Object.assign(err, { code: \"USAGE_ERROR\", exitCode: 2 });\n throw err;\n });\n\n po.command(\"deactivate <id>\")\n .description(\"Deactivate a purchase option\")\n .action(async () => {\n const err = new Error(REDIRECT_MESSAGE);\n Object.assign(err, { code: \"USAGE_ERROR\", exitCode: 2 });\n throw err;\n });\n}\n"],"mappings":";;;AAEA,IAAM,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAWlB,SAAS,gCAAgC,SAAwB;AACtE,QAAM,KAAK,QACR,QAAQ,kBAAkB,EAC1B,YAAY,qDAAqD;AAEpE,KAAG,QAAQ,MAAM,EACd,YAAY,uBAAuB,EACnC,OAAO,kBAAkB,eAAe,EACxC,OAAO,YAAY;AAClB,UAAM,MAAM,IAAI,MAAM,gBAAgB;AACtC,WAAO,OAAO,KAAK,EAAE,MAAM,eAAe,UAAU,EAAE,CAAC;AACvD,UAAM;AAAA,EACR,CAAC;AAEH,KAAG,QAAQ,UAAU,EAClB,YAAY,uBAAuB,EACnC,OAAO,YAAY;AAClB,UAAM,MAAM,IAAI,MAAM,gBAAgB;AACtC,WAAO,OAAO,KAAK,EAAE,MAAM,eAAe,UAAU,EAAE,CAAC;AACvD,UAAM;AAAA,EACR,CAAC;AAEH,KAAG,QAAQ,QAAQ,EAChB,YAAY,0BAA0B,EACtC,OAAO,iBAAiB,qCAAqC,EAC7D,OAAO,YAAY;AAClB,UAAM,MAAM,IAAI,MAAM,gBAAgB;AACtC,WAAO,OAAO,KAAK,EAAE,MAAM,eAAe,UAAU,EAAE,CAAC;AACvD,UAAM;AAAA,EACR,CAAC;AAEH,KAAG,QAAQ,eAAe,EACvB,YAAY,4BAA4B,EACxC,OAAO,YAAY;AAClB,UAAM,MAAM,IAAI,MAAM,gBAAgB;AACtC,WAAO,OAAO,KAAK,EAAE,MAAM,eAAe,UAAU,EAAE,CAAC;AACvD,UAAM;AAAA,EACR,CAAC;AAEH,KAAG,QAAQ,iBAAiB,EACzB,YAAY,8BAA8B,EAC1C,OAAO,YAAY;AAClB,UAAM,MAAM,IAAI,MAAM,gBAAgB;AACtC,WAAO,OAAO,KAAK,EAAE,MAAM,eAAe,UAAU,EAAE,CAAC;AACvD,UAAM;AAAA,EACR,CAAC;AACL;","names":[]}
|
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
getClient,
|
|
4
|
+
resolvePackageName
|
|
5
|
+
} from "./chunk-NQH4G7BI.js";
|
|
6
|
+
import {
|
|
7
|
+
isDryRun,
|
|
8
|
+
printDryRun
|
|
9
|
+
} from "./chunk-Y3QZDAKS.js";
|
|
10
|
+
import {
|
|
11
|
+
getOutputFormat
|
|
12
|
+
} from "./chunk-ELXAK7GI.js";
|
|
13
|
+
import {
|
|
14
|
+
isInteractive,
|
|
15
|
+
requireConfirm,
|
|
16
|
+
requireOption
|
|
17
|
+
} from "./chunk-YFUBD2XB.js";
|
|
18
|
+
|
|
19
|
+
// src/commands/purchases.ts
|
|
20
|
+
import { Option } from "commander";
|
|
21
|
+
import { loadConfig } from "@gpc-cli/config";
|
|
22
|
+
import {
|
|
23
|
+
getProductPurchase,
|
|
24
|
+
getProductPurchaseV2,
|
|
25
|
+
acknowledgeProductPurchase,
|
|
26
|
+
consumeProductPurchase,
|
|
27
|
+
getSubscriptionPurchase,
|
|
28
|
+
cancelSubscriptionPurchase,
|
|
29
|
+
cancelSubscriptionV2,
|
|
30
|
+
deferSubscriptionPurchase,
|
|
31
|
+
deferSubscriptionV2,
|
|
32
|
+
revokeSubscriptionPurchase,
|
|
33
|
+
refundSubscriptionV2,
|
|
34
|
+
listVoidedPurchases,
|
|
35
|
+
refundOrder,
|
|
36
|
+
getOrderDetails,
|
|
37
|
+
batchGetOrders,
|
|
38
|
+
formatOutput
|
|
39
|
+
} from "@gpc-cli/core";
|
|
40
|
+
function registerPurchasesCommands(program) {
|
|
41
|
+
const purchases = program.command("purchases").description("Manage purchases and orders");
|
|
42
|
+
purchases.command("get <product-id> <token>").description("Get a product purchase").action(async (productId, token) => {
|
|
43
|
+
const config = await loadConfig();
|
|
44
|
+
const packageName = resolvePackageName(program.opts()["app"], config);
|
|
45
|
+
const client = await getClient(config);
|
|
46
|
+
const format = getOutputFormat(program, config);
|
|
47
|
+
const result = await getProductPurchase(client, packageName, productId, token);
|
|
48
|
+
if (format !== "json") {
|
|
49
|
+
const r = result;
|
|
50
|
+
const row = {
|
|
51
|
+
orderId: r["orderId"] || "-",
|
|
52
|
+
purchaseState: r["purchaseState"] ?? "-",
|
|
53
|
+
consumptionState: r["consumptionState"] ?? "-",
|
|
54
|
+
purchaseTime: r["purchaseTimeMillis"] ? new Date(Number(r["purchaseTimeMillis"])).toISOString() : "-",
|
|
55
|
+
acknowledged: r["acknowledgementState"] ?? "-"
|
|
56
|
+
};
|
|
57
|
+
console.log(formatOutput(row, format));
|
|
58
|
+
} else {
|
|
59
|
+
console.log(formatOutput(result, format));
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
purchases.command("acknowledge <product-id> <token>").description("Acknowledge a product purchase").option("--payload <text>", "Developer payload").action(async (productId, token, options) => {
|
|
63
|
+
const config = await loadConfig();
|
|
64
|
+
const packageName = resolvePackageName(program.opts()["app"], config);
|
|
65
|
+
if (isDryRun(program)) {
|
|
66
|
+
const format = getOutputFormat(program, config);
|
|
67
|
+
printDryRun(
|
|
68
|
+
{
|
|
69
|
+
command: "purchases acknowledge",
|
|
70
|
+
action: "acknowledge",
|
|
71
|
+
target: `${productId}/${token}`,
|
|
72
|
+
details: { payload: options.payload }
|
|
73
|
+
},
|
|
74
|
+
format,
|
|
75
|
+
formatOutput
|
|
76
|
+
);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const client = await getClient(config);
|
|
80
|
+
await acknowledgeProductPurchase(client, packageName, productId, token, options.payload);
|
|
81
|
+
console.log(`Purchase acknowledged.`);
|
|
82
|
+
});
|
|
83
|
+
purchases.command("consume <product-id> <token>").description("Consume a product purchase").action(async (productId, token) => {
|
|
84
|
+
const config = await loadConfig();
|
|
85
|
+
const packageName = resolvePackageName(program.opts()["app"], config);
|
|
86
|
+
if (isDryRun(program)) {
|
|
87
|
+
const format = getOutputFormat(program, config);
|
|
88
|
+
printDryRun(
|
|
89
|
+
{
|
|
90
|
+
command: "purchases consume",
|
|
91
|
+
action: "consume",
|
|
92
|
+
target: `${productId}/${token}`
|
|
93
|
+
},
|
|
94
|
+
format,
|
|
95
|
+
formatOutput
|
|
96
|
+
);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const client = await getClient(config);
|
|
100
|
+
await consumeProductPurchase(client, packageName, productId, token);
|
|
101
|
+
console.log(`Purchase consumed.`);
|
|
102
|
+
});
|
|
103
|
+
const sub = purchases.command("subscription").description("Manage subscription purchases");
|
|
104
|
+
sub.command("get <token>").description("Get a subscription purchase (v2)").action(async (token) => {
|
|
105
|
+
const config = await loadConfig();
|
|
106
|
+
const packageName = resolvePackageName(program.opts()["app"], config);
|
|
107
|
+
const client = await getClient(config);
|
|
108
|
+
const format = getOutputFormat(program, config);
|
|
109
|
+
const result = await getSubscriptionPurchase(client, packageName, token);
|
|
110
|
+
if (format !== "json") {
|
|
111
|
+
const r = result;
|
|
112
|
+
const lineItems = r["lineItems"];
|
|
113
|
+
const row = {
|
|
114
|
+
subscriptionState: r["subscriptionState"] || "-",
|
|
115
|
+
startTime: r["startTime"] || "-",
|
|
116
|
+
expiryTime: r["expiryTime"] || "-",
|
|
117
|
+
linkedPurchaseToken: r["linkedPurchaseToken"] ? "yes" : "no",
|
|
118
|
+
lineItems: lineItems?.length || 0,
|
|
119
|
+
acknowledgement: r["acknowledgementState"] || "-"
|
|
120
|
+
};
|
|
121
|
+
console.log(formatOutput(row, format));
|
|
122
|
+
} else {
|
|
123
|
+
console.log(formatOutput(result, format));
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
sub.command("cancel <subscription-id> <token>").description("Cancel a subscription (v1)").action(async (subscriptionId, token) => {
|
|
127
|
+
const config = await loadConfig();
|
|
128
|
+
const packageName = resolvePackageName(program.opts()["app"], config);
|
|
129
|
+
if (isDryRun(program)) {
|
|
130
|
+
const format = getOutputFormat(program, config);
|
|
131
|
+
printDryRun(
|
|
132
|
+
{
|
|
133
|
+
command: "purchases subscription cancel",
|
|
134
|
+
action: "cancel subscription",
|
|
135
|
+
target: `${subscriptionId}/${token}`
|
|
136
|
+
},
|
|
137
|
+
format,
|
|
138
|
+
formatOutput
|
|
139
|
+
);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
const client = await getClient(config);
|
|
143
|
+
await cancelSubscriptionPurchase(client, packageName, subscriptionId, token);
|
|
144
|
+
console.log(`Subscription cancelled.`);
|
|
145
|
+
});
|
|
146
|
+
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) => {
|
|
147
|
+
const config = await loadConfig();
|
|
148
|
+
const packageName = resolvePackageName(program.opts()["app"], config);
|
|
149
|
+
const format = getOutputFormat(program, config);
|
|
150
|
+
const interactive = isInteractive(program);
|
|
151
|
+
options.expiry = await requireOption(
|
|
152
|
+
"expiry",
|
|
153
|
+
options.expiry,
|
|
154
|
+
{
|
|
155
|
+
message: "New expiry date (ISO 8601, e.g. 2026-12-31T23:59:59Z):"
|
|
156
|
+
},
|
|
157
|
+
interactive
|
|
158
|
+
);
|
|
159
|
+
if (isDryRun(program)) {
|
|
160
|
+
printDryRun(
|
|
161
|
+
{
|
|
162
|
+
command: "purchases subscription defer",
|
|
163
|
+
action: "defer subscription",
|
|
164
|
+
target: `${subscriptionId}/${token}`,
|
|
165
|
+
details: { expiry: options.expiry }
|
|
166
|
+
},
|
|
167
|
+
format,
|
|
168
|
+
formatOutput
|
|
169
|
+
);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const client = await getClient(config);
|
|
173
|
+
const result = await deferSubscriptionPurchase(
|
|
174
|
+
client,
|
|
175
|
+
packageName,
|
|
176
|
+
subscriptionId,
|
|
177
|
+
token,
|
|
178
|
+
options.expiry
|
|
179
|
+
);
|
|
180
|
+
console.log(formatOutput(result, format));
|
|
181
|
+
});
|
|
182
|
+
sub.command("refund <token>").description("Refund a subscription purchase (v2)").action(async (token) => {
|
|
183
|
+
const config = await loadConfig();
|
|
184
|
+
const packageName = resolvePackageName(program.opts()["app"], config);
|
|
185
|
+
await requireConfirm(`Refund subscription for token "${token.slice(0, 16)}..."?`, program);
|
|
186
|
+
if (isDryRun(program)) {
|
|
187
|
+
const format = getOutputFormat(program, config);
|
|
188
|
+
printDryRun(
|
|
189
|
+
{
|
|
190
|
+
command: "purchases subscription refund",
|
|
191
|
+
action: "refund subscription",
|
|
192
|
+
target: token
|
|
193
|
+
},
|
|
194
|
+
format,
|
|
195
|
+
formatOutput
|
|
196
|
+
);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
const client = await getClient(config);
|
|
200
|
+
await refundSubscriptionV2(client, packageName, token);
|
|
201
|
+
console.log(`Subscription refunded.`);
|
|
202
|
+
});
|
|
203
|
+
sub.command("revoke <token>").description("Revoke a subscription (v2)").action(async (token) => {
|
|
204
|
+
const config = await loadConfig();
|
|
205
|
+
const packageName = resolvePackageName(program.opts()["app"], config);
|
|
206
|
+
if (isDryRun(program)) {
|
|
207
|
+
const format = getOutputFormat(program, config);
|
|
208
|
+
printDryRun(
|
|
209
|
+
{
|
|
210
|
+
command: "purchases subscription revoke",
|
|
211
|
+
action: "revoke subscription",
|
|
212
|
+
target: token
|
|
213
|
+
},
|
|
214
|
+
format,
|
|
215
|
+
formatOutput
|
|
216
|
+
);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
const client = await getClient(config);
|
|
220
|
+
await revokeSubscriptionPurchase(client, packageName, token);
|
|
221
|
+
console.log(`Subscription revoked.`);
|
|
222
|
+
});
|
|
223
|
+
purchases.command("voided").description("List voided purchases").option("--start-time <time>", "Start time (milliseconds)").option("--end-time <time>", "End time (milliseconds)").addOption(
|
|
224
|
+
new Option("--max-results <n>", "Maximum results per page").argParser(parseInt).hideHelp()
|
|
225
|
+
).option("--limit <n>", "Maximum total results", parseInt).option("--next-page <token>", "Resume from page token").action(async (options) => {
|
|
226
|
+
const config = await loadConfig();
|
|
227
|
+
const packageName = resolvePackageName(program.opts()["app"], config);
|
|
228
|
+
const client = await getClient(config);
|
|
229
|
+
const format = getOutputFormat(program, config);
|
|
230
|
+
const result = await listVoidedPurchases(client, packageName, {
|
|
231
|
+
startTime: options.startTime,
|
|
232
|
+
endTime: options.endTime,
|
|
233
|
+
maxResults: options.maxResults,
|
|
234
|
+
limit: options.limit,
|
|
235
|
+
nextPage: options.nextPage
|
|
236
|
+
});
|
|
237
|
+
if (format !== "json") {
|
|
238
|
+
const purchases2 = result["voidedPurchases"];
|
|
239
|
+
if (purchases2 && purchases2.length > 0) {
|
|
240
|
+
const rows = purchases2.map((p) => ({
|
|
241
|
+
orderId: p["orderId"] || "-",
|
|
242
|
+
purchaseToken: String(p["purchaseToken"] || "-").slice(0, 16) + "...",
|
|
243
|
+
voidedTime: p["voidedTimeMillis"] ? new Date(Number(p["voidedTimeMillis"])).toISOString() : "-",
|
|
244
|
+
voidedSource: p["voidedSource"] ?? "-",
|
|
245
|
+
voidedReason: p["voidedReason"] ?? "-"
|
|
246
|
+
}));
|
|
247
|
+
console.log(formatOutput(rows, format));
|
|
248
|
+
} else {
|
|
249
|
+
console.log("No voided purchases found.");
|
|
250
|
+
}
|
|
251
|
+
} else {
|
|
252
|
+
console.log(formatOutput(result, format));
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
const orders = purchases.command("orders").description("Manage orders");
|
|
256
|
+
orders.command("refund <order-id>").description("Refund an order").option("--full-refund", "Full refund").option("--prorated-refund", "Prorated refund").action(async (orderId, options) => {
|
|
257
|
+
const config = await loadConfig();
|
|
258
|
+
const packageName = resolvePackageName(program.opts()["app"], config);
|
|
259
|
+
await requireConfirm(`Refund order "${orderId}"?`, program);
|
|
260
|
+
if (isDryRun(program)) {
|
|
261
|
+
const format = getOutputFormat(program, config);
|
|
262
|
+
printDryRun(
|
|
263
|
+
{
|
|
264
|
+
command: "purchases orders refund",
|
|
265
|
+
action: "refund",
|
|
266
|
+
target: orderId,
|
|
267
|
+
details: { fullRefund: options.fullRefund, proratedRefund: options.proratedRefund }
|
|
268
|
+
},
|
|
269
|
+
format,
|
|
270
|
+
formatOutput
|
|
271
|
+
);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
const client = await getClient(config);
|
|
275
|
+
await refundOrder(client, packageName, orderId, {
|
|
276
|
+
fullRefund: options.fullRefund,
|
|
277
|
+
proratedRefund: options.proratedRefund
|
|
278
|
+
});
|
|
279
|
+
console.log(`Order ${orderId} refunded.`);
|
|
280
|
+
});
|
|
281
|
+
orders.command("get <order-id>").description("Get order details").action(async (orderId) => {
|
|
282
|
+
const config = await loadConfig();
|
|
283
|
+
const packageName = resolvePackageName(program.opts()["app"], config);
|
|
284
|
+
const client = await getClient(config);
|
|
285
|
+
const format = getOutputFormat(program, config);
|
|
286
|
+
const result = await getOrderDetails(client, packageName, orderId);
|
|
287
|
+
if (format !== "json") {
|
|
288
|
+
const row = {
|
|
289
|
+
orderId: result.orderId,
|
|
290
|
+
state: result.state,
|
|
291
|
+
purchaseToken: result.purchaseToken ? result.purchaseToken.slice(0, 16) + "..." : "-",
|
|
292
|
+
createTime: result.createTime || "-",
|
|
293
|
+
total: result.total ? `${result.total.units || "0"}.${String(result.total.nanos || 0).padStart(9, "0").slice(0, 2)} ${result.total.currencyCode}` : "-",
|
|
294
|
+
lineItems: result.lineItems?.length || 0
|
|
295
|
+
};
|
|
296
|
+
console.log(formatOutput(row, format));
|
|
297
|
+
} else {
|
|
298
|
+
console.log(formatOutput(result, format));
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
orders.command("batch-get").description("Get multiple orders at once").requiredOption("--ids <order-ids>", "Comma-separated order IDs (max 1000)").action(async (options) => {
|
|
302
|
+
const config = await loadConfig();
|
|
303
|
+
const packageName = resolvePackageName(program.opts()["app"], config);
|
|
304
|
+
const client = await getClient(config);
|
|
305
|
+
const format = getOutputFormat(program, config);
|
|
306
|
+
const orderIds = options.ids.split(",").map((id) => id.trim()).filter(Boolean);
|
|
307
|
+
const result = await batchGetOrders(client, packageName, orderIds);
|
|
308
|
+
if (format !== "json") {
|
|
309
|
+
if (result.length === 0) {
|
|
310
|
+
console.log("No orders found.");
|
|
311
|
+
} else {
|
|
312
|
+
const rows = result.map((o) => ({
|
|
313
|
+
orderId: o.orderId,
|
|
314
|
+
state: o.state,
|
|
315
|
+
createTime: o.createTime || "-",
|
|
316
|
+
lineItems: o.lineItems?.length || 0
|
|
317
|
+
}));
|
|
318
|
+
console.log(formatOutput(rows, format));
|
|
319
|
+
}
|
|
320
|
+
} else {
|
|
321
|
+
console.log(formatOutput(result, format));
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
const product = purchases.command("product").description("Product purchase operations");
|
|
325
|
+
product.command("get-v2 <token>").description("Get product purchase details (v2 \u2014 supports multi-offer OTPs)").action(async (token) => {
|
|
326
|
+
const config = await loadConfig();
|
|
327
|
+
const packageName = resolvePackageName(program.opts()["app"], config);
|
|
328
|
+
const client = await getClient(config);
|
|
329
|
+
const format = getOutputFormat(program, config);
|
|
330
|
+
const result = await getProductPurchaseV2(client, packageName, token);
|
|
331
|
+
if (format !== "json") {
|
|
332
|
+
const row = {
|
|
333
|
+
orderId: result.orderId || "-",
|
|
334
|
+
state: result.purchaseStateContext?.state || "-",
|
|
335
|
+
regionCode: result.regionCode || "-",
|
|
336
|
+
completionTime: result.purchaseCompletionTime || "-",
|
|
337
|
+
acknowledgement: result.acknowledgementState || "-",
|
|
338
|
+
lineItems: result.productLineItem?.length || 0
|
|
339
|
+
};
|
|
340
|
+
console.log(formatOutput(row, format));
|
|
341
|
+
} else {
|
|
342
|
+
console.log(formatOutput(result, format));
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
sub.command("cancel-v2 <token>").description("Cancel a subscription (v2 \u2014 supports cancellation types)").option("--type <cancellationType>", "Cancellation type (e.g., USER_CANCELED, SYSTEM_CANCELED, DEVELOPER_CANCELED, REPLACED)").action(async (token, options) => {
|
|
346
|
+
const config = await loadConfig();
|
|
347
|
+
const packageName = resolvePackageName(program.opts()["app"], config);
|
|
348
|
+
await requireConfirm(`Cancel subscription (token: ${token.slice(0, 16)}...)?`, program);
|
|
349
|
+
if (isDryRun(program)) {
|
|
350
|
+
const format = getOutputFormat(program, config);
|
|
351
|
+
printDryRun(
|
|
352
|
+
{ command: "purchases subscription cancel-v2", action: "cancel", target: token.slice(0, 16) + "...", details: { cancellationType: options.type } },
|
|
353
|
+
format,
|
|
354
|
+
formatOutput
|
|
355
|
+
);
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
const client = await getClient(config);
|
|
359
|
+
await cancelSubscriptionV2(client, packageName, token, options.type);
|
|
360
|
+
console.log("Subscription cancelled.");
|
|
361
|
+
});
|
|
362
|
+
sub.command("defer-v2 <token>").description("Defer a subscription renewal (v2 \u2014 supports add-on subscriptions)").requiredOption("--until <date>", "Desired expiry time (ISO 8601 date)").action(async (token, options) => {
|
|
363
|
+
const config = await loadConfig();
|
|
364
|
+
const packageName = resolvePackageName(program.opts()["app"], config);
|
|
365
|
+
await requireConfirm(`Defer subscription renewal to ${options.until}?`, program);
|
|
366
|
+
if (isDryRun(program)) {
|
|
367
|
+
const format = getOutputFormat(program, config);
|
|
368
|
+
printDryRun(
|
|
369
|
+
{ command: "purchases subscription defer-v2", action: "defer", target: token.slice(0, 16) + "...", details: { desiredExpiryTime: options.until } },
|
|
370
|
+
format,
|
|
371
|
+
formatOutput
|
|
372
|
+
);
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
const client = await getClient(config);
|
|
376
|
+
const result = await deferSubscriptionV2(client, packageName, token, options.until);
|
|
377
|
+
console.log(`Subscription deferred. New expiry: ${result.newExpiryTime}`);
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
export {
|
|
381
|
+
registerPurchasesCommands
|
|
382
|
+
};
|
|
383
|
+
//# sourceMappingURL=purchases-DAWTMXP6.js.map
|