@analyticscli/growth-engineer 0.1.1-preview.24 → 0.1.1-preview.26
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.
|
@@ -2133,6 +2133,12 @@ function summarizeFailureReason(detail) {
|
|
|
2133
2133
|
if (/ASC Setup Admin key auth failed: .*\.p8 key could not be parsed/i.test(text)) {
|
|
2134
2134
|
return 'ASC Setup Admin key auth failed: the .p8 key could not be parsed';
|
|
2135
2135
|
}
|
|
2136
|
+
if (/ASC Reports key auth failed: .*\.p8 file permissions are too open/i.test(text)) {
|
|
2137
|
+
return 'ASC Reports key auth failed: .p8 file permissions are too open';
|
|
2138
|
+
}
|
|
2139
|
+
if (/ASC Setup Admin key auth failed: .*\.p8 file permissions are too open/i.test(text)) {
|
|
2140
|
+
return 'ASC Setup Admin key auth failed: .p8 file permissions are too open';
|
|
2141
|
+
}
|
|
2136
2142
|
if (/ASC .*\.p8 private key is invalid|invalid private key|failed to parse|sequence truncated|malformed|asn1/i.test(text)) {
|
|
2137
2143
|
return 'ASC auth failed: the .p8 key could not be parsed';
|
|
2138
2144
|
}
|
|
@@ -2178,8 +2184,11 @@ function summarizeFailureFix(connector, blockers) {
|
|
|
2178
2184
|
return 'Paste a Coolify base URL and read-only API token from Keys & Tokens / API tokens, then rerun setup.';
|
|
2179
2185
|
}
|
|
2180
2186
|
if (connector === 'asc') {
|
|
2187
|
+
if (/file permissions are too open|too permissive|chmod 600/i.test(combined)) {
|
|
2188
|
+
return 'Rerun ASC setup. The wizard saves a secure local copy of AuthKey_<KEY_ID>.p8 with chmod 600 before testing.';
|
|
2189
|
+
}
|
|
2181
2190
|
if (/Reports key auth failed|Reports key/i.test(combined) && /private key|could not be parsed|failed to parse|asn1/i.test(combined)) {
|
|
2182
|
-
return 'Use the original downloaded AuthKey_<KEY_ID>.p8 file for the Reports key. The wizard bypasses old asc keychain credentials during setup.';
|
|
2191
|
+
return 'Use the original downloaded AuthKey_<KEY_ID>.p8 file for the Reports key. The wizard bypasses old asc keychain/config credentials during setup.';
|
|
2183
2192
|
}
|
|
2184
2193
|
if (/Setup Admin key auth failed|Admin key/i.test(combined) && /private key|could not be parsed|failed to parse|asn1/i.test(combined)) {
|
|
2185
2194
|
return 'Use the original downloaded AuthKey_<KEY_ID>.p8 file for the Setup Admin key. This key is temporary and should have the Admin role.';
|
|
@@ -2737,6 +2746,15 @@ function inferAscKeyIdFromPrivateKeyPath(filePath) {
|
|
|
2737
2746
|
const match = fileName.match(/^AuthKey_([A-Za-z0-9]+)\.p8$/);
|
|
2738
2747
|
return match?.[1] || '';
|
|
2739
2748
|
}
|
|
2749
|
+
async function copyAscPrivateKeyToSecurePath(sourcePath, keyId, suffix = '') {
|
|
2750
|
+
const destinationPath = resolveAscPrivateKeyPath(keyId, suffix);
|
|
2751
|
+
await fs.mkdir(path.dirname(destinationPath), { recursive: true, mode: 0o700 });
|
|
2752
|
+
if (path.resolve(sourcePath) !== path.resolve(destinationPath)) {
|
|
2753
|
+
await fs.copyFile(sourcePath, destinationPath);
|
|
2754
|
+
}
|
|
2755
|
+
await fs.chmod(destinationPath, 0o600);
|
|
2756
|
+
return destinationPath;
|
|
2757
|
+
}
|
|
2740
2758
|
function renderEnvValue(value) {
|
|
2741
2759
|
return `"${String(value).replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\$/g, '\\$')}"`;
|
|
2742
2760
|
}
|
|
@@ -3977,6 +3995,7 @@ async function guideAscConnector(rl, secrets) {
|
|
|
3977
3995
|
process.stdout.write(`${bold('Enter the Reports key now:')}\n`);
|
|
3978
3996
|
printBullets([
|
|
3979
3997
|
`${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.`,
|
|
3998
|
+
`The wizard saves a secure local copy with ${bold('chmod 600')}.`,
|
|
3980
3999
|
`${bold('Issuer ID')} from the API keys page. Same value for both keys.`,
|
|
3981
4000
|
`${bold('Vendor Number')} from Sales and Trends > Reports.`,
|
|
3982
4001
|
]);
|
|
@@ -3987,11 +4006,13 @@ async function guideAscConnector(rl, secrets) {
|
|
|
3987
4006
|
});
|
|
3988
4007
|
let keyId = normalKeyPath.keyId;
|
|
3989
4008
|
if (normalKeyPath.privateKeyPath) {
|
|
3990
|
-
|
|
4009
|
+
const securePrivateKeyPath = await copyAscPrivateKeyToSecurePath(normalKeyPath.privateKeyPath, keyId);
|
|
4010
|
+
secrets.ASC_PRIVATE_KEY_PATH = securePrivateKeyPath;
|
|
3991
4011
|
secrets.ASC_PRIVATE_KEY = DELETE_SECRET;
|
|
3992
4012
|
secrets.ASC_PRIVATE_KEY_B64 = DELETE_SECRET;
|
|
3993
4013
|
secrets.ASC_KEY_ID = keyId;
|
|
3994
4014
|
process.stdout.write(`Inferred ASC_KEY_ID=${keyId} from ${path.basename(normalKeyPath.privateKeyPath)}.\n`);
|
|
4015
|
+
process.stdout.write(`Saved secure Reports key copy to ${securePrivateKeyPath} with chmod 600.\n`);
|
|
3995
4016
|
}
|
|
3996
4017
|
const issuerId = await ask(rl, 'ASC_ISSUER_ID (same for both keys, empty = skip)', process.env.ASC_ISSUER_ID || '');
|
|
3997
4018
|
if (issuerId.trim())
|
|
@@ -4025,7 +4046,7 @@ async function guideAscBootstrapAdminKey(rl, issuerIdDefault = '') {
|
|
|
4025
4046
|
printBullets([
|
|
4026
4047
|
`${bold('Role must be Admin')} so Apple can create the first App Analytics report request.`,
|
|
4027
4048
|
`${bold('Use original AuthKey_<KEY_ID>.p8 filename')} so KEY_ID is read automatically.`,
|
|
4028
|
-
`${bold('Not saved')} to secrets.env.
|
|
4049
|
+
`${bold('Not saved')} to secrets.env. The temporary secure copy is deleted after setup.`,
|
|
4029
4050
|
]);
|
|
4030
4051
|
const bootstrapKeyPath = await askAscPrivateKeyPathWithKeyId(rl, {
|
|
4031
4052
|
label: 'Setup Admin .p8 path (AuthKey_<KEY_ID>.p8, empty = paste)',
|
|
@@ -4038,11 +4059,11 @@ async function guideAscBootstrapAdminKey(rl, issuerIdDefault = '') {
|
|
|
4038
4059
|
bootstrapIssuerId = await ask(rl, 'ASC_ISSUER_ID (same API keys page)', process.env.ASC_ISSUER_ID || '');
|
|
4039
4060
|
}
|
|
4040
4061
|
if (bootstrapKeyPath.privateKeyPath) {
|
|
4041
|
-
|
|
4062
|
+
const secureBootstrapPath = await copyAscPrivateKeyToSecurePath(bootstrapKeyPath.privateKeyPath, bootstrapKeyId, '_bootstrap_admin');
|
|
4063
|
+
bootstrapEnv.ASC_BOOTSTRAP_PRIVATE_KEY_PATH = secureBootstrapPath;
|
|
4042
4064
|
process.stdout.write(`Inferred ASC_BOOTSTRAP_KEY_ID=${bootstrapKeyId} from ${path.basename(bootstrapKeyPath.privateKeyPath)}.\n`);
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
bootstrapEnv.ASC_BOOTSTRAP_PRIVATE_KEY_DELETE_AFTER_USE = '1';
|
|
4065
|
+
process.stdout.write(`Saved secure temporary Admin key copy to ${secureBootstrapPath} with chmod 600.\n`);
|
|
4066
|
+
bootstrapEnv.ASC_BOOTSTRAP_PRIVATE_KEY_DELETE_AFTER_USE = '1';
|
|
4046
4067
|
}
|
|
4047
4068
|
else {
|
|
4048
4069
|
bootstrapKeyId = await ask(rl, 'ASC_BOOTSTRAP_KEY_ID (from AuthKey_<KEY_ID>.p8)', '');
|