@analyticscli/growth-engineer 0.1.1-preview.15 → 0.1.1-preview.18
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/runtime/export-asc-summary.mjs +239 -79
- package/dist/runtime/export-asc-summary.mjs.map +1 -1
- package/dist/runtime/openclaw-exporters-lib.d.mts +62 -2
- package/dist/runtime/openclaw-exporters-lib.mjs +166 -67
- package/dist/runtime/openclaw-exporters-lib.mjs.map +1 -1
- package/dist/runtime/openclaw-growth-runner.mjs +0 -16
- package/dist/runtime/openclaw-growth-runner.mjs.map +1 -1
- package/dist/runtime/openclaw-growth-start.mjs +74 -5
- package/dist/runtime/openclaw-growth-start.mjs.map +1 -1
- package/dist/runtime/openclaw-growth-status.mjs +2 -2
- package/dist/runtime/openclaw-growth-status.mjs.map +1 -1
- package/dist/runtime/openclaw-growth-wizard.mjs +160 -45
- package/dist/runtime/openclaw-growth-wizard.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -108,7 +108,7 @@ const CONNECTOR_DEFINITIONS = [
|
|
|
108
108
|
key: 'asc',
|
|
109
109
|
label: 'ASC / App Store Connect CLI',
|
|
110
110
|
summary: 'Read App Store analytics, reviews/ratings, builds/TestFlight/release context, subscriptions, purchases, and crash totals.',
|
|
111
|
-
needs: '
|
|
111
|
+
needs: 'Two App Store Connect API keys: a Sales and Reports or Finance key for ongoing use, plus a temporary Admin key for one-time analytics bootstrap.',
|
|
112
112
|
},
|
|
113
113
|
{
|
|
114
114
|
key: 'stripe',
|
|
@@ -2535,7 +2535,7 @@ async function saveSecretsImmediately(secrets) {
|
|
|
2535
2535
|
process.stdout.write(`Saved local secrets to ${secretsFile} with chmod 600.\n`);
|
|
2536
2536
|
return true;
|
|
2537
2537
|
}
|
|
2538
|
-
async function runImmediateConnectorHealthCheck({ rl, configPath, connector, secrets, sentryAccounts = [], paddleAccounts = [], }) {
|
|
2538
|
+
async function runImmediateConnectorHealthCheck({ rl, configPath, connector, secrets, runtimeEnv = {}, sentryAccounts = [], paddleAccounts = [], }) {
|
|
2539
2539
|
if (connector === 'sentry' && sentryAccounts.length > 0) {
|
|
2540
2540
|
await upsertSentryAccountsConfig(configPath, sentryAccounts);
|
|
2541
2541
|
}
|
|
@@ -2546,6 +2546,7 @@ async function runImmediateConnectorHealthCheck({ rl, configPath, connector, sec
|
|
|
2546
2546
|
const env = {
|
|
2547
2547
|
...process.env,
|
|
2548
2548
|
...secrets,
|
|
2549
|
+
...runtimeEnv,
|
|
2549
2550
|
};
|
|
2550
2551
|
const command = `${nodeRuntimeScriptCommand('openclaw-growth-start.mjs')} --config ${quote(configPath)} --setup-only --connectors ${quote(connector)} --only-connectors ${quote(connector)}`;
|
|
2551
2552
|
let result = await runSetupCommandWithProgress(command, env, [connector], `Checking ${connectorLabel(connector)} immediately after setup...`);
|
|
@@ -2686,12 +2687,17 @@ function resolveSecretsFile() {
|
|
|
2686
2687
|
return path.join(process.env.HOME, '.config', 'openclaw-growth', 'secrets.env');
|
|
2687
2688
|
return path.resolve('.openclaw-growth-secrets.env');
|
|
2688
2689
|
}
|
|
2689
|
-
function resolveAscPrivateKeyPath(keyId) {
|
|
2690
|
+
function resolveAscPrivateKeyPath(keyId, suffix = '') {
|
|
2690
2691
|
const safeKeyId = (keyId || 'OPENCLAW').trim().replace(/[^a-zA-Z0-9_-]/g, '_') || 'OPENCLAW';
|
|
2691
2692
|
const baseDir = process.env.HOME
|
|
2692
2693
|
? path.join(process.env.HOME, '.config', 'openclaw-growth')
|
|
2693
2694
|
: path.resolve('.openclaw-growth');
|
|
2694
|
-
return path.join(baseDir, `AuthKey_${safeKeyId}.p8`);
|
|
2695
|
+
return path.join(baseDir, `AuthKey_${safeKeyId}${suffix}.p8`);
|
|
2696
|
+
}
|
|
2697
|
+
function inferAscKeyIdFromPrivateKeyPath(filePath) {
|
|
2698
|
+
const fileName = path.basename(String(filePath || '').trim());
|
|
2699
|
+
const match = fileName.match(/^AuthKey_([A-Za-z0-9]+)\.p8$/);
|
|
2700
|
+
return match?.[1] || '';
|
|
2695
2701
|
}
|
|
2696
2702
|
function renderEnvValue(value) {
|
|
2697
2703
|
return `"${String(value).replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\$/g, '\\$')}"`;
|
|
@@ -3384,11 +3390,14 @@ function validateAscPrivateKeyContent(value) {
|
|
|
3384
3390
|
};
|
|
3385
3391
|
}
|
|
3386
3392
|
}
|
|
3387
|
-
async function askAscPrivateKeyContent(rl) {
|
|
3388
|
-
|
|
3389
|
-
|
|
3393
|
+
async function askAscPrivateKeyContent(rl, options = {}) {
|
|
3394
|
+
const envName = options.envName || 'ASC_PRIVATE_KEY';
|
|
3395
|
+
const persistLabel = options.persistLabel || 'ASC_PRIVATE_KEY_PATH';
|
|
3396
|
+
const keyLabel = options.keyLabel || 'this App Store Connect key';
|
|
3397
|
+
process.stdout.write(`\nPaste the full .p8 file content for ${keyLabel}.\nLeave the first line empty if the .p8 file is already saved on this host.\n`);
|
|
3398
|
+
process.stdout.write(`The wizard validates the pasted key, stores it locally with chmod 600, and only saves ${persistLabel}.\n`);
|
|
3390
3399
|
while (true) {
|
|
3391
|
-
const value = await readAscPrivateKeyPaste(rl);
|
|
3400
|
+
const value = await readAscPrivateKeyPaste(rl, envName);
|
|
3392
3401
|
if (!value.trim())
|
|
3393
3402
|
return '';
|
|
3394
3403
|
const validation = validateAscPrivateKeyContent(value);
|
|
@@ -3398,7 +3407,7 @@ async function askAscPrivateKeyContent(rl) {
|
|
|
3398
3407
|
process.stdout.write('The .p8 was not saved. Paste the full file again from BEGIN to END, or leave empty to use a path.\n');
|
|
3399
3408
|
}
|
|
3400
3409
|
}
|
|
3401
|
-
async function readAscPrivateKeyPaste(rl) {
|
|
3410
|
+
async function readAscPrivateKeyPaste(rl, envName = 'ASC_PRIVATE_KEY') {
|
|
3402
3411
|
return await new Promise((resolve, reject) => {
|
|
3403
3412
|
let buffer = '';
|
|
3404
3413
|
let settled = false;
|
|
@@ -3459,7 +3468,7 @@ async function readAscPrivateKeyPaste(rl) {
|
|
|
3459
3468
|
process.stdin.setEncoding('utf8');
|
|
3460
3469
|
process.stdin.on('data', onData);
|
|
3461
3470
|
process.stdin.on('error', onError);
|
|
3462
|
-
process.stdout.write(
|
|
3471
|
+
process.stdout.write(`${envName} content: `);
|
|
3463
3472
|
process.stdin.resume();
|
|
3464
3473
|
});
|
|
3465
3474
|
}
|
|
@@ -3467,9 +3476,11 @@ async function validateAscPrivateKeyPath(filePath) {
|
|
|
3467
3476
|
const raw = await fs.readFile(filePath, 'utf8');
|
|
3468
3477
|
return validateAscPrivateKeyContent(raw);
|
|
3469
3478
|
}
|
|
3470
|
-
async function askAscPrivateKeyPath(rl) {
|
|
3479
|
+
async function askAscPrivateKeyPath(rl, options = {}) {
|
|
3480
|
+
const label = options.label || 'ASC_PRIVATE_KEY_PATH (path to AuthKey_XXXX.p8, leave empty to skip)';
|
|
3481
|
+
const defaultValue = options.defaultValue ?? process.env.ASC_PRIVATE_KEY_PATH ?? '';
|
|
3471
3482
|
while (true) {
|
|
3472
|
-
const privateKeyPath = await ask(rl,
|
|
3483
|
+
const privateKeyPath = await ask(rl, label, defaultValue);
|
|
3473
3484
|
const trimmedPath = privateKeyPath.trim();
|
|
3474
3485
|
if (!trimmedPath)
|
|
3475
3486
|
return '';
|
|
@@ -3485,6 +3496,19 @@ async function askAscPrivateKeyPath(rl) {
|
|
|
3485
3496
|
process.stdout.write('The ASC private key path was not saved. Paste a valid path, or leave empty to skip.\n');
|
|
3486
3497
|
}
|
|
3487
3498
|
}
|
|
3499
|
+
async function askAscPrivateKeyPathWithKeyId(rl, options = {}) {
|
|
3500
|
+
const keyLabel = options.keyLabel || 'App Store Connect key';
|
|
3501
|
+
while (true) {
|
|
3502
|
+
const privateKeyPath = await askAscPrivateKeyPath(rl, options);
|
|
3503
|
+
if (!privateKeyPath)
|
|
3504
|
+
return { privateKeyPath: '', keyId: '' };
|
|
3505
|
+
const keyId = inferAscKeyIdFromPrivateKeyPath(privateKeyPath);
|
|
3506
|
+
if (keyId)
|
|
3507
|
+
return { privateKeyPath, keyId };
|
|
3508
|
+
process.stdout.write(`Could not infer Key ID for ${keyLabel} from the .p8 file name.\n`);
|
|
3509
|
+
process.stdout.write('Use Apple\'s original downloaded file name: AuthKey_<KEY_ID>.p8. Do not rename the .p8 file.\n');
|
|
3510
|
+
}
|
|
3511
|
+
}
|
|
3488
3512
|
function printSection(title, lines = []) {
|
|
3489
3513
|
process.stdout.write(`\n${ANSI.bold}${title}${ANSI.reset}\n`);
|
|
3490
3514
|
process.stdout.write(`${'-'.repeat(title.length)}\n`);
|
|
@@ -3921,41 +3945,58 @@ async function guideCoolifyConnector(rl, secrets) {
|
|
|
3921
3945
|
}
|
|
3922
3946
|
async function guideAscConnector(rl, secrets) {
|
|
3923
3947
|
printSection('App Store Connect CLI', [
|
|
3924
|
-
'
|
|
3925
|
-
'
|
|
3948
|
+
'You need two App Store Connect API keys: one normal reporting key and one temporary Admin key.',
|
|
3949
|
+
'Create both here: https://appstoreconnect.apple.com/access/integrations/api',
|
|
3926
3950
|
]);
|
|
3927
|
-
process.stdout.write('
|
|
3928
|
-
process.stdout.write('Roles to choose for this key:\n');
|
|
3951
|
+
process.stdout.write('\nStep 1 - normal key, saved for Growth Engineer\n');
|
|
3929
3952
|
printBullets([
|
|
3930
|
-
'
|
|
3931
|
-
'
|
|
3932
|
-
'
|
|
3933
|
-
'
|
|
3934
|
-
'Optional: App Manager, only if OpenClaw should also read or manage app metadata, pricing, or release settings.',
|
|
3935
|
-
'Least privilege option: run setup once with Admin, then rotate Growth Engineer to a Sales and Reports key for ongoing analytics downloads.',
|
|
3953
|
+
'Create an API key named "Growth Engineer Reports".',
|
|
3954
|
+
'Role: Sales and Reports. Finance or Admin also works.',
|
|
3955
|
+
'Optional extra roles: Customer Support for reviews, Developer for builds/TestFlight.',
|
|
3956
|
+
'Copy this key into the ASC_KEY_ID, ASC_ISSUER_ID, and ASC_PRIVATE_KEY prompts below.',
|
|
3936
3957
|
]);
|
|
3937
|
-
process.stdout.write('\
|
|
3958
|
+
process.stdout.write('\nStep 2 - temporary Admin key, used once\n');
|
|
3938
3959
|
printBullets([
|
|
3939
|
-
'
|
|
3940
|
-
'
|
|
3941
|
-
'
|
|
3960
|
+
'Create a second API key named "Growth Engineer Setup Admin".',
|
|
3961
|
+
'Role: Admin.',
|
|
3962
|
+
'Use it only when the wizard asks for ASC_BOOTSTRAP_* later.',
|
|
3963
|
+
'The wizard does not save this key to secrets.env. Revoke it in App Store Connect after setup.',
|
|
3942
3964
|
]);
|
|
3943
|
-
process.stdout.write('\
|
|
3965
|
+
process.stdout.write('\nWhy two keys?\n');
|
|
3944
3966
|
printBullets([
|
|
3945
|
-
'
|
|
3946
|
-
'
|
|
3947
|
-
'Download the .p8 file, open it, then paste the full file content into this terminal.',
|
|
3948
|
-
'If the .p8 is already on this host, leave the content prompt empty and paste the file path instead.',
|
|
3949
|
-
'Vendor Number from App Store Connect Sales and Trends > Reports. Required for healthy ASC status because Sales and Trends/App Units depend on it.',
|
|
3967
|
+
'Apple requires Admin once to create the initial App Analytics report request.',
|
|
3968
|
+
'After that, Growth Engineer only needs the normal reporting key for downloads.',
|
|
3950
3969
|
]);
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3970
|
+
process.stdout.write('\nNeeded values for the normal key now\n');
|
|
3971
|
+
printBullets([
|
|
3972
|
+
'Issuer ID: shown at the top of the API keys page.',
|
|
3973
|
+
'.p8 file path: use Apple\'s original downloaded file name AuthKey_<KEY_ID>.p8.',
|
|
3974
|
+
'Do not rename the .p8 file; the wizard reads ASC_KEY_ID from the file name.',
|
|
3975
|
+
'Vendor Number: App Store Connect > Sales and Trends > Reports.',
|
|
3976
|
+
]);
|
|
3977
|
+
const normalKeyPath = await askAscPrivateKeyPathWithKeyId(rl, {
|
|
3978
|
+
label: 'ASC_PRIVATE_KEY_PATH for normal reporting key (AuthKey_<KEY_ID>.p8 path, empty = paste content instead)',
|
|
3979
|
+
defaultValue: process.env.ASC_PRIVATE_KEY_PATH || '',
|
|
3980
|
+
keyLabel: 'the normal reporting key',
|
|
3981
|
+
});
|
|
3982
|
+
let keyId = normalKeyPath.keyId;
|
|
3983
|
+
if (normalKeyPath.privateKeyPath) {
|
|
3984
|
+
secrets.ASC_PRIVATE_KEY_PATH = normalKeyPath.privateKeyPath;
|
|
3985
|
+
secrets.ASC_KEY_ID = keyId;
|
|
3986
|
+
process.stdout.write(`Inferred ASC_KEY_ID=${keyId} from ${path.basename(normalKeyPath.privateKeyPath)}.\n`);
|
|
3987
|
+
}
|
|
3988
|
+
const issuerId = await ask(rl, 'ASC_ISSUER_ID for normal reporting key (leave empty to skip)', process.env.ASC_ISSUER_ID || '');
|
|
3955
3989
|
if (issuerId.trim())
|
|
3956
3990
|
secrets.ASC_ISSUER_ID = issuerId.trim();
|
|
3957
|
-
|
|
3958
|
-
|
|
3991
|
+
if (!normalKeyPath.privateKeyPath) {
|
|
3992
|
+
keyId = await ask(rl, 'ASC_KEY_ID for normal reporting key (from AuthKey_<KEY_ID>.p8, leave empty to skip)', process.env.ASC_KEY_ID || '');
|
|
3993
|
+
if (keyId.trim())
|
|
3994
|
+
secrets.ASC_KEY_ID = keyId.trim();
|
|
3995
|
+
const privateKeyContent = await askAscPrivateKeyContent(rl, {
|
|
3996
|
+
keyLabel: 'the normal reporting key',
|
|
3997
|
+
});
|
|
3998
|
+
if (!privateKeyContent)
|
|
3999
|
+
return await guideAscBootstrapAdminKey(rl, issuerId.trim());
|
|
3959
4000
|
const privateKeyPath = resolveAscPrivateKeyPath(keyId);
|
|
3960
4001
|
await fs.mkdir(path.dirname(privateKeyPath), { recursive: true, mode: 0o700 });
|
|
3961
4002
|
await fs.writeFile(privateKeyPath, privateKeyContent, { encoding: 'utf8', mode: 0o600 });
|
|
@@ -3963,14 +4004,85 @@ async function guideAscConnector(rl, secrets) {
|
|
|
3963
4004
|
secrets.ASC_PRIVATE_KEY_PATH = privateKeyPath;
|
|
3964
4005
|
process.stdout.write(`Saved ASC private key to ${privateKeyPath} with chmod 600.\n`);
|
|
3965
4006
|
}
|
|
3966
|
-
else {
|
|
3967
|
-
const privateKeyPath = await askAscPrivateKeyPath(rl);
|
|
3968
|
-
if (privateKeyPath.trim())
|
|
3969
|
-
secrets.ASC_PRIVATE_KEY_PATH = privateKeyPath.trim();
|
|
3970
|
-
}
|
|
3971
4007
|
const vendorNumber = await ask(rl, 'ASC_VENDOR_NUMBER for Sales and Trends/App Units (required for healthy ASC status)', process.env.ASC_VENDOR_NUMBER || '');
|
|
3972
4008
|
if (vendorNumber.trim())
|
|
3973
4009
|
secrets.ASC_VENDOR_NUMBER = vendorNumber.trim();
|
|
4010
|
+
return await guideAscBootstrapAdminKey(rl, issuerId.trim());
|
|
4011
|
+
}
|
|
4012
|
+
async function guideAscBootstrapAdminKey(rl, issuerIdDefault = '') {
|
|
4013
|
+
const bootstrapEnv = {};
|
|
4014
|
+
process.stdout.write('\nStep 3 - temporary Admin key for first ASC setup\n');
|
|
4015
|
+
printBullets([
|
|
4016
|
+
'Use the second key you created with the Admin role.',
|
|
4017
|
+
'This is only needed to create the first App Analytics report request.',
|
|
4018
|
+
'The wizard does not save ASC_BOOTSTRAP_* to secrets.env.',
|
|
4019
|
+
'Use Apple\'s original AuthKey_<KEY_ID>.p8 file name so the wizard can infer ASC_BOOTSTRAP_KEY_ID.',
|
|
4020
|
+
'Do not rename the .p8 file.',
|
|
4021
|
+
'If you paste the .p8 content, the local temporary file is deleted after analytics initialization.',
|
|
4022
|
+
'Revoke this Admin key in App Store Connect after setup.',
|
|
4023
|
+
]);
|
|
4024
|
+
const bootstrapKeyPath = await askAscPrivateKeyPathWithKeyId(rl, {
|
|
4025
|
+
label: 'ASC_BOOTSTRAP_PRIVATE_KEY_PATH for temporary Admin key (AuthKey_<KEY_ID>.p8 path, empty = paste content instead)',
|
|
4026
|
+
defaultValue: '',
|
|
4027
|
+
keyLabel: 'the temporary Admin key',
|
|
4028
|
+
});
|
|
4029
|
+
let bootstrapKeyId = bootstrapKeyPath.keyId;
|
|
4030
|
+
const bootstrapIssuerId = await ask(rl, 'ASC_BOOTSTRAP_ISSUER_ID for temporary Admin key', issuerIdDefault);
|
|
4031
|
+
if (bootstrapKeyPath.privateKeyPath) {
|
|
4032
|
+
bootstrapEnv.ASC_BOOTSTRAP_PRIVATE_KEY_PATH = bootstrapKeyPath.privateKeyPath;
|
|
4033
|
+
process.stdout.write(`Inferred ASC_BOOTSTRAP_KEY_ID=${bootstrapKeyId} from ${path.basename(bootstrapKeyPath.privateKeyPath)}.\n`);
|
|
4034
|
+
const shouldDelete = await askYesNo(rl, 'Delete this temporary Admin .p8 file from this host after the setup check?', true);
|
|
4035
|
+
if (shouldDelete)
|
|
4036
|
+
bootstrapEnv.ASC_BOOTSTRAP_PRIVATE_KEY_DELETE_AFTER_USE = '1';
|
|
4037
|
+
}
|
|
4038
|
+
else {
|
|
4039
|
+
bootstrapKeyId = await ask(rl, 'ASC_BOOTSTRAP_KEY_ID for temporary Admin key (from AuthKey_<KEY_ID>.p8, required)', '');
|
|
4040
|
+
}
|
|
4041
|
+
if (!bootstrapKeyId.trim() || !bootstrapIssuerId.trim())
|
|
4042
|
+
return { bootstrapEnv };
|
|
4043
|
+
bootstrapEnv.ASC_BOOTSTRAP_KEY_ID = bootstrapKeyId.trim();
|
|
4044
|
+
bootstrapEnv.ASC_BOOTSTRAP_ISSUER_ID = bootstrapIssuerId.trim();
|
|
4045
|
+
if (!bootstrapKeyPath.privateKeyPath) {
|
|
4046
|
+
const bootstrapPrivateKeyContent = await askAscPrivateKeyContent(rl, {
|
|
4047
|
+
envName: 'ASC_BOOTSTRAP_PRIVATE_KEY',
|
|
4048
|
+
persistLabel: 'ASC_BOOTSTRAP_PRIVATE_KEY_PATH temporarily',
|
|
4049
|
+
keyLabel: 'the temporary Admin key',
|
|
4050
|
+
});
|
|
4051
|
+
if (!bootstrapPrivateKeyContent)
|
|
4052
|
+
return { bootstrapEnv };
|
|
4053
|
+
const bootstrapPrivateKeyPath = resolveAscPrivateKeyPath(bootstrapKeyId, '_bootstrap_admin');
|
|
4054
|
+
await fs.mkdir(path.dirname(bootstrapPrivateKeyPath), { recursive: true, mode: 0o700 });
|
|
4055
|
+
await fs.writeFile(bootstrapPrivateKeyPath, bootstrapPrivateKeyContent, { encoding: 'utf8', mode: 0o600 });
|
|
4056
|
+
await fs.chmod(bootstrapPrivateKeyPath, 0o600);
|
|
4057
|
+
bootstrapEnv.ASC_BOOTSTRAP_PRIVATE_KEY_PATH = bootstrapPrivateKeyPath;
|
|
4058
|
+
bootstrapEnv.ASC_BOOTSTRAP_PRIVATE_KEY_DELETE_AFTER_USE = '1';
|
|
4059
|
+
process.stdout.write(`Saved temporary Admin ASC private key to ${bootstrapPrivateKeyPath} with chmod 600. It will be deleted after the setup check.\n`);
|
|
4060
|
+
}
|
|
4061
|
+
return { bootstrapEnv };
|
|
4062
|
+
}
|
|
4063
|
+
async function cleanupTemporaryAscBootstrapPrivateKey(bootstrapEnv = {}) {
|
|
4064
|
+
const privateKeyPath = String(bootstrapEnv.ASC_BOOTSTRAP_PRIVATE_KEY_PATH || '').trim();
|
|
4065
|
+
const shouldDelete = String(bootstrapEnv.ASC_BOOTSTRAP_PRIVATE_KEY_DELETE_AFTER_USE || '').trim().toLowerCase();
|
|
4066
|
+
if (!privateKeyPath || !['1', 'true', 'yes'].includes(shouldDelete))
|
|
4067
|
+
return;
|
|
4068
|
+
if (privateKeyPath === String(bootstrapEnv.ASC_PRIVATE_KEY_PATH || process.env.ASC_PRIVATE_KEY_PATH || '').trim()) {
|
|
4069
|
+
process.stdout.write('Temporary Admin .p8 path matches the steady-state ASC_PRIVATE_KEY_PATH; leaving it in place.\n');
|
|
4070
|
+
process.stdout.write('You can also revoke the temporary Admin API key in App Store Connect.\n');
|
|
4071
|
+
return;
|
|
4072
|
+
}
|
|
4073
|
+
try {
|
|
4074
|
+
await fs.unlink(privateKeyPath);
|
|
4075
|
+
process.stdout.write(`Deleted temporary Admin .p8 from ${privateKeyPath}.\n`);
|
|
4076
|
+
}
|
|
4077
|
+
catch (error) {
|
|
4078
|
+
if (error?.code === 'ENOENT') {
|
|
4079
|
+
process.stdout.write(`Temporary Admin .p8 was already absent at ${privateKeyPath}.\n`);
|
|
4080
|
+
process.stdout.write('You can also revoke the temporary Admin API key in App Store Connect.\n');
|
|
4081
|
+
return;
|
|
4082
|
+
}
|
|
4083
|
+
process.stdout.write(`Could not delete temporary Admin .p8 at ${privateKeyPath}: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
4084
|
+
}
|
|
4085
|
+
process.stdout.write('You can also revoke the temporary Admin API key in App Store Connect.\n');
|
|
3974
4086
|
}
|
|
3975
4087
|
async function shouldRunSelfUpdate(workspaceRoot, force) {
|
|
3976
4088
|
if (force)
|
|
@@ -4204,13 +4316,16 @@ async function runConnectorSetupSteps({ rl, args, selected, healthByConnector, a
|
|
|
4204
4316
|
if (selected.includes('asc')) {
|
|
4205
4317
|
while (true) {
|
|
4206
4318
|
clearTerminal();
|
|
4207
|
-
await guideAscConnector(rl, secrets);
|
|
4208
|
-
|
|
4319
|
+
const ascSetup = await guideAscConnector(rl, secrets);
|
|
4320
|
+
let bootstrapEnv = ascSetup?.bootstrapEnv || {};
|
|
4321
|
+
let check = await runImmediateConnectorHealthCheck({
|
|
4209
4322
|
rl,
|
|
4210
4323
|
configPath: args.config,
|
|
4211
4324
|
connector: 'asc',
|
|
4212
4325
|
secrets,
|
|
4326
|
+
runtimeEnv: bootstrapEnv,
|
|
4213
4327
|
});
|
|
4328
|
+
await cleanupTemporaryAscBootstrapPrivateKey(bootstrapEnv);
|
|
4214
4329
|
if (!check.retry)
|
|
4215
4330
|
break;
|
|
4216
4331
|
}
|