preflightlaunch 0.2.1 → 0.2.2
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.
|
@@ -18839,7 +18839,8 @@ var config = new Conf({
|
|
|
18839
18839
|
userId: { type: "string" },
|
|
18840
18840
|
email: { type: "string" },
|
|
18841
18841
|
hasRunBefore: { type: "boolean", default: false },
|
|
18842
|
-
lastScannedPath: { type: "string" }
|
|
18842
|
+
lastScannedPath: { type: "string" },
|
|
18843
|
+
ascConnected: { type: "boolean", default: false }
|
|
18843
18844
|
}
|
|
18844
18845
|
});
|
|
18845
18846
|
try {
|
|
@@ -18856,7 +18857,8 @@ function getConfig() {
|
|
|
18856
18857
|
userId: config.get("userId"),
|
|
18857
18858
|
email: config.get("email"),
|
|
18858
18859
|
hasRunBefore: config.get("hasRunBefore") || false,
|
|
18859
|
-
lastScannedPath: config.get("lastScannedPath")
|
|
18860
|
+
lastScannedPath: config.get("lastScannedPath"),
|
|
18861
|
+
ascConnected: config.get("ascConnected") || false
|
|
18860
18862
|
};
|
|
18861
18863
|
}
|
|
18862
18864
|
function setTokens(accessToken, refreshToken) {
|
|
@@ -18888,6 +18890,12 @@ function setLastScannedPath(path5) {
|
|
|
18888
18890
|
function getLastScannedPath() {
|
|
18889
18891
|
return config.get("lastScannedPath");
|
|
18890
18892
|
}
|
|
18893
|
+
function getAscConnected() {
|
|
18894
|
+
return config.get("ascConnected") || false;
|
|
18895
|
+
}
|
|
18896
|
+
function setAscConnected(connected) {
|
|
18897
|
+
config.set("ascConnected", connected);
|
|
18898
|
+
}
|
|
18891
18899
|
|
|
18892
18900
|
// ../../node_modules/@supabase/supabase-js/dist/index.mjs
|
|
18893
18901
|
init_esm_shims();
|
|
@@ -32293,11 +32301,66 @@ function ora(options) {
|
|
|
32293
32301
|
return new Ora(options);
|
|
32294
32302
|
}
|
|
32295
32303
|
|
|
32304
|
+
// src/ui/theme.ts
|
|
32305
|
+
init_esm_shims();
|
|
32306
|
+
var brand = source_default.bold.hex("#E8700A");
|
|
32307
|
+
var brandDim = source_default.hex("#E8700A");
|
|
32308
|
+
var heading = source_default.bold.white;
|
|
32309
|
+
var subtext = source_default.dim;
|
|
32310
|
+
var muted = source_default.gray;
|
|
32311
|
+
var ok = source_default.green;
|
|
32312
|
+
var okBold = source_default.bold.green;
|
|
32313
|
+
var warning = source_default.yellow;
|
|
32314
|
+
var warningBold = source_default.bold.yellow;
|
|
32315
|
+
var critical = source_default.red;
|
|
32316
|
+
var criticalBold = source_default.bold.red;
|
|
32317
|
+
var info = source_default.hex("#E8700A");
|
|
32318
|
+
var infoBold = source_default.bold.hex("#E8700A");
|
|
32319
|
+
var icons = {
|
|
32320
|
+
check: ok("\u2714"),
|
|
32321
|
+
cross: critical("\u2716"),
|
|
32322
|
+
warn: warning("!"),
|
|
32323
|
+
info: info("i"),
|
|
32324
|
+
bullet: "\u25CF",
|
|
32325
|
+
circle: "\u25CB",
|
|
32326
|
+
arrow: "\u2192",
|
|
32327
|
+
block: "\u2588",
|
|
32328
|
+
blockDim: source_default.dim("\u2591"),
|
|
32329
|
+
file: "\u{1F4C4}",
|
|
32330
|
+
image: "\u{1F5BC}",
|
|
32331
|
+
plane: "\u{1F6EB}"
|
|
32332
|
+
};
|
|
32333
|
+
function scoreBar(score, width = 20) {
|
|
32334
|
+
const filled = Math.round(score / 100 * width);
|
|
32335
|
+
const empty = width - filled;
|
|
32336
|
+
let color;
|
|
32337
|
+
let label;
|
|
32338
|
+
if (score >= 80) {
|
|
32339
|
+
color = ok;
|
|
32340
|
+
label = "READY";
|
|
32341
|
+
} else if (score >= 60) {
|
|
32342
|
+
color = warning;
|
|
32343
|
+
label = "NEEDS ATTENTION";
|
|
32344
|
+
} else {
|
|
32345
|
+
color = critical;
|
|
32346
|
+
label = "AT RISK";
|
|
32347
|
+
}
|
|
32348
|
+
return `${score}/100 ${color(icons.block.repeat(filled))}${icons.blockDim.repeat(empty)} ${color(label)}`;
|
|
32349
|
+
}
|
|
32350
|
+
function formatBytes(bytes) {
|
|
32351
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
32352
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
32353
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
32354
|
+
}
|
|
32355
|
+
var APP_VERSION = "0.2.0";
|
|
32356
|
+
var APP_NAME = "Preflight";
|
|
32357
|
+
var APP_TAGLINE = "App Store Review Scanner";
|
|
32358
|
+
|
|
32296
32359
|
// src/ui/spinner.ts
|
|
32297
32360
|
function createSpinner(text2) {
|
|
32298
32361
|
return ora({
|
|
32299
32362
|
text: text2,
|
|
32300
|
-
color: "
|
|
32363
|
+
color: "yellow",
|
|
32301
32364
|
spinner: "dots"
|
|
32302
32365
|
});
|
|
32303
32366
|
}
|
|
@@ -33352,61 +33415,6 @@ ${l}
|
|
|
33352
33415
|
}
|
|
33353
33416
|
} }).prompt();
|
|
33354
33417
|
|
|
33355
|
-
// src/ui/theme.ts
|
|
33356
|
-
init_esm_shims();
|
|
33357
|
-
var brand = source_default.bold.cyan;
|
|
33358
|
-
var brandDim = source_default.cyan;
|
|
33359
|
-
var heading = source_default.bold.white;
|
|
33360
|
-
var subtext = source_default.dim;
|
|
33361
|
-
var muted = source_default.gray;
|
|
33362
|
-
var ok = source_default.green;
|
|
33363
|
-
var okBold = source_default.bold.green;
|
|
33364
|
-
var warning = source_default.yellow;
|
|
33365
|
-
var warningBold = source_default.bold.yellow;
|
|
33366
|
-
var critical = source_default.red;
|
|
33367
|
-
var criticalBold = source_default.bold.red;
|
|
33368
|
-
var info = source_default.blue;
|
|
33369
|
-
var infoBold = source_default.bold.blue;
|
|
33370
|
-
var icons = {
|
|
33371
|
-
check: ok("\u2714"),
|
|
33372
|
-
cross: critical("\u2716"),
|
|
33373
|
-
warn: warning("!"),
|
|
33374
|
-
info: info("i"),
|
|
33375
|
-
bullet: "\u25CF",
|
|
33376
|
-
circle: "\u25CB",
|
|
33377
|
-
arrow: "\u2192",
|
|
33378
|
-
block: "\u2588",
|
|
33379
|
-
blockDim: source_default.dim("\u2591"),
|
|
33380
|
-
file: "\u{1F4C4}",
|
|
33381
|
-
image: "\u{1F5BC}",
|
|
33382
|
-
plane: "\u{1F6EB}"
|
|
33383
|
-
};
|
|
33384
|
-
function scoreBar(score, width = 20) {
|
|
33385
|
-
const filled = Math.round(score / 100 * width);
|
|
33386
|
-
const empty = width - filled;
|
|
33387
|
-
let color;
|
|
33388
|
-
let label;
|
|
33389
|
-
if (score >= 80) {
|
|
33390
|
-
color = ok;
|
|
33391
|
-
label = "READY";
|
|
33392
|
-
} else if (score >= 60) {
|
|
33393
|
-
color = warning;
|
|
33394
|
-
label = "NEEDS ATTENTION";
|
|
33395
|
-
} else {
|
|
33396
|
-
color = critical;
|
|
33397
|
-
label = "AT RISK";
|
|
33398
|
-
}
|
|
33399
|
-
return `${score}/100 ${color(icons.block.repeat(filled))}${icons.blockDim.repeat(empty)} ${color(label)}`;
|
|
33400
|
-
}
|
|
33401
|
-
function formatBytes(bytes) {
|
|
33402
|
-
if (bytes < 1024) return `${bytes} B`;
|
|
33403
|
-
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
33404
|
-
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
33405
|
-
}
|
|
33406
|
-
var APP_VERSION = "0.2.0";
|
|
33407
|
-
var APP_NAME = "Preflight";
|
|
33408
|
-
var APP_TAGLINE = "App Store Review Scanner";
|
|
33409
|
-
|
|
33410
33418
|
// src/ui/report.ts
|
|
33411
33419
|
function renderReport(report, items) {
|
|
33412
33420
|
console.log();
|
|
@@ -33428,7 +33436,7 @@ function renderReport(report, items) {
|
|
|
33428
33436
|
for (const item of criticals) {
|
|
33429
33437
|
console.log(` ${critical(item.title)}`);
|
|
33430
33438
|
if (item.fix_suggestion) {
|
|
33431
|
-
console.log(` ${
|
|
33439
|
+
console.log(` ${brandDim(icons.arrow)} ${item.fix_suggestion}`);
|
|
33432
33440
|
}
|
|
33433
33441
|
if (item.description && item.description !== item.title) {
|
|
33434
33442
|
console.log(` ${subtext(item.description)}`);
|
|
@@ -33442,7 +33450,7 @@ function renderReport(report, items) {
|
|
|
33442
33450
|
for (const item of warnings) {
|
|
33443
33451
|
console.log(` ${warning(item.title)}`);
|
|
33444
33452
|
if (item.fix_suggestion) {
|
|
33445
|
-
console.log(` ${
|
|
33453
|
+
console.log(` ${brandDim(icons.arrow)} ${item.fix_suggestion}`);
|
|
33446
33454
|
}
|
|
33447
33455
|
console.log();
|
|
33448
33456
|
}
|
|
@@ -33804,7 +33812,8 @@ var KNOWN_COMMANDS = [
|
|
|
33804
33812
|
{ name: "status", description: "Check analysis status" },
|
|
33805
33813
|
{ name: "report", description: "View analysis report" },
|
|
33806
33814
|
{ name: "history", description: "List past submissions" },
|
|
33807
|
-
{ name: "setup", description: "Run guided setup" }
|
|
33815
|
+
{ name: "setup", description: "Run guided setup" },
|
|
33816
|
+
{ name: "asc", description: "App Store Connect integration" }
|
|
33808
33817
|
];
|
|
33809
33818
|
function levenshtein(a, b) {
|
|
33810
33819
|
const matrix = [];
|
|
@@ -33845,7 +33854,7 @@ function handleUnknownCommand(cmdName) {
|
|
|
33845
33854
|
console.log();
|
|
33846
33855
|
console.log(source_default.dim(" Did you mean:"));
|
|
33847
33856
|
for (const s of suggestions) {
|
|
33848
|
-
console.log(` ${
|
|
33857
|
+
console.log(` ${brandDim("\u2192")} ${brand(`preflight ${s.name}`)} ${source_default.dim(s.description)}`);
|
|
33849
33858
|
}
|
|
33850
33859
|
}
|
|
33851
33860
|
console.log();
|
|
@@ -33918,7 +33927,10 @@ var FEATURE_ITEMS = [
|
|
|
33918
33927
|
{ value: "thirdPartyLogin", label: "Sign in with Apple / Google", hint: "Social login" },
|
|
33919
33928
|
{ value: "aiContent", label: "AI-Generated Content", hint: "ChatGPT, DALL-E, etc." },
|
|
33920
33929
|
{ value: "healthClaims", label: "Health / Medical Advice", hint: "Diagnosis, treatment" },
|
|
33921
|
-
{ value: "crypto", label: "Crypto / NFTs", hint: "Buy, sell, trade" }
|
|
33930
|
+
{ value: "crypto", label: "Crypto / NFTs", hint: "Buy, sell, trade" },
|
|
33931
|
+
{ value: "miniApps", label: "Mini Apps / Plugins", hint: "Hosts mini games, chatbots, or plugins" },
|
|
33932
|
+
{ value: "euDistribution", label: "Available in the EU", hint: "Distributed on EU App Store" },
|
|
33933
|
+
{ value: "externalPayments", label: "External Payment Links (US)", hint: "Links to pay outside Apple" }
|
|
33922
33934
|
];
|
|
33923
33935
|
function calculateAgeRating(answers) {
|
|
33924
33936
|
if (answers.prolongedViolence === 2 || answers.sexualContent === 2 || answers.gamblingSimulated === 2 || answers.gamblingContests > 0) {
|
|
@@ -33932,25 +33944,28 @@ function calculateAgeRating(answers) {
|
|
|
33932
33944
|
}
|
|
33933
33945
|
return "4+";
|
|
33934
33946
|
}
|
|
33935
|
-
async function collectAppDetails(projectName) {
|
|
33936
|
-
|
|
33937
|
-
|
|
33938
|
-
|
|
33939
|
-
|
|
33940
|
-
|
|
33941
|
-
|
|
33942
|
-
|
|
33943
|
-
|
|
33944
|
-
|
|
33945
|
-
|
|
33946
|
-
|
|
33947
|
-
|
|
33948
|
-
|
|
33947
|
+
async function collectAppDetails(projectName, defaults) {
|
|
33948
|
+
if (!defaults) {
|
|
33949
|
+
const skipGate = await select({
|
|
33950
|
+
message: "App Details (you can always add these later on the web)",
|
|
33951
|
+
options: [
|
|
33952
|
+
{ value: "fill", label: "Fill in now", hint: "Name, description, keywords, category" },
|
|
33953
|
+
{ value: "skip", label: "Skip for now", hint: "Just use the project name" }
|
|
33954
|
+
]
|
|
33955
|
+
});
|
|
33956
|
+
if (skipGate === null) return null;
|
|
33957
|
+
if (skipGate === "skip") {
|
|
33958
|
+
return {
|
|
33959
|
+
appName: projectName,
|
|
33960
|
+
signInRequired: false
|
|
33961
|
+
};
|
|
33962
|
+
}
|
|
33949
33963
|
}
|
|
33964
|
+
const defaultName = defaults?.appName || projectName;
|
|
33950
33965
|
const appName = await text({
|
|
33951
33966
|
message: "App Name",
|
|
33952
|
-
placeholder:
|
|
33953
|
-
defaultValue:
|
|
33967
|
+
placeholder: defaultName,
|
|
33968
|
+
defaultValue: defaultName,
|
|
33954
33969
|
validate: (val) => {
|
|
33955
33970
|
if (!val?.trim()) return "App name is required";
|
|
33956
33971
|
}
|
|
@@ -33958,12 +33973,14 @@ async function collectAppDetails(projectName) {
|
|
|
33958
33973
|
if (appName === null) return null;
|
|
33959
33974
|
const description = await text({
|
|
33960
33975
|
message: "Description (press Enter to skip)",
|
|
33961
|
-
placeholder: "Describe your app as it appears in the App Store"
|
|
33976
|
+
placeholder: "Describe your app as it appears in the App Store",
|
|
33977
|
+
...defaults?.description ? { defaultValue: defaults.description } : {}
|
|
33962
33978
|
});
|
|
33963
33979
|
if (description === null) return null;
|
|
33964
33980
|
const keywords = await text({
|
|
33965
33981
|
message: "Keywords (press Enter to skip)",
|
|
33966
33982
|
placeholder: "Comma-separated, 100 chars max",
|
|
33983
|
+
...defaults?.keywords ? { defaultValue: defaults.keywords } : {},
|
|
33967
33984
|
validate: (val) => {
|
|
33968
33985
|
if (val && val.length > 100) return "Keywords must be 100 characters or less";
|
|
33969
33986
|
}
|
|
@@ -33972,6 +33989,7 @@ async function collectAppDetails(projectName) {
|
|
|
33972
33989
|
const promotionalText = await text({
|
|
33973
33990
|
message: "Promotional Text (press Enter to skip)",
|
|
33974
33991
|
placeholder: "Short promotional text, 170 chars max",
|
|
33992
|
+
...defaults?.promotionalText ? { defaultValue: defaults.promotionalText } : {},
|
|
33975
33993
|
validate: (val) => {
|
|
33976
33994
|
if (val && val.length > 170) return "Promotional text must be 170 characters or less";
|
|
33977
33995
|
}
|
|
@@ -33983,20 +34001,26 @@ async function collectAppDetails(projectName) {
|
|
|
33983
34001
|
];
|
|
33984
34002
|
const category = await select({
|
|
33985
34003
|
message: "Primary Category",
|
|
33986
|
-
options: categoryOptions
|
|
34004
|
+
options: categoryOptions,
|
|
34005
|
+
...defaults?.category ? { initialValue: defaults.category } : {}
|
|
33987
34006
|
});
|
|
33988
34007
|
if (category === null) return null;
|
|
33989
34008
|
const supportUrl = await text({
|
|
33990
34009
|
message: "Support URL (press Enter to skip)",
|
|
33991
|
-
placeholder: "https://example.com/support"
|
|
34010
|
+
placeholder: "https://example.com/support",
|
|
34011
|
+
...defaults?.supportUrl ? { defaultValue: defaults.supportUrl } : {}
|
|
33992
34012
|
});
|
|
33993
34013
|
if (supportUrl === null) return null;
|
|
33994
34014
|
const marketingUrl = await text({
|
|
33995
34015
|
message: "Marketing URL (press Enter to skip)",
|
|
33996
|
-
placeholder: "https://example.com"
|
|
34016
|
+
placeholder: "https://example.com",
|
|
34017
|
+
...defaults?.marketingUrl ? { defaultValue: defaults.marketingUrl } : {}
|
|
33997
34018
|
});
|
|
33998
34019
|
if (marketingUrl === null) return null;
|
|
33999
|
-
const signInRequired = await confirm(
|
|
34020
|
+
const signInRequired = await confirm(
|
|
34021
|
+
"Does your app require sign-in for review?",
|
|
34022
|
+
defaults?.signInRequired ?? false
|
|
34023
|
+
);
|
|
34000
34024
|
if (signInRequired === null) return null;
|
|
34001
34025
|
let demoUsername;
|
|
34002
34026
|
let demoPassword;
|
|
@@ -34004,6 +34028,7 @@ async function collectAppDetails(projectName) {
|
|
|
34004
34028
|
const email = await text({
|
|
34005
34029
|
message: "Demo Email",
|
|
34006
34030
|
placeholder: "test@example.com",
|
|
34031
|
+
...defaults?.demoUsername ? { defaultValue: defaults.demoUsername } : {},
|
|
34007
34032
|
validate: (val) => {
|
|
34008
34033
|
if (!val?.trim()) return "Demo email is required when sign-in is required";
|
|
34009
34034
|
}
|
|
@@ -34144,7 +34169,10 @@ async function collectFeatureChecklist() {
|
|
|
34144
34169
|
thirdPartyLogin: selectedFeatures.includes("thirdPartyLogin"),
|
|
34145
34170
|
aiContent: selectedFeatures.includes("aiContent"),
|
|
34146
34171
|
healthClaims: selectedFeatures.includes("healthClaims"),
|
|
34147
|
-
crypto: selectedFeatures.includes("crypto")
|
|
34172
|
+
crypto: selectedFeatures.includes("crypto"),
|
|
34173
|
+
miniApps: selectedFeatures.includes("miniApps"),
|
|
34174
|
+
euDistribution: selectedFeatures.includes("euDistribution"),
|
|
34175
|
+
externalPayments: selectedFeatures.includes("externalPayments")
|
|
34148
34176
|
};
|
|
34149
34177
|
if (checklist.login) {
|
|
34150
34178
|
const hasAccountDeletion = await confirm(
|
|
@@ -34162,9 +34190,41 @@ async function collectFeatureChecklist() {
|
|
|
34162
34190
|
if (hasRestorePurchases === null) return null;
|
|
34163
34191
|
checklist.restorePurchases = hasRestorePurchases;
|
|
34164
34192
|
}
|
|
34193
|
+
if (checklist.ugc) {
|
|
34194
|
+
const hasCreatorAgeGate = await confirm(
|
|
34195
|
+
"Do you verify content creators are 13+ (or local minimum age)?",
|
|
34196
|
+
false
|
|
34197
|
+
);
|
|
34198
|
+
if (hasCreatorAgeGate === null) return null;
|
|
34199
|
+
checklist.creatorAgeGate = hasCreatorAgeGate;
|
|
34200
|
+
}
|
|
34201
|
+
if (checklist.miniApps) {
|
|
34202
|
+
const miniAppsReviewed = await confirm(
|
|
34203
|
+
"Have all mini apps/plugins been individually submitted for Apple review?",
|
|
34204
|
+
false
|
|
34205
|
+
);
|
|
34206
|
+
if (miniAppsReviewed === null) return null;
|
|
34207
|
+
checklist.miniAppsReviewed = miniAppsReviewed;
|
|
34208
|
+
}
|
|
34209
|
+
if (checklist.euDistribution) {
|
|
34210
|
+
const euTraderDeclared = await confirm(
|
|
34211
|
+
"Have you declared your trader status in App Store Connect? (EU DSA requirement)",
|
|
34212
|
+
false
|
|
34213
|
+
);
|
|
34214
|
+
if (euTraderDeclared === null) return null;
|
|
34215
|
+
checklist.euTraderDeclared = euTraderDeclared;
|
|
34216
|
+
}
|
|
34217
|
+
if (checklist.externalPayments) {
|
|
34218
|
+
const externalLinkCompliant = await confirm(
|
|
34219
|
+
"Do you use StoreKit External Link Account API with Apple's disclosure sheet?",
|
|
34220
|
+
false
|
|
34221
|
+
);
|
|
34222
|
+
if (externalLinkCompliant === null) return null;
|
|
34223
|
+
checklist.externalLinkCompliant = externalLinkCompliant;
|
|
34224
|
+
}
|
|
34165
34225
|
return checklist;
|
|
34166
34226
|
}
|
|
34167
|
-
async function collectCompliance() {
|
|
34227
|
+
async function collectCompliance(defaults) {
|
|
34168
34228
|
const ageResult = await collectAgeRating();
|
|
34169
34229
|
if (ageResult === null) return null;
|
|
34170
34230
|
const privacyResult = await collectPrivacyData();
|
|
@@ -34261,7 +34321,77 @@ async function creditPreCheck() {
|
|
|
34261
34321
|
log.warning("Still waiting for credits. You can try again later.");
|
|
34262
34322
|
return false;
|
|
34263
34323
|
}
|
|
34324
|
+
async function offerDraftSave(state) {
|
|
34325
|
+
if (!state.appName) return;
|
|
34326
|
+
const save = await confirm("Save your progress as a draft?", true);
|
|
34327
|
+
if (save === null || !save) return;
|
|
34328
|
+
const s = spinner();
|
|
34329
|
+
s.start("Saving draft...");
|
|
34330
|
+
try {
|
|
34331
|
+
const body = { app_name: state.appName };
|
|
34332
|
+
if (state.description) body.description = state.description;
|
|
34333
|
+
if (state.keywords) body.keywords = state.keywords;
|
|
34334
|
+
if (state.category) body.category = state.category;
|
|
34335
|
+
if (state.supportUrl) body.support_url = state.supportUrl;
|
|
34336
|
+
if (state.promotionalText) body.promotional_text = state.promotionalText;
|
|
34337
|
+
if (state.marketingUrl) body.marketing_url = state.marketingUrl;
|
|
34338
|
+
if (state.signInRequired != null) body.sign_in_required = state.signInRequired;
|
|
34339
|
+
if (state.demoUsername) body.demo_username = state.demoUsername;
|
|
34340
|
+
if (state.demoPassword) body.demo_password = state.demoPassword;
|
|
34341
|
+
if (state.compliance) Object.assign(body, formatComplianceForApi(state.compliance));
|
|
34342
|
+
const res = await apiRequest("/api/submissions", {
|
|
34343
|
+
method: "POST",
|
|
34344
|
+
body: JSON.stringify(body)
|
|
34345
|
+
});
|
|
34346
|
+
s.stop(res.ok ? "Draft saved" : "Could not save draft");
|
|
34347
|
+
if (res.ok) {
|
|
34348
|
+
log.success("Draft saved. Resume it from View Reviews anytime.");
|
|
34349
|
+
}
|
|
34350
|
+
} catch {
|
|
34351
|
+
s.stop("Could not save draft");
|
|
34352
|
+
}
|
|
34353
|
+
}
|
|
34354
|
+
async function offerAscAutofill(appDetails) {
|
|
34355
|
+
const ascConnected = getAscConnected();
|
|
34356
|
+
if (!ascConnected) return appDetails;
|
|
34357
|
+
try {
|
|
34358
|
+
const statusRes = await apiRequest("/api/asc/connect");
|
|
34359
|
+
if (!statusRes.ok) return appDetails;
|
|
34360
|
+
const statusData = await statusRes.json();
|
|
34361
|
+
if (!statusData.connected || !statusData.appId) return appDetails;
|
|
34362
|
+
const useAutofill = await confirm(
|
|
34363
|
+
`Autofill from App Store Connect? (${statusData.appName || "Connected app"})`,
|
|
34364
|
+
true
|
|
34365
|
+
);
|
|
34366
|
+
if (useAutofill === null || !useAutofill) return appDetails;
|
|
34367
|
+
const s = spinner();
|
|
34368
|
+
s.start("Fetching from App Store Connect...");
|
|
34369
|
+
const autofillRes = await apiRequest("/api/asc/autofill", {
|
|
34370
|
+
method: "POST",
|
|
34371
|
+
body: JSON.stringify({ appId: statusData.appId })
|
|
34372
|
+
});
|
|
34373
|
+
const autofillData = await autofillRes.json();
|
|
34374
|
+
s.stop(autofillRes.ok ? "Autofill complete" : "Autofill failed");
|
|
34375
|
+
if (autofillRes.ok && autofillData) {
|
|
34376
|
+
return {
|
|
34377
|
+
appName: appDetails.appName || autofillData.app_name || appDetails.appName,
|
|
34378
|
+
description: appDetails.description || autofillData.description,
|
|
34379
|
+
keywords: appDetails.keywords || autofillData.keywords,
|
|
34380
|
+
category: appDetails.category || autofillData.category,
|
|
34381
|
+
supportUrl: appDetails.supportUrl || autofillData.support_url,
|
|
34382
|
+
promotionalText: appDetails.promotionalText || autofillData.promotional_text,
|
|
34383
|
+
marketingUrl: appDetails.marketingUrl || autofillData.marketing_url,
|
|
34384
|
+
signInRequired: appDetails.signInRequired,
|
|
34385
|
+
demoUsername: appDetails.demoUsername,
|
|
34386
|
+
demoPassword: appDetails.demoPassword
|
|
34387
|
+
};
|
|
34388
|
+
}
|
|
34389
|
+
} catch {
|
|
34390
|
+
}
|
|
34391
|
+
return appDetails;
|
|
34392
|
+
}
|
|
34264
34393
|
async function submitCommand(path5, options = {}, fromMenu = false) {
|
|
34394
|
+
const draftState = {};
|
|
34265
34395
|
if (!isLoggedIn()) {
|
|
34266
34396
|
if (fromMenu) {
|
|
34267
34397
|
log.error("Not logged in.");
|
|
@@ -34298,6 +34428,7 @@ async function submitCommand(path5, options = {}, fromMenu = false) {
|
|
|
34298
34428
|
const detected = scanProject(dir);
|
|
34299
34429
|
const projectName = detected.projectName || "Unknown App";
|
|
34300
34430
|
let appName = options.appName || projectName;
|
|
34431
|
+
draftState.appName = appName;
|
|
34301
34432
|
if (options.plist) detected.infoPlist = resolve3(options.plist);
|
|
34302
34433
|
if (options.manifest) detected.privacyManifest = resolve3(options.manifest);
|
|
34303
34434
|
if (options.ipa) detected.ipa = resolve3(options.ipa);
|
|
@@ -34335,13 +34466,34 @@ async function submitCommand(path5, options = {}, fromMenu = false) {
|
|
|
34335
34466
|
{ value: "full", label: "Full review (add app details + compliance info)", hint: "More thorough" }
|
|
34336
34467
|
]
|
|
34337
34468
|
});
|
|
34338
|
-
if (reviewType === null)
|
|
34469
|
+
if (reviewType === null) {
|
|
34470
|
+
if (fromMenu) await offerDraftSave(draftState);
|
|
34471
|
+
return;
|
|
34472
|
+
}
|
|
34339
34473
|
if (reviewType === "full") {
|
|
34340
34474
|
appDetails = await collectAppDetails(projectName);
|
|
34341
|
-
if (appDetails === null)
|
|
34475
|
+
if (appDetails === null) {
|
|
34476
|
+
if (fromMenu) await offerDraftSave(draftState);
|
|
34477
|
+
return;
|
|
34478
|
+
}
|
|
34342
34479
|
appName = appDetails.appName;
|
|
34480
|
+
draftState.appName = appName;
|
|
34481
|
+
draftState.description = appDetails.description;
|
|
34482
|
+
draftState.keywords = appDetails.keywords;
|
|
34483
|
+
draftState.category = appDetails.category;
|
|
34484
|
+
draftState.supportUrl = appDetails.supportUrl;
|
|
34485
|
+
draftState.promotionalText = appDetails.promotionalText;
|
|
34486
|
+
draftState.marketingUrl = appDetails.marketingUrl;
|
|
34487
|
+
draftState.signInRequired = appDetails.signInRequired;
|
|
34488
|
+
draftState.demoUsername = appDetails.demoUsername;
|
|
34489
|
+
draftState.demoPassword = appDetails.demoPassword;
|
|
34490
|
+
appDetails = await offerAscAutofill(appDetails);
|
|
34343
34491
|
compliance = await collectCompliance();
|
|
34344
|
-
if (compliance === null)
|
|
34492
|
+
if (compliance === null) {
|
|
34493
|
+
if (fromMenu) await offerDraftSave(draftState);
|
|
34494
|
+
return;
|
|
34495
|
+
}
|
|
34496
|
+
draftState.compliance = compliance;
|
|
34345
34497
|
}
|
|
34346
34498
|
}
|
|
34347
34499
|
if (fromMenu) {
|
|
@@ -34354,7 +34506,10 @@ async function submitCommand(path5, options = {}, fromMenu = false) {
|
|
|
34354
34506
|
{ value: "cancel", label: "Cancel", hint: "Back to menu" }
|
|
34355
34507
|
]
|
|
34356
34508
|
});
|
|
34357
|
-
if (action === null || action === "cancel")
|
|
34509
|
+
if (action === null || action === "cancel") {
|
|
34510
|
+
if (fromMenu) await offerDraftSave(draftState);
|
|
34511
|
+
return;
|
|
34512
|
+
}
|
|
34358
34513
|
} else {
|
|
34359
34514
|
if (!fromMenu) {
|
|
34360
34515
|
intro(`Submit ${appName} for analysis`);
|
|
@@ -34490,8 +34645,15 @@ async function submitCommand(path5, options = {}, fromMenu = false) {
|
|
|
34490
34645
|
log.error("Could not finalize after multiple attempts. Your files are saved -- try again later.");
|
|
34491
34646
|
return;
|
|
34492
34647
|
}
|
|
34493
|
-
analyzeSpinner.text = "AI review in progress...";
|
|
34648
|
+
analyzeSpinner.text = "AI review in progress... (press Esc to stop waiting)";
|
|
34494
34649
|
const reportData = await pollForReport(submissionId, analyzeSpinner);
|
|
34650
|
+
if (reportData.status === "cancelled") {
|
|
34651
|
+
analyzeSpinner.stop();
|
|
34652
|
+
log.info("Analysis continues in the background.");
|
|
34653
|
+
console.log(subtext(` Check status with ${brand(`preflight status ${submissionId}`)} or from View Reviews.`));
|
|
34654
|
+
console.log();
|
|
34655
|
+
return;
|
|
34656
|
+
}
|
|
34495
34657
|
analyzeSpinner.succeed("Analysis complete!");
|
|
34496
34658
|
if (reportData.status === "complete" && reportData.data) {
|
|
34497
34659
|
if (options.json) {
|
|
@@ -34526,6 +34688,186 @@ async function submitCommand(path5, options = {}, fromMenu = false) {
|
|
|
34526
34688
|
if (!fromMenu) process.exitCode = 1;
|
|
34527
34689
|
}
|
|
34528
34690
|
}
|
|
34691
|
+
async function resumeSubmitCommand(draft) {
|
|
34692
|
+
const submissionId = draft.id;
|
|
34693
|
+
log.step(`Resuming draft: ${draft.app_name || "Unknown"}`);
|
|
34694
|
+
console.log();
|
|
34695
|
+
const lastPath = draft.project_path;
|
|
34696
|
+
let path5;
|
|
34697
|
+
if (lastPath) {
|
|
34698
|
+
const useLast = await confirm(`Use previous project path? (${lastPath})`, true);
|
|
34699
|
+
if (useLast === null) return;
|
|
34700
|
+
if (useLast) path5 = lastPath;
|
|
34701
|
+
}
|
|
34702
|
+
if (!path5) {
|
|
34703
|
+
const resolvedPath = await interactiveProjectSelect();
|
|
34704
|
+
if (!resolvedPath) return;
|
|
34705
|
+
path5 = resolvedPath;
|
|
34706
|
+
}
|
|
34707
|
+
const dir = resolve3(path5);
|
|
34708
|
+
setLastScannedPath(dir);
|
|
34709
|
+
const detected = scanProject(dir);
|
|
34710
|
+
const projectName = detected.projectName || "Unknown App";
|
|
34711
|
+
const filesToUpload = [];
|
|
34712
|
+
if (detected.infoPlist) {
|
|
34713
|
+
filesToUpload.push({ type: "plist", filename: "Info.plist", path: detected.infoPlist });
|
|
34714
|
+
}
|
|
34715
|
+
if (detected.privacyManifest) {
|
|
34716
|
+
filesToUpload.push({ type: "manifest", filename: "PrivacyInfo.xcprivacy", path: detected.privacyManifest });
|
|
34717
|
+
}
|
|
34718
|
+
if (detected.ipa) {
|
|
34719
|
+
filesToUpload.push({ type: "ipa", filename: basename3(detected.ipa), path: detected.ipa });
|
|
34720
|
+
}
|
|
34721
|
+
for (let i = 0; i < Math.min(detected.screenshots.length, 10); i++) {
|
|
34722
|
+
filesToUpload.push({
|
|
34723
|
+
type: "screenshot",
|
|
34724
|
+
index: i,
|
|
34725
|
+
filename: basename3(detected.screenshots[i]),
|
|
34726
|
+
path: detected.screenshots[i]
|
|
34727
|
+
});
|
|
34728
|
+
}
|
|
34729
|
+
if (filesToUpload.length === 0) {
|
|
34730
|
+
log.warning("No files found. Make sure you're pointing to an Xcode project directory.");
|
|
34731
|
+
return;
|
|
34732
|
+
}
|
|
34733
|
+
const draftDefaults = {
|
|
34734
|
+
appName: draft.app_name || projectName,
|
|
34735
|
+
description: draft.description,
|
|
34736
|
+
keywords: draft.keywords,
|
|
34737
|
+
category: draft.category,
|
|
34738
|
+
supportUrl: draft.support_url,
|
|
34739
|
+
promotionalText: draft.promotional_text,
|
|
34740
|
+
marketingUrl: draft.marketing_url,
|
|
34741
|
+
signInRequired: draft.sign_in_required ?? false,
|
|
34742
|
+
demoUsername: draft.demo_username,
|
|
34743
|
+
demoPassword: draft.demo_password
|
|
34744
|
+
};
|
|
34745
|
+
const appDetails = await collectAppDetails(projectName, draftDefaults);
|
|
34746
|
+
if (appDetails === null) return;
|
|
34747
|
+
const appName = appDetails.appName;
|
|
34748
|
+
const compliance = await collectCompliance();
|
|
34749
|
+
if (compliance === null) return;
|
|
34750
|
+
console.log();
|
|
34751
|
+
note(buildSummary(appName, dir, filesToUpload, compliance), "Review Summary");
|
|
34752
|
+
const action = await select({
|
|
34753
|
+
message: `Submit review? (100 credits)`,
|
|
34754
|
+
options: [
|
|
34755
|
+
{ value: "submit", label: "Submit review", hint: "100 credits will be deducted" },
|
|
34756
|
+
{ value: "cancel", label: "Cancel", hint: "Back to menu" }
|
|
34757
|
+
]
|
|
34758
|
+
});
|
|
34759
|
+
if (action === null || action === "cancel") return;
|
|
34760
|
+
log.info(subtext("Reviews usually take 1-3 minutes."));
|
|
34761
|
+
console.log();
|
|
34762
|
+
const spinner2 = createSpinner("Updating submission...");
|
|
34763
|
+
spinner2.start();
|
|
34764
|
+
let activeSpinner = spinner2;
|
|
34765
|
+
try {
|
|
34766
|
+
const submissionBody = {
|
|
34767
|
+
submission_id: submissionId,
|
|
34768
|
+
app_name: appName
|
|
34769
|
+
};
|
|
34770
|
+
if (appDetails.description) submissionBody.description = appDetails.description;
|
|
34771
|
+
if (appDetails.keywords) submissionBody.keywords = appDetails.keywords;
|
|
34772
|
+
if (appDetails.category) submissionBody.category = appDetails.category;
|
|
34773
|
+
if (appDetails.supportUrl) submissionBody.support_url = appDetails.supportUrl;
|
|
34774
|
+
if (appDetails.promotionalText) submissionBody.promotional_text = appDetails.promotionalText;
|
|
34775
|
+
if (appDetails.marketingUrl) submissionBody.marketing_url = appDetails.marketingUrl;
|
|
34776
|
+
submissionBody.sign_in_required = appDetails.signInRequired;
|
|
34777
|
+
if (appDetails.demoUsername) submissionBody.demo_username = appDetails.demoUsername;
|
|
34778
|
+
if (appDetails.demoPassword) submissionBody.demo_password = appDetails.demoPassword;
|
|
34779
|
+
if (compliance) {
|
|
34780
|
+
Object.assign(submissionBody, formatComplianceForApi(compliance));
|
|
34781
|
+
}
|
|
34782
|
+
const createRes = await apiRequest("/api/submissions", {
|
|
34783
|
+
method: "POST",
|
|
34784
|
+
body: JSON.stringify(submissionBody)
|
|
34785
|
+
});
|
|
34786
|
+
const createData = await createRes.json();
|
|
34787
|
+
if (!createRes.ok) {
|
|
34788
|
+
spinner2.stop();
|
|
34789
|
+
log.error(createData.message || "Failed to update submission");
|
|
34790
|
+
return;
|
|
34791
|
+
}
|
|
34792
|
+
const finalId = createData.submissionId || submissionId;
|
|
34793
|
+
spinner2.succeed("Submission updated");
|
|
34794
|
+
const uploadSpinner = createSpinner("Getting upload URLs...");
|
|
34795
|
+
uploadSpinner.start();
|
|
34796
|
+
activeSpinner = uploadSpinner;
|
|
34797
|
+
const urlsRes = await apiRequest(`/api/submissions/${finalId}/upload-urls`, {
|
|
34798
|
+
method: "POST",
|
|
34799
|
+
body: JSON.stringify({
|
|
34800
|
+
files: filesToUpload.map((f) => ({
|
|
34801
|
+
type: f.type,
|
|
34802
|
+
index: f.index,
|
|
34803
|
+
filename: f.filename
|
|
34804
|
+
}))
|
|
34805
|
+
})
|
|
34806
|
+
});
|
|
34807
|
+
const urlsData = await urlsRes.json();
|
|
34808
|
+
if (!urlsRes.ok) {
|
|
34809
|
+
uploadSpinner.stop();
|
|
34810
|
+
log.error(urlsData.message || "Failed to get upload URLs");
|
|
34811
|
+
return;
|
|
34812
|
+
}
|
|
34813
|
+
for (let i = 0; i < urlsData.urls.length; i++) {
|
|
34814
|
+
const urlInfo = urlsData.urls[i];
|
|
34815
|
+
const fileInfo = filesToUpload[i];
|
|
34816
|
+
const fileBuffer = readFileSync(fileInfo.path);
|
|
34817
|
+
const fileSize = fileBuffer.length;
|
|
34818
|
+
uploadSpinner.text = `Uploading ${fileInfo.filename} (${formatBytes(fileSize)})...`;
|
|
34819
|
+
const uploadRes = await fetch(urlInfo.signedUrl, {
|
|
34820
|
+
method: "PUT",
|
|
34821
|
+
body: fileBuffer,
|
|
34822
|
+
headers: { "Content-Type": "application/octet-stream" }
|
|
34823
|
+
});
|
|
34824
|
+
if (!uploadRes.ok) {
|
|
34825
|
+
uploadSpinner.stop();
|
|
34826
|
+
log.error(`Failed to upload ${fileInfo.filename}: HTTP ${uploadRes.status} ${uploadRes.statusText}`);
|
|
34827
|
+
return;
|
|
34828
|
+
}
|
|
34829
|
+
}
|
|
34830
|
+
uploadSpinner.succeed("Files uploaded");
|
|
34831
|
+
const analyzeSpinner = createSpinner("Starting analysis...");
|
|
34832
|
+
analyzeSpinner.start();
|
|
34833
|
+
activeSpinner = analyzeSpinner;
|
|
34834
|
+
const finalizeRes = await apiRequest(`/api/submissions/${finalId}/finalize`, {
|
|
34835
|
+
method: "POST",
|
|
34836
|
+
body: JSON.stringify({
|
|
34837
|
+
files: filesToUpload.map((f) => ({ type: f.type, index: f.index }))
|
|
34838
|
+
})
|
|
34839
|
+
});
|
|
34840
|
+
const finalizeData = await finalizeRes.json();
|
|
34841
|
+
if (!finalizeRes.ok) {
|
|
34842
|
+
analyzeSpinner.stop();
|
|
34843
|
+
log.error(finalizeData.message || "Failed to start analysis");
|
|
34844
|
+
return;
|
|
34845
|
+
}
|
|
34846
|
+
analyzeSpinner.text = "AI review in progress... (press Esc to stop waiting)";
|
|
34847
|
+
const reportData = await pollForReport(finalId, analyzeSpinner);
|
|
34848
|
+
if (reportData.status === "cancelled") {
|
|
34849
|
+
analyzeSpinner.stop();
|
|
34850
|
+
log.info("Analysis continues in the background.");
|
|
34851
|
+
console.log(subtext(` Check status with ${brand(`preflight status ${finalId}`)} or from View Reviews.`));
|
|
34852
|
+
console.log();
|
|
34853
|
+
return;
|
|
34854
|
+
}
|
|
34855
|
+
analyzeSpinner.succeed("Analysis complete!");
|
|
34856
|
+
if (reportData.status === "complete" && reportData.data) {
|
|
34857
|
+
renderReport(reportData.data.report, reportData.data.items);
|
|
34858
|
+
console.log(subtext(` Full report: https://preflightlaunch.com/report/${reportData.data.report.id}`));
|
|
34859
|
+
console.log();
|
|
34860
|
+
} else if (reportData.status === "failed") {
|
|
34861
|
+
log.error("Analysis failed. Please try submitting again or contact support.");
|
|
34862
|
+
} else {
|
|
34863
|
+
log.warning("Analysis is still running. Check status with:");
|
|
34864
|
+
console.log(subtext(` preflight status ${finalId}`));
|
|
34865
|
+
}
|
|
34866
|
+
} catch (err) {
|
|
34867
|
+
activeSpinner.stop();
|
|
34868
|
+
log.error(`Resume failed: ${err instanceof Error ? err.message : "Unknown error"}`);
|
|
34869
|
+
}
|
|
34870
|
+
}
|
|
34529
34871
|
function buildSummary(appName, dir, files, compliance) {
|
|
34530
34872
|
const home = __require("os").homedir();
|
|
34531
34873
|
const shortDir = dir.startsWith(home) ? "~" + dir.slice(home.length) : dir;
|
|
@@ -34553,42 +34895,71 @@ function getFileSize(filePath) {
|
|
|
34553
34895
|
async function pollForReport(submissionId, spinner2, maxAttempts = 60, interval = 5e3) {
|
|
34554
34896
|
let consecutiveFailures = 0;
|
|
34555
34897
|
const startTime = Date.now();
|
|
34556
|
-
|
|
34557
|
-
|
|
34558
|
-
|
|
34559
|
-
|
|
34560
|
-
|
|
34561
|
-
|
|
34562
|
-
|
|
34898
|
+
let cancelled = false;
|
|
34899
|
+
const onKeypress = (data) => {
|
|
34900
|
+
if (data[0] === 27 || data[0] === 3) {
|
|
34901
|
+
cancelled = true;
|
|
34902
|
+
}
|
|
34903
|
+
};
|
|
34904
|
+
if (process.stdin.isTTY) {
|
|
34905
|
+
process.stdin.setRawMode(true);
|
|
34906
|
+
process.stdin.resume();
|
|
34907
|
+
process.stdin.on("data", onKeypress);
|
|
34908
|
+
}
|
|
34909
|
+
try {
|
|
34910
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
34911
|
+
for (let w3 = 0; w3 < interval / 500; w3++) {
|
|
34912
|
+
if (cancelled) return { status: "cancelled" };
|
|
34913
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
34563
34914
|
}
|
|
34564
|
-
if (
|
|
34565
|
-
|
|
34915
|
+
if (cancelled) return { status: "cancelled" };
|
|
34916
|
+
const res = await apiRequest(`/api/submissions/${submissionId}`);
|
|
34917
|
+
if (!res.ok) {
|
|
34918
|
+
consecutiveFailures++;
|
|
34919
|
+
if (res.status === 401) {
|
|
34920
|
+
throw new Error("Session expired. Please run `preflight login` to re-authenticate.");
|
|
34921
|
+
}
|
|
34922
|
+
if (consecutiveFailures >= 3) {
|
|
34923
|
+
throw new Error(`Polling failed after 3 consecutive errors (last status: HTTP ${res.status})`);
|
|
34924
|
+
}
|
|
34925
|
+
continue;
|
|
34566
34926
|
}
|
|
34567
|
-
|
|
34568
|
-
|
|
34569
|
-
|
|
34570
|
-
|
|
34571
|
-
|
|
34572
|
-
|
|
34573
|
-
|
|
34574
|
-
|
|
34575
|
-
|
|
34576
|
-
|
|
34577
|
-
|
|
34578
|
-
|
|
34579
|
-
|
|
34927
|
+
consecutiveFailures = 0;
|
|
34928
|
+
const data = await res.json();
|
|
34929
|
+
const submission = data.data;
|
|
34930
|
+
const elapsed = Math.round((Date.now() - startTime) / 1e3);
|
|
34931
|
+
spinner2.text = `AI review in progress... (${elapsed}s elapsed, press Esc to stop waiting)`;
|
|
34932
|
+
if (submission.status === "complete") {
|
|
34933
|
+
if (submission.report_id) {
|
|
34934
|
+
const reportRes = await apiRequest(`/api/reports/${submission.report_id}`);
|
|
34935
|
+
const reportData = await reportRes.json();
|
|
34936
|
+
return { status: "complete", data: reportData };
|
|
34937
|
+
}
|
|
34938
|
+
return { status: "failed" };
|
|
34939
|
+
}
|
|
34940
|
+
if (submission.status === "failed") {
|
|
34941
|
+
return { status: "failed" };
|
|
34580
34942
|
}
|
|
34581
|
-
return { status: "failed" };
|
|
34582
34943
|
}
|
|
34583
|
-
|
|
34584
|
-
|
|
34944
|
+
return { status: "timeout" };
|
|
34945
|
+
} finally {
|
|
34946
|
+
if (process.stdin.isTTY) {
|
|
34947
|
+
process.stdin.removeListener("data", onKeypress);
|
|
34948
|
+
process.stdin.setRawMode(false);
|
|
34949
|
+
process.stdin.pause();
|
|
34585
34950
|
}
|
|
34586
34951
|
}
|
|
34587
|
-
return { status: "timeout" };
|
|
34588
34952
|
}
|
|
34589
34953
|
|
|
34590
34954
|
export {
|
|
34591
34955
|
source_default,
|
|
34956
|
+
brand,
|
|
34957
|
+
brandDim,
|
|
34958
|
+
subtext,
|
|
34959
|
+
ok,
|
|
34960
|
+
warning,
|
|
34961
|
+
critical,
|
|
34962
|
+
icons,
|
|
34592
34963
|
createSpinner,
|
|
34593
34964
|
success,
|
|
34594
34965
|
error,
|
|
@@ -34599,27 +34970,24 @@ export {
|
|
|
34599
34970
|
hasRunBefore,
|
|
34600
34971
|
markAsRun,
|
|
34601
34972
|
setLastScannedPath,
|
|
34973
|
+
setAscConnected,
|
|
34602
34974
|
loginWithBrowser,
|
|
34603
34975
|
logout,
|
|
34604
34976
|
apiRequest,
|
|
34605
34977
|
scanProject,
|
|
34606
|
-
brand,
|
|
34607
|
-
subtext,
|
|
34608
|
-
ok,
|
|
34609
|
-
warning,
|
|
34610
|
-
critical,
|
|
34611
|
-
icons,
|
|
34612
34978
|
intro,
|
|
34613
34979
|
tip,
|
|
34614
34980
|
renderHeader,
|
|
34615
34981
|
select,
|
|
34616
34982
|
confirm,
|
|
34983
|
+
text,
|
|
34617
34984
|
spinner,
|
|
34618
34985
|
log,
|
|
34619
34986
|
interactiveProjectSelect,
|
|
34620
34987
|
renderReport,
|
|
34621
34988
|
renderReportJson,
|
|
34622
34989
|
handleUnknownCommand,
|
|
34623
|
-
submitCommand
|
|
34990
|
+
submitCommand,
|
|
34991
|
+
resumeSubmitCommand
|
|
34624
34992
|
};
|
|
34625
|
-
//# sourceMappingURL=chunk-
|
|
34993
|
+
//# sourceMappingURL=chunk-26P7VL2P.js.map
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
DEFAULT_API_URL,
|
|
6
6
|
apiRequest,
|
|
7
7
|
brand,
|
|
8
|
+
brandDim,
|
|
8
9
|
clearAuth,
|
|
9
10
|
confirm,
|
|
10
11
|
createSpinner,
|
|
@@ -25,17 +26,20 @@ import {
|
|
|
25
26
|
renderHeader,
|
|
26
27
|
renderReport,
|
|
27
28
|
renderReportJson,
|
|
29
|
+
resumeSubmitCommand,
|
|
28
30
|
scanProject,
|
|
29
31
|
select,
|
|
32
|
+
setAscConnected,
|
|
30
33
|
setLastScannedPath,
|
|
31
34
|
source_default,
|
|
32
35
|
spinner,
|
|
33
36
|
submitCommand,
|
|
34
37
|
subtext,
|
|
35
38
|
success,
|
|
39
|
+
text,
|
|
36
40
|
tip,
|
|
37
41
|
warning
|
|
38
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-26P7VL2P.js";
|
|
39
43
|
import {
|
|
40
44
|
__commonJS,
|
|
41
45
|
__require,
|
|
@@ -3094,9 +3098,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3094
3098
|
helpWidth: context.helpWidth,
|
|
3095
3099
|
outputHasColors: context.hasColors
|
|
3096
3100
|
});
|
|
3097
|
-
const
|
|
3098
|
-
if (context.hasColors) return
|
|
3099
|
-
return this._outputConfiguration.stripColor(
|
|
3101
|
+
const text2 = helper.formatHelp(this, helper);
|
|
3102
|
+
if (context.hasColors) return text2;
|
|
3103
|
+
return this._outputConfiguration.stripColor(text2);
|
|
3100
3104
|
}
|
|
3101
3105
|
/**
|
|
3102
3106
|
* @typedef HelpContext
|
|
@@ -3250,7 +3254,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3250
3254
|
* @param {(string | Function)} text - string to add, or a function returning a string
|
|
3251
3255
|
* @return {Command} `this` command for chaining
|
|
3252
3256
|
*/
|
|
3253
|
-
addHelpText(position,
|
|
3257
|
+
addHelpText(position, text2) {
|
|
3254
3258
|
const allowedValues = ["beforeAll", "before", "after", "afterAll"];
|
|
3255
3259
|
if (!allowedValues.includes(position)) {
|
|
3256
3260
|
throw new Error(`Unexpected value for position to addHelpText.
|
|
@@ -3259,10 +3263,10 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3259
3263
|
const helpEvent = `${position}Help`;
|
|
3260
3264
|
this.on(helpEvent, (context) => {
|
|
3261
3265
|
let helpStr;
|
|
3262
|
-
if (typeof
|
|
3263
|
-
helpStr =
|
|
3266
|
+
if (typeof text2 === "function") {
|
|
3267
|
+
helpStr = text2({ error: context.error, command: context.command });
|
|
3264
3268
|
} else {
|
|
3265
|
-
helpStr =
|
|
3269
|
+
helpStr = text2;
|
|
3266
3270
|
}
|
|
3267
3271
|
if (helpStr) {
|
|
3268
3272
|
context.write(`${helpStr}
|
|
@@ -3773,11 +3777,11 @@ var require_utils = __commonJS({
|
|
|
3773
3777
|
}
|
|
3774
3778
|
return output;
|
|
3775
3779
|
}
|
|
3776
|
-
function hyperlink(url,
|
|
3780
|
+
function hyperlink(url, text2) {
|
|
3777
3781
|
const OSC = "\x1B]";
|
|
3778
3782
|
const BEL = "\x07";
|
|
3779
3783
|
const SEP = ";";
|
|
3780
|
-
return [OSC, "8", SEP, SEP, url ||
|
|
3784
|
+
return [OSC, "8", SEP, SEP, url || text2, BEL, text2, OSC, "8", SEP, SEP, BEL].join("");
|
|
3781
3785
|
}
|
|
3782
3786
|
module.exports = {
|
|
3783
3787
|
strlen,
|
|
@@ -3978,10 +3982,10 @@ var require_trap = __commonJS({
|
|
|
3978
3982
|
"../../node_modules/@colors/colors/lib/custom/trap.js"(exports, module) {
|
|
3979
3983
|
"use strict";
|
|
3980
3984
|
init_esm_shims();
|
|
3981
|
-
module["exports"] = function runTheTrap(
|
|
3985
|
+
module["exports"] = function runTheTrap(text2, options) {
|
|
3982
3986
|
var result = "";
|
|
3983
|
-
|
|
3984
|
-
|
|
3987
|
+
text2 = text2 || "Run the trap, drop the bass";
|
|
3988
|
+
text2 = text2.split("");
|
|
3985
3989
|
var trap = {
|
|
3986
3990
|
a: ["@", "\u0104", "\u023A", "\u0245", "\u0394", "\u039B", "\u0414"],
|
|
3987
3991
|
b: ["\xDF", "\u0181", "\u0243", "\u026E", "\u03B2", "\u0E3F"],
|
|
@@ -4029,7 +4033,7 @@ var require_trap = __commonJS({
|
|
|
4029
4033
|
y: ["\xA5", "\u04B0", "\u04CB"],
|
|
4030
4034
|
z: ["\u01B5", "\u0240"]
|
|
4031
4035
|
};
|
|
4032
|
-
|
|
4036
|
+
text2.forEach(function(c) {
|
|
4033
4037
|
c = c.toLowerCase();
|
|
4034
4038
|
var chars = trap[c] || [" "];
|
|
4035
4039
|
var rand = Math.floor(Math.random() * chars.length);
|
|
@@ -4049,8 +4053,8 @@ var require_zalgo = __commonJS({
|
|
|
4049
4053
|
"../../node_modules/@colors/colors/lib/custom/zalgo.js"(exports, module) {
|
|
4050
4054
|
"use strict";
|
|
4051
4055
|
init_esm_shims();
|
|
4052
|
-
module["exports"] = function zalgo(
|
|
4053
|
-
|
|
4056
|
+
module["exports"] = function zalgo(text2, options) {
|
|
4057
|
+
text2 = text2 || " he is here ";
|
|
4054
4058
|
var soul = {
|
|
4055
4059
|
"up": [
|
|
4056
4060
|
"\u030D",
|
|
@@ -4183,7 +4187,7 @@ var require_zalgo = __commonJS({
|
|
|
4183
4187
|
});
|
|
4184
4188
|
return bool;
|
|
4185
4189
|
}
|
|
4186
|
-
function heComes(
|
|
4190
|
+
function heComes(text3, options2) {
|
|
4187
4191
|
var result = "";
|
|
4188
4192
|
var counts;
|
|
4189
4193
|
var l;
|
|
@@ -4192,12 +4196,12 @@ var require_zalgo = __commonJS({
|
|
|
4192
4196
|
options2["mid"] = typeof options2["mid"] !== "undefined" ? options2["mid"] : true;
|
|
4193
4197
|
options2["down"] = typeof options2["down"] !== "undefined" ? options2["down"] : true;
|
|
4194
4198
|
options2["size"] = typeof options2["size"] !== "undefined" ? options2["size"] : "maxi";
|
|
4195
|
-
|
|
4196
|
-
for (l in
|
|
4199
|
+
text3 = text3.split("");
|
|
4200
|
+
for (l in text3) {
|
|
4197
4201
|
if (isChar(l)) {
|
|
4198
4202
|
continue;
|
|
4199
4203
|
}
|
|
4200
|
-
result = result +
|
|
4204
|
+
result = result + text3[l];
|
|
4201
4205
|
counts = { "up": 0, "down": 0, "mid": 0 };
|
|
4202
4206
|
switch (options2.size) {
|
|
4203
4207
|
case "mini":
|
|
@@ -4228,7 +4232,7 @@ var require_zalgo = __commonJS({
|
|
|
4228
4232
|
}
|
|
4229
4233
|
return result;
|
|
4230
4234
|
}
|
|
4231
|
-
return heComes(
|
|
4235
|
+
return heComes(text2, options);
|
|
4232
4236
|
};
|
|
4233
4237
|
}
|
|
4234
4238
|
});
|
|
@@ -5266,7 +5270,7 @@ async function whoamiCommand() {
|
|
|
5266
5270
|
}
|
|
5267
5271
|
console.log();
|
|
5268
5272
|
console.log(source_default.bold(" Account"));
|
|
5269
|
-
console.log(` Email: ${
|
|
5273
|
+
console.log(` Email: ${brandDim(data.user.email)}`);
|
|
5270
5274
|
console.log(` ID: ${source_default.dim(data.user.id)}`);
|
|
5271
5275
|
if (data.user.credits != null) {
|
|
5272
5276
|
console.log(` Credits: ${source_default.green(data.user.credits)}`);
|
|
@@ -5385,7 +5389,7 @@ async function scanCommand(path) {
|
|
|
5385
5389
|
]
|
|
5386
5390
|
});
|
|
5387
5391
|
if (next === "submit") {
|
|
5388
|
-
const { submitCommand: submitCommand2 } = await import("./submit-
|
|
5392
|
+
const { submitCommand: submitCommand2 } = await import("./submit-HEQTSQL5.js");
|
|
5389
5393
|
await submitCommand2(dir, {});
|
|
5390
5394
|
} else {
|
|
5391
5395
|
tip(`Run ${brand("preflight submit")} anytime to get AI-powered fix instructions.`);
|
|
@@ -5598,11 +5602,12 @@ async function interactiveHistory() {
|
|
|
5598
5602
|
month: "short",
|
|
5599
5603
|
day: "numeric"
|
|
5600
5604
|
});
|
|
5601
|
-
const statusLabel = sub2.status === "complete" ? "Ready" : sub2.status === "failed" ? "Failed" : sub2.status === "analyzing" ? "Analyzing..." : sub2.status;
|
|
5605
|
+
const statusLabel = sub2.status === "complete" ? "Ready" : sub2.status === "failed" ? "Failed" : sub2.status === "analyzing" ? "Analyzing..." : sub2.status === "draft" ? "Draft" : sub2.status;
|
|
5606
|
+
const hint = sub2.status === "complete" ? "View report" : sub2.status === "draft" ? "Resume draft" : "";
|
|
5602
5607
|
return {
|
|
5603
5608
|
value: sub2.id,
|
|
5604
5609
|
label: `${sub2.app_name || "Unknown"} - ${statusLabel} (${date})`,
|
|
5605
|
-
hint
|
|
5610
|
+
hint
|
|
5606
5611
|
};
|
|
5607
5612
|
});
|
|
5608
5613
|
options.push({
|
|
@@ -5617,6 +5622,32 @@ async function interactiveHistory() {
|
|
|
5617
5622
|
if (selected === null || selected === "__back__") return;
|
|
5618
5623
|
const sub = submissions.find((s2) => s2.id === selected);
|
|
5619
5624
|
if (!sub) return;
|
|
5625
|
+
if (sub.status === "draft") {
|
|
5626
|
+
const draftAction = await select({
|
|
5627
|
+
message: `Draft: ${sub.app_name || "Unknown"}`,
|
|
5628
|
+
options: [
|
|
5629
|
+
{ value: "resume", label: "Resume Draft", hint: "Continue where you left off" },
|
|
5630
|
+
{ value: "back", label: "Back to list" }
|
|
5631
|
+
]
|
|
5632
|
+
});
|
|
5633
|
+
if (draftAction === null || draftAction === "back") continue;
|
|
5634
|
+
const draftSpinner = spinner();
|
|
5635
|
+
draftSpinner.start("Loading draft...");
|
|
5636
|
+
try {
|
|
5637
|
+
const draftRes = await apiRequest(`/api/submissions/${sub.id}`);
|
|
5638
|
+
const draftData = await draftRes.json();
|
|
5639
|
+
draftSpinner.stop("Draft loaded");
|
|
5640
|
+
if (draftRes.ok && draftData.data) {
|
|
5641
|
+
await resumeSubmitCommand(draftData.data);
|
|
5642
|
+
return;
|
|
5643
|
+
} else {
|
|
5644
|
+
log.error("Could not load this draft.");
|
|
5645
|
+
}
|
|
5646
|
+
} catch {
|
|
5647
|
+
draftSpinner.stop("Failed to load draft");
|
|
5648
|
+
}
|
|
5649
|
+
continue;
|
|
5650
|
+
}
|
|
5620
5651
|
if (sub.status !== "complete" || !sub.report_id) {
|
|
5621
5652
|
if (sub.status === "analyzing") {
|
|
5622
5653
|
log.info("This review is still being analyzed. Check back in a few minutes.");
|
|
@@ -5738,9 +5769,185 @@ async function setupCommand() {
|
|
|
5738
5769
|
await runOnboarding();
|
|
5739
5770
|
}
|
|
5740
5771
|
|
|
5772
|
+
// src/commands/asc.ts
|
|
5773
|
+
init_esm_shims();
|
|
5774
|
+
import { readFileSync, existsSync } from "fs";
|
|
5775
|
+
import { resolve as resolve2 } from "path";
|
|
5776
|
+
async function ascConnectCommand() {
|
|
5777
|
+
log.step("Connect to App Store Connect");
|
|
5778
|
+
console.log(subtext(" You'll need your API key from App Store Connect > Users and Access > Integrations."));
|
|
5779
|
+
console.log();
|
|
5780
|
+
const keyId = await text({
|
|
5781
|
+
message: "Key ID",
|
|
5782
|
+
placeholder: "e.g. ABC123DEF4",
|
|
5783
|
+
validate: (val) => {
|
|
5784
|
+
if (!val?.trim()) return "Key ID is required";
|
|
5785
|
+
}
|
|
5786
|
+
});
|
|
5787
|
+
if (keyId === null) return;
|
|
5788
|
+
const issuerId = await text({
|
|
5789
|
+
message: "Issuer ID",
|
|
5790
|
+
placeholder: "e.g. 12345678-1234-1234-1234-123456789012",
|
|
5791
|
+
validate: (val) => {
|
|
5792
|
+
if (!val?.trim()) return "Issuer ID is required";
|
|
5793
|
+
}
|
|
5794
|
+
});
|
|
5795
|
+
if (issuerId === null) return;
|
|
5796
|
+
const p8Path = await text({
|
|
5797
|
+
message: "Path to .p8 private key file",
|
|
5798
|
+
placeholder: "~/Downloads/AuthKey_ABC123DEF4.p8",
|
|
5799
|
+
validate: (val) => {
|
|
5800
|
+
if (!val?.trim()) return "File path is required";
|
|
5801
|
+
const resolved = resolve2(val.replace(/^~/, process.env.HOME || ""));
|
|
5802
|
+
if (!existsSync(resolved)) return `File not found: ${resolved}`;
|
|
5803
|
+
if (!resolved.endsWith(".p8")) return "File must be a .p8 key file";
|
|
5804
|
+
}
|
|
5805
|
+
});
|
|
5806
|
+
if (p8Path === null) return;
|
|
5807
|
+
const resolvedP8 = resolve2(p8Path.replace(/^~/, process.env.HOME || ""));
|
|
5808
|
+
let privateKey;
|
|
5809
|
+
try {
|
|
5810
|
+
privateKey = readFileSync(resolvedP8, "utf-8");
|
|
5811
|
+
} catch {
|
|
5812
|
+
log.error("Could not read .p8 file.");
|
|
5813
|
+
return;
|
|
5814
|
+
}
|
|
5815
|
+
const s = spinner();
|
|
5816
|
+
s.start("Connecting to App Store Connect...");
|
|
5817
|
+
try {
|
|
5818
|
+
const res = await apiRequest("/api/asc/connect", {
|
|
5819
|
+
method: "POST",
|
|
5820
|
+
body: JSON.stringify({
|
|
5821
|
+
keyId: keyId.trim(),
|
|
5822
|
+
issuerId: issuerId.trim(),
|
|
5823
|
+
privateKey
|
|
5824
|
+
})
|
|
5825
|
+
});
|
|
5826
|
+
const data = await res.json();
|
|
5827
|
+
if (!res.ok) {
|
|
5828
|
+
s.stop("Connection failed");
|
|
5829
|
+
log.error(data.message || "Failed to connect to App Store Connect");
|
|
5830
|
+
return;
|
|
5831
|
+
}
|
|
5832
|
+
s.stop("Connected to App Store Connect!");
|
|
5833
|
+
if (data.apps && data.apps.length > 0) {
|
|
5834
|
+
console.log();
|
|
5835
|
+
log.success(`Team: ${data.teamName || "Your team"}`);
|
|
5836
|
+
console.log();
|
|
5837
|
+
const appOptions = data.apps.map((app) => ({
|
|
5838
|
+
value: app.id,
|
|
5839
|
+
label: app.name,
|
|
5840
|
+
hint: app.bundleId || ""
|
|
5841
|
+
}));
|
|
5842
|
+
const selectedApp = await select({
|
|
5843
|
+
message: "Select your app",
|
|
5844
|
+
options: appOptions
|
|
5845
|
+
});
|
|
5846
|
+
if (selectedApp !== null) {
|
|
5847
|
+
const autofillSpinner = spinner();
|
|
5848
|
+
autofillSpinner.start("Saving app selection...");
|
|
5849
|
+
const autofillRes = await apiRequest("/api/asc/autofill", {
|
|
5850
|
+
method: "POST",
|
|
5851
|
+
body: JSON.stringify({ appId: selectedApp })
|
|
5852
|
+
});
|
|
5853
|
+
if (autofillRes.ok) {
|
|
5854
|
+
const autofillData = await autofillRes.json();
|
|
5855
|
+
autofillSpinner.stop("App selected");
|
|
5856
|
+
log.success(`Connected: ${autofillData.app_name || "App"} is ready for autofill.`);
|
|
5857
|
+
} else {
|
|
5858
|
+
autofillSpinner.stop("App selection saved");
|
|
5859
|
+
}
|
|
5860
|
+
}
|
|
5861
|
+
} else {
|
|
5862
|
+
log.success("Connected! Your App Store Connect data is now available for autofill.");
|
|
5863
|
+
}
|
|
5864
|
+
setAscConnected(true);
|
|
5865
|
+
console.log();
|
|
5866
|
+
} catch (err) {
|
|
5867
|
+
s.stop("Connection failed");
|
|
5868
|
+
log.error(`Error: ${err instanceof Error ? err.message : "Unknown error"}`);
|
|
5869
|
+
}
|
|
5870
|
+
}
|
|
5871
|
+
async function ascStatusCommand() {
|
|
5872
|
+
const s = createSpinner("Checking App Store Connect status...");
|
|
5873
|
+
s.start();
|
|
5874
|
+
try {
|
|
5875
|
+
const res = await apiRequest("/api/asc/connect");
|
|
5876
|
+
const data = await res.json();
|
|
5877
|
+
s.stop();
|
|
5878
|
+
if (!res.ok) {
|
|
5879
|
+
log.error(data.message || "Could not check status");
|
|
5880
|
+
return;
|
|
5881
|
+
}
|
|
5882
|
+
console.log();
|
|
5883
|
+
console.log(brand(" App Store Connect"));
|
|
5884
|
+
console.log();
|
|
5885
|
+
if (data.connected) {
|
|
5886
|
+
console.log(` Status: ${brandDim("Connected")}`);
|
|
5887
|
+
if (data.appName) console.log(` App: ${data.appName}`);
|
|
5888
|
+
if (data.keyId) console.log(` Key ID: ${data.keyId.slice(0, 3)}${"*".repeat(Math.max(0, data.keyId.length - 3))}`);
|
|
5889
|
+
} else {
|
|
5890
|
+
console.log(` Status: ${subtext("Not connected")}`);
|
|
5891
|
+
console.log();
|
|
5892
|
+
console.log(subtext(` Run ${brand("preflight asc connect")} to set up.`));
|
|
5893
|
+
}
|
|
5894
|
+
console.log();
|
|
5895
|
+
} catch (err) {
|
|
5896
|
+
s.stop();
|
|
5897
|
+
log.error(`Error: ${err instanceof Error ? err.message : "Unknown error"}`);
|
|
5898
|
+
}
|
|
5899
|
+
}
|
|
5900
|
+
async function ascDisconnectCommand() {
|
|
5901
|
+
const confirmed = await confirm("Disconnect from App Store Connect?", false);
|
|
5902
|
+
if (confirmed === null || !confirmed) return;
|
|
5903
|
+
const s = createSpinner("Disconnecting...");
|
|
5904
|
+
s.start();
|
|
5905
|
+
try {
|
|
5906
|
+
const res = await apiRequest("/api/asc/connect", {
|
|
5907
|
+
method: "DELETE"
|
|
5908
|
+
});
|
|
5909
|
+
s.stop();
|
|
5910
|
+
if (res.ok) {
|
|
5911
|
+
setAscConnected(false);
|
|
5912
|
+
log.success("Disconnected from App Store Connect.");
|
|
5913
|
+
} else {
|
|
5914
|
+
const data = await res.json();
|
|
5915
|
+
log.error(data.message || "Could not disconnect");
|
|
5916
|
+
}
|
|
5917
|
+
} catch (err) {
|
|
5918
|
+
s.stop();
|
|
5919
|
+
log.error(`Error: ${err instanceof Error ? err.message : "Unknown error"}`);
|
|
5920
|
+
}
|
|
5921
|
+
}
|
|
5922
|
+
async function ascInteractiveMenu() {
|
|
5923
|
+
while (true) {
|
|
5924
|
+
const choice = await select({
|
|
5925
|
+
message: "App Store Connect",
|
|
5926
|
+
options: [
|
|
5927
|
+
{ value: "connect", label: "Connect", hint: "Set up API key" },
|
|
5928
|
+
{ value: "status", label: "View Status", hint: "Check connection" },
|
|
5929
|
+
{ value: "disconnect", label: "Disconnect", hint: "Remove API key" },
|
|
5930
|
+
{ value: "back", label: "Back to menu" }
|
|
5931
|
+
]
|
|
5932
|
+
});
|
|
5933
|
+
if (choice === null || choice === "back") return;
|
|
5934
|
+
switch (choice) {
|
|
5935
|
+
case "connect":
|
|
5936
|
+
await ascConnectCommand();
|
|
5937
|
+
break;
|
|
5938
|
+
case "status":
|
|
5939
|
+
await ascStatusCommand();
|
|
5940
|
+
break;
|
|
5941
|
+
case "disconnect":
|
|
5942
|
+
await ascDisconnectCommand();
|
|
5943
|
+
break;
|
|
5944
|
+
}
|
|
5945
|
+
}
|
|
5946
|
+
}
|
|
5947
|
+
|
|
5741
5948
|
// src/index.ts
|
|
5742
5949
|
var program2 = new Command();
|
|
5743
|
-
program2.name("preflight").description("Preflight - App Store Review Scanner").version("0.2.
|
|
5950
|
+
program2.name("preflight").description("Preflight - App Store Review Scanner").version("0.2.2");
|
|
5744
5951
|
program2.command("login").description("Log in to Preflight (opens browser)").action(loginCommand);
|
|
5745
5952
|
program2.command("logout").description("Log out and clear stored credentials").action(logoutCommand);
|
|
5746
5953
|
program2.command("whoami").description("Show current user and credit balance").action(whoamiCommand);
|
|
@@ -5751,6 +5958,10 @@ program2.command("report [id]").description("View full analysis report").option(
|
|
|
5751
5958
|
program2.command("history").description("List past submissions").option("--json", "Output as JSON").action(historyCommand);
|
|
5752
5959
|
program2.command("credits").description("Show credit balance").action(creditsCommand);
|
|
5753
5960
|
program2.command("setup").description("Run guided setup (can be re-run anytime)").action(setupCommand);
|
|
5961
|
+
var ascCmd = program2.command("asc").description("App Store Connect integration");
|
|
5962
|
+
ascCmd.command("connect").description("Connect your App Store Connect account").action(ascConnectCommand);
|
|
5963
|
+
ascCmd.command("status").description("Check App Store Connect connection status").action(ascStatusCommand);
|
|
5964
|
+
ascCmd.command("disconnect").description("Disconnect from App Store Connect").action(ascDisconnectCommand);
|
|
5754
5965
|
program2.on("command:*", (operands) => {
|
|
5755
5966
|
handleUnknownCommand(operands[0]);
|
|
5756
5967
|
process.exitCode = 1;
|
|
@@ -5793,6 +6004,7 @@ async function interactiveMenu() {
|
|
|
5793
6004
|
{ value: "review", label: "New Review", hint: "Scan your app for App Store issues" },
|
|
5794
6005
|
{ value: "history", label: "View Reviews", hint: "See your past review reports" },
|
|
5795
6006
|
{ value: "buy", label: "Buy Credits", hint: "Get more credits at preflightlaunch.com" },
|
|
6007
|
+
{ value: "asc", label: "App Store Connect", hint: "Connect your ASC account for autofill" },
|
|
5796
6008
|
{ value: "logout", label: "Log Out" }
|
|
5797
6009
|
]
|
|
5798
6010
|
});
|
|
@@ -5816,6 +6028,9 @@ async function interactiveMenu() {
|
|
|
5816
6028
|
await new Promise((r) => setTimeout(r, 2e3));
|
|
5817
6029
|
cachedCredits = await fetchCredits();
|
|
5818
6030
|
break;
|
|
6031
|
+
case "asc":
|
|
6032
|
+
await ascInteractiveMenu();
|
|
6033
|
+
break;
|
|
5819
6034
|
case "logout":
|
|
5820
6035
|
clearAuth();
|
|
5821
6036
|
const authenticated = await showAuthScreen();
|
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
const require = createRequire(import.meta.url);
|
|
4
4
|
import {
|
|
5
|
+
resumeSubmitCommand,
|
|
5
6
|
submitCommand
|
|
6
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-26P7VL2P.js";
|
|
7
8
|
import "./chunk-45JYNMSU.js";
|
|
8
9
|
export {
|
|
10
|
+
resumeSubmitCommand,
|
|
9
11
|
submitCommand
|
|
10
12
|
};
|
|
11
|
-
//# sourceMappingURL=submit-
|
|
13
|
+
//# sourceMappingURL=submit-HEQTSQL5.js.map
|