@analyticscli/growth-engineer 0.1.1-preview.15 → 0.1.1-preview.19
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 +145 -50
- 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`);
|
|
@@ -3500,6 +3524,9 @@ function printBullets(lines) {
|
|
|
3500
3524
|
}
|
|
3501
3525
|
process.stdout.write('\n');
|
|
3502
3526
|
}
|
|
3527
|
+
function bold(text) {
|
|
3528
|
+
return `${ANSI.bold}${text}${ANSI.reset}`;
|
|
3529
|
+
}
|
|
3503
3530
|
async function guideGitHubConnector(rl, secrets) {
|
|
3504
3531
|
printSection('GitHub code access', [
|
|
3505
3532
|
'Use this when OpenClaw should read repo context or create GitHub delivery artifacts.',
|
|
@@ -3921,41 +3948,39 @@ async function guideCoolifyConnector(rl, secrets) {
|
|
|
3921
3948
|
}
|
|
3922
3949
|
async function guideAscConnector(rl, secrets) {
|
|
3923
3950
|
printSection('App Store Connect CLI', [
|
|
3924
|
-
'
|
|
3925
|
-
'
|
|
3926
|
-
|
|
3927
|
-
process.stdout.write('Create an App Store Connect API key here:\n https://appstoreconnect.apple.com/access/integrations/api\n\n');
|
|
3928
|
-
process.stdout.write('Roles to choose for this key:\n');
|
|
3929
|
-
printBullets([
|
|
3930
|
-
'Required for first setup: Admin, because Apple only allows Admin keys to create the initial Analytics Report Request.',
|
|
3931
|
-
'Required for steady-state report downloads after the request exists: Sales and Reports, Finance, or Admin.',
|
|
3932
|
-
'Recommended: Customer Support, for App Store ratings and review text.',
|
|
3933
|
-
'Recommended: Developer, for builds, TestFlight, and delivery status.',
|
|
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.',
|
|
3936
|
-
]);
|
|
3937
|
-
process.stdout.write('\nWhy Admin is requested during setup:\n');
|
|
3938
|
-
printBullets([
|
|
3939
|
-
'Growth Engineer automatically creates an ongoing App Analytics report request when none exists.',
|
|
3940
|
-
'Without that request, Apple will not generate Impressions, Product Page Views, App Units, Conversion Rate, and related report instances.',
|
|
3941
|
-
'A non-Admin key can read existing reports, but creation fails with a forbidden response.',
|
|
3951
|
+
`${bold('Create 2 API keys')} here: https://appstoreconnect.apple.com/access/integrations/api`,
|
|
3952
|
+
`1. ${bold('Reports key')} - role ${bold('Sales and Reports')} - saved for Growth Engineer.`,
|
|
3953
|
+
`2. ${bold('Setup key')} - role ${bold('Admin')} - used once, then revoke.`,
|
|
3942
3954
|
]);
|
|
3943
|
-
process.stdout.write('
|
|
3955
|
+
process.stdout.write(`${bold('Enter the Reports key now:')}\n`);
|
|
3944
3956
|
printBullets([
|
|
3945
|
-
'
|
|
3946
|
-
'
|
|
3947
|
-
'
|
|
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.',
|
|
3957
|
+
`${bold('.p8 path')} to Apple\'s original ${bold('AuthKey_<KEY_ID>.p8')} file. ${bold('Do not rename it')}; KEY_ID is read from the filename.`,
|
|
3958
|
+
`${bold('Issuer ID')} from the API keys page.`,
|
|
3959
|
+
`${bold('Vendor Number')} from Sales and Trends > Reports.`,
|
|
3950
3960
|
]);
|
|
3951
|
-
const
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3961
|
+
const normalKeyPath = await askAscPrivateKeyPathWithKeyId(rl, {
|
|
3962
|
+
label: 'Reports .p8 path (AuthKey_<KEY_ID>.p8, empty = paste)',
|
|
3963
|
+
defaultValue: process.env.ASC_PRIVATE_KEY_PATH || '',
|
|
3964
|
+
keyLabel: 'the normal reporting key',
|
|
3965
|
+
});
|
|
3966
|
+
let keyId = normalKeyPath.keyId;
|
|
3967
|
+
if (normalKeyPath.privateKeyPath) {
|
|
3968
|
+
secrets.ASC_PRIVATE_KEY_PATH = normalKeyPath.privateKeyPath;
|
|
3969
|
+
secrets.ASC_KEY_ID = keyId;
|
|
3970
|
+
process.stdout.write(`Inferred ASC_KEY_ID=${keyId} from ${path.basename(normalKeyPath.privateKeyPath)}.\n`);
|
|
3971
|
+
}
|
|
3972
|
+
const issuerId = await ask(rl, 'ASC_ISSUER_ID (reports key, empty = skip)', process.env.ASC_ISSUER_ID || '');
|
|
3955
3973
|
if (issuerId.trim())
|
|
3956
3974
|
secrets.ASC_ISSUER_ID = issuerId.trim();
|
|
3957
|
-
|
|
3958
|
-
|
|
3975
|
+
if (!normalKeyPath.privateKeyPath) {
|
|
3976
|
+
keyId = await ask(rl, 'ASC_KEY_ID (from AuthKey_<KEY_ID>.p8, empty = skip)', process.env.ASC_KEY_ID || '');
|
|
3977
|
+
if (keyId.trim())
|
|
3978
|
+
secrets.ASC_KEY_ID = keyId.trim();
|
|
3979
|
+
const privateKeyContent = await askAscPrivateKeyContent(rl, {
|
|
3980
|
+
keyLabel: 'the normal reporting key',
|
|
3981
|
+
});
|
|
3982
|
+
if (!privateKeyContent)
|
|
3983
|
+
return await guideAscBootstrapAdminKey(rl, issuerId.trim());
|
|
3959
3984
|
const privateKeyPath = resolveAscPrivateKeyPath(keyId);
|
|
3960
3985
|
await fs.mkdir(path.dirname(privateKeyPath), { recursive: true, mode: 0o700 });
|
|
3961
3986
|
await fs.writeFile(privateKeyPath, privateKeyContent, { encoding: 'utf8', mode: 0o600 });
|
|
@@ -3963,14 +3988,81 @@ async function guideAscConnector(rl, secrets) {
|
|
|
3963
3988
|
secrets.ASC_PRIVATE_KEY_PATH = privateKeyPath;
|
|
3964
3989
|
process.stdout.write(`Saved ASC private key to ${privateKeyPath} with chmod 600.\n`);
|
|
3965
3990
|
}
|
|
3966
|
-
|
|
3967
|
-
const privateKeyPath = await askAscPrivateKeyPath(rl);
|
|
3968
|
-
if (privateKeyPath.trim())
|
|
3969
|
-
secrets.ASC_PRIVATE_KEY_PATH = privateKeyPath.trim();
|
|
3970
|
-
}
|
|
3971
|
-
const vendorNumber = await ask(rl, 'ASC_VENDOR_NUMBER for Sales and Trends/App Units (required for healthy ASC status)', process.env.ASC_VENDOR_NUMBER || '');
|
|
3991
|
+
const vendorNumber = await ask(rl, 'ASC_VENDOR_NUMBER (Sales and Trends > Reports)', process.env.ASC_VENDOR_NUMBER || '');
|
|
3972
3992
|
if (vendorNumber.trim())
|
|
3973
3993
|
secrets.ASC_VENDOR_NUMBER = vendorNumber.trim();
|
|
3994
|
+
return await guideAscBootstrapAdminKey(rl, issuerId.trim());
|
|
3995
|
+
}
|
|
3996
|
+
async function guideAscBootstrapAdminKey(rl, issuerIdDefault = '') {
|
|
3997
|
+
const bootstrapEnv = {};
|
|
3998
|
+
process.stdout.write(`\n${bold('Enter the Setup Admin key:')}\n`);
|
|
3999
|
+
printBullets([
|
|
4000
|
+
`${bold('Role must be Admin')} so Apple can create the first App Analytics report request.`,
|
|
4001
|
+
`${bold('Use original AuthKey_<KEY_ID>.p8 filename')} so KEY_ID is read automatically.`,
|
|
4002
|
+
`${bold('Not saved')} to secrets.env. Revoke this key after setup.`,
|
|
4003
|
+
]);
|
|
4004
|
+
const bootstrapKeyPath = await askAscPrivateKeyPathWithKeyId(rl, {
|
|
4005
|
+
label: 'Setup Admin .p8 path (AuthKey_<KEY_ID>.p8, empty = paste)',
|
|
4006
|
+
defaultValue: '',
|
|
4007
|
+
keyLabel: 'the temporary Admin key',
|
|
4008
|
+
});
|
|
4009
|
+
let bootstrapKeyId = bootstrapKeyPath.keyId;
|
|
4010
|
+
const bootstrapIssuerId = await ask(rl, 'ASC_BOOTSTRAP_ISSUER_ID', issuerIdDefault);
|
|
4011
|
+
if (bootstrapKeyPath.privateKeyPath) {
|
|
4012
|
+
bootstrapEnv.ASC_BOOTSTRAP_PRIVATE_KEY_PATH = bootstrapKeyPath.privateKeyPath;
|
|
4013
|
+
process.stdout.write(`Inferred ASC_BOOTSTRAP_KEY_ID=${bootstrapKeyId} from ${path.basename(bootstrapKeyPath.privateKeyPath)}.\n`);
|
|
4014
|
+
const shouldDelete = await askYesNo(rl, 'Delete this temporary Admin .p8 file from this host after the setup check?', true);
|
|
4015
|
+
if (shouldDelete)
|
|
4016
|
+
bootstrapEnv.ASC_BOOTSTRAP_PRIVATE_KEY_DELETE_AFTER_USE = '1';
|
|
4017
|
+
}
|
|
4018
|
+
else {
|
|
4019
|
+
bootstrapKeyId = await ask(rl, 'ASC_BOOTSTRAP_KEY_ID (from AuthKey_<KEY_ID>.p8)', '');
|
|
4020
|
+
}
|
|
4021
|
+
if (!bootstrapKeyId.trim() || !bootstrapIssuerId.trim())
|
|
4022
|
+
return { bootstrapEnv };
|
|
4023
|
+
bootstrapEnv.ASC_BOOTSTRAP_KEY_ID = bootstrapKeyId.trim();
|
|
4024
|
+
bootstrapEnv.ASC_BOOTSTRAP_ISSUER_ID = bootstrapIssuerId.trim();
|
|
4025
|
+
if (!bootstrapKeyPath.privateKeyPath) {
|
|
4026
|
+
const bootstrapPrivateKeyContent = await askAscPrivateKeyContent(rl, {
|
|
4027
|
+
envName: 'ASC_BOOTSTRAP_PRIVATE_KEY',
|
|
4028
|
+
persistLabel: 'ASC_BOOTSTRAP_PRIVATE_KEY_PATH temporarily',
|
|
4029
|
+
keyLabel: 'the temporary Admin key',
|
|
4030
|
+
});
|
|
4031
|
+
if (!bootstrapPrivateKeyContent)
|
|
4032
|
+
return { bootstrapEnv };
|
|
4033
|
+
const bootstrapPrivateKeyPath = resolveAscPrivateKeyPath(bootstrapKeyId, '_bootstrap_admin');
|
|
4034
|
+
await fs.mkdir(path.dirname(bootstrapPrivateKeyPath), { recursive: true, mode: 0o700 });
|
|
4035
|
+
await fs.writeFile(bootstrapPrivateKeyPath, bootstrapPrivateKeyContent, { encoding: 'utf8', mode: 0o600 });
|
|
4036
|
+
await fs.chmod(bootstrapPrivateKeyPath, 0o600);
|
|
4037
|
+
bootstrapEnv.ASC_BOOTSTRAP_PRIVATE_KEY_PATH = bootstrapPrivateKeyPath;
|
|
4038
|
+
bootstrapEnv.ASC_BOOTSTRAP_PRIVATE_KEY_DELETE_AFTER_USE = '1';
|
|
4039
|
+
process.stdout.write(`Saved temporary Admin ASC private key to ${bootstrapPrivateKeyPath} with chmod 600. It will be deleted after the setup check.\n`);
|
|
4040
|
+
}
|
|
4041
|
+
return { bootstrapEnv };
|
|
4042
|
+
}
|
|
4043
|
+
async function cleanupTemporaryAscBootstrapPrivateKey(bootstrapEnv = {}) {
|
|
4044
|
+
const privateKeyPath = String(bootstrapEnv.ASC_BOOTSTRAP_PRIVATE_KEY_PATH || '').trim();
|
|
4045
|
+
const shouldDelete = String(bootstrapEnv.ASC_BOOTSTRAP_PRIVATE_KEY_DELETE_AFTER_USE || '').trim().toLowerCase();
|
|
4046
|
+
if (!privateKeyPath || !['1', 'true', 'yes'].includes(shouldDelete))
|
|
4047
|
+
return;
|
|
4048
|
+
if (privateKeyPath === String(bootstrapEnv.ASC_PRIVATE_KEY_PATH || process.env.ASC_PRIVATE_KEY_PATH || '').trim()) {
|
|
4049
|
+
process.stdout.write('Temporary Admin .p8 path matches the steady-state ASC_PRIVATE_KEY_PATH; leaving it in place.\n');
|
|
4050
|
+
process.stdout.write('You can also revoke the temporary Admin API key in App Store Connect.\n');
|
|
4051
|
+
return;
|
|
4052
|
+
}
|
|
4053
|
+
try {
|
|
4054
|
+
await fs.unlink(privateKeyPath);
|
|
4055
|
+
process.stdout.write(`Deleted temporary Admin .p8 from ${privateKeyPath}.\n`);
|
|
4056
|
+
}
|
|
4057
|
+
catch (error) {
|
|
4058
|
+
if (error?.code === 'ENOENT') {
|
|
4059
|
+
process.stdout.write(`Temporary Admin .p8 was already absent at ${privateKeyPath}.\n`);
|
|
4060
|
+
process.stdout.write('You can also revoke the temporary Admin API key in App Store Connect.\n');
|
|
4061
|
+
return;
|
|
4062
|
+
}
|
|
4063
|
+
process.stdout.write(`Could not delete temporary Admin .p8 at ${privateKeyPath}: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
4064
|
+
}
|
|
4065
|
+
process.stdout.write('You can also revoke the temporary Admin API key in App Store Connect.\n');
|
|
3974
4066
|
}
|
|
3975
4067
|
async function shouldRunSelfUpdate(workspaceRoot, force) {
|
|
3976
4068
|
if (force)
|
|
@@ -4204,13 +4296,16 @@ async function runConnectorSetupSteps({ rl, args, selected, healthByConnector, a
|
|
|
4204
4296
|
if (selected.includes('asc')) {
|
|
4205
4297
|
while (true) {
|
|
4206
4298
|
clearTerminal();
|
|
4207
|
-
await guideAscConnector(rl, secrets);
|
|
4208
|
-
|
|
4299
|
+
const ascSetup = await guideAscConnector(rl, secrets);
|
|
4300
|
+
let bootstrapEnv = ascSetup?.bootstrapEnv || {};
|
|
4301
|
+
let check = await runImmediateConnectorHealthCheck({
|
|
4209
4302
|
rl,
|
|
4210
4303
|
configPath: args.config,
|
|
4211
4304
|
connector: 'asc',
|
|
4212
4305
|
secrets,
|
|
4306
|
+
runtimeEnv: bootstrapEnv,
|
|
4213
4307
|
});
|
|
4308
|
+
await cleanupTemporaryAscBootstrapPrivateKey(bootstrapEnv);
|
|
4214
4309
|
if (!check.retry)
|
|
4215
4310
|
break;
|
|
4216
4311
|
}
|