preflightlaunch 0.2.0 → 0.2.1

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/index.js CHANGED
@@ -2,14 +2,14 @@
2
2
  import { createRequire } from "node:module";
3
3
  const require = createRequire(import.meta.url);
4
4
  import {
5
+ DEFAULT_API_URL,
5
6
  apiRequest,
6
7
  brand,
7
- brandSplash,
8
+ clearAuth,
9
+ confirm,
8
10
  createSpinner,
9
11
  critical,
10
12
  error,
11
- findProjectInDir,
12
- findXcodeProjects,
13
13
  getConfig,
14
14
  handleUnknownCommand,
15
15
  hasRunBefore,
@@ -22,13 +22,12 @@ import {
22
22
  logout,
23
23
  markAsRun,
24
24
  ok,
25
- outro,
25
+ renderHeader,
26
26
  renderReport,
27
27
  renderReportJson,
28
28
  scanProject,
29
29
  select,
30
30
  setLastScannedPath,
31
- showTagline,
32
31
  source_default,
33
32
  spinner,
34
33
  submitCommand,
@@ -36,7 +35,7 @@ import {
36
35
  success,
37
36
  tip,
38
37
  warning
39
- } from "./chunk-X5CBMYPG.js";
38
+ } from "./chunk-PMKDGQCB.js";
40
39
  import {
41
40
  __commonJS,
42
41
  __require,
@@ -5227,7 +5226,7 @@ async function loginCommand() {
5227
5226
  const spinner2 = createSpinner("Opening browser for login...");
5228
5227
  spinner2.start();
5229
5228
  try {
5230
- const result = await loginWithBrowser();
5229
+ const result = await loginWithBrowser("login");
5231
5230
  if (result) {
5232
5231
  spinner2.stop();
5233
5232
  success(`Logged in as ${source_default.bold(result.email)}`);
@@ -5318,10 +5317,11 @@ async function scanCommand(path) {
5318
5317
  const resolvedPath = await interactiveProjectSelect();
5319
5318
  if (!resolvedPath) return;
5320
5319
  path = resolvedPath;
5320
+ } else {
5321
+ intro("Scanning project");
5321
5322
  }
5322
5323
  const dir = resolve(path);
5323
5324
  setLastScannedPath(dir);
5324
- intro("Scanning project");
5325
5325
  const s = spinner();
5326
5326
  s.start("Looking for App Store files...");
5327
5327
  const detected = scanProject(dir);
@@ -5380,12 +5380,12 @@ async function scanCommand(path) {
5380
5380
  const next = await select({
5381
5381
  message: "What next?",
5382
5382
  options: [
5383
- { value: "submit", label: "Submit for full analysis", hint: "1 credit" },
5383
+ { value: "submit", label: "Submit for full analysis", hint: "100 credits" },
5384
5384
  { value: "done", label: "Done for now" }
5385
5385
  ]
5386
5386
  });
5387
5387
  if (next === "submit") {
5388
- const { submitCommand: submitCommand2 } = await import("./submit-WANAECAV.js");
5388
+ const { submitCommand: submitCommand2 } = await import("./submit-HBKAOG3Y.js");
5389
5389
  await submitCommand2(dir, {});
5390
5390
  } else {
5391
5391
  tip(`Run ${brand("preflight submit")} anytime to get AI-powered fix instructions.`);
@@ -5399,6 +5399,11 @@ async function statusCommand(id, options) {
5399
5399
  error("Not logged in. Run `preflight login` first.");
5400
5400
  process.exit(1);
5401
5401
  }
5402
+ if (!id) {
5403
+ error("Please provide a submission ID. Run `preflight history` to find one.");
5404
+ process.exitCode = 1;
5405
+ return;
5406
+ }
5402
5407
  if (options.watch) {
5403
5408
  return watchStatus(id);
5404
5409
  }
@@ -5480,8 +5485,13 @@ async function reportCommand(id, options) {
5480
5485
  error("Not logged in. Run `preflight login` first.");
5481
5486
  process.exit(1);
5482
5487
  }
5488
+ if (!id) {
5489
+ error("Please provide a submission or report ID. Run `preflight history` to find one.");
5490
+ process.exitCode = 1;
5491
+ return;
5492
+ }
5483
5493
  if (options.open) {
5484
- await open_default(`https://preflight.dev/report/${id}`);
5494
+ await open_default(`${DEFAULT_API_URL}/report/${id}`);
5485
5495
  console.log(source_default.dim(" Opened report in browser"));
5486
5496
  return;
5487
5497
  }
@@ -5499,7 +5509,7 @@ async function reportCommand(id, options) {
5499
5509
  renderReportJson(data.report, data.items);
5500
5510
  } else {
5501
5511
  renderReport(data.report, data.items);
5502
- console.log(source_default.dim(` Full report: https://preflight.dev/report/${id}`));
5512
+ console.log(source_default.dim(` Full report: ${DEFAULT_API_URL}/report/${id}`));
5503
5513
  console.log();
5504
5514
  }
5505
5515
  } catch (err) {
@@ -5564,64 +5574,163 @@ async function historyCommand(options) {
5564
5574
  process.exit(1);
5565
5575
  }
5566
5576
  }
5577
+ async function interactiveHistory() {
5578
+ const s = spinner();
5579
+ s.start("Loading your reviews...");
5580
+ try {
5581
+ const res = await apiRequest("/api/submissions");
5582
+ const data = await res.json();
5583
+ s.stop("Reviews loaded");
5584
+ if (!res.ok) {
5585
+ log.error(data.message || "Failed to load reviews");
5586
+ return;
5587
+ }
5588
+ if (!data.data || data.data.length === 0) {
5589
+ log.info("No reviews yet. Start your first review from the main menu.");
5590
+ console.log();
5591
+ await confirm("Back to menu?", true);
5592
+ return;
5593
+ }
5594
+ const submissions = data.data;
5595
+ while (true) {
5596
+ const options = submissions.map((sub2) => {
5597
+ const date = new Date(sub2.created_at).toLocaleDateString("en-US", {
5598
+ month: "short",
5599
+ day: "numeric"
5600
+ });
5601
+ const statusLabel = sub2.status === "complete" ? "Ready" : sub2.status === "failed" ? "Failed" : sub2.status === "analyzing" ? "Analyzing..." : sub2.status;
5602
+ return {
5603
+ value: sub2.id,
5604
+ label: `${sub2.app_name || "Unknown"} - ${statusLabel} (${date})`,
5605
+ hint: sub2.status === "complete" ? "View report" : ""
5606
+ };
5607
+ });
5608
+ options.push({
5609
+ value: "__back__",
5610
+ label: "Back to menu",
5611
+ hint: ""
5612
+ });
5613
+ const selected = await select({
5614
+ message: "Your Reviews",
5615
+ options
5616
+ });
5617
+ if (selected === null || selected === "__back__") return;
5618
+ const sub = submissions.find((s2) => s2.id === selected);
5619
+ if (!sub) return;
5620
+ if (sub.status !== "complete" || !sub.report_id) {
5621
+ if (sub.status === "analyzing") {
5622
+ log.info("This review is still being analyzed. Check back in a few minutes.");
5623
+ } else if (sub.status === "failed") {
5624
+ log.warning("This review failed. Try submitting again.");
5625
+ } else {
5626
+ log.info(`Status: ${sub.status}`);
5627
+ }
5628
+ console.log();
5629
+ continue;
5630
+ }
5631
+ const reportSpinner = spinner();
5632
+ reportSpinner.start("Loading report...");
5633
+ try {
5634
+ const reportRes = await apiRequest(`/api/reports/${sub.report_id}`);
5635
+ const reportData = await reportRes.json();
5636
+ reportSpinner.stop("Report loaded");
5637
+ if (reportRes.ok) {
5638
+ renderReport(reportData.report, reportData.items);
5639
+ console.log(subtext(` Full report: https://preflightlaunch.com/report/${sub.report_id}`));
5640
+ console.log();
5641
+ } else {
5642
+ log.error("Could not load this report.");
5643
+ }
5644
+ } catch {
5645
+ reportSpinner.stop("Failed to load report");
5646
+ }
5647
+ }
5648
+ } catch (err) {
5649
+ s.stop("Failed to load reviews");
5650
+ log.error(`Error: ${err instanceof Error ? err.message : "Unknown error"}`);
5651
+ }
5652
+ }
5567
5653
 
5568
5654
  // src/commands/setup.ts
5569
5655
  init_esm_shims();
5570
5656
 
5571
5657
  // src/commands/onboarding.ts
5572
5658
  init_esm_shims();
5573
- import { homedir } from "os";
5574
- async function runOnboarding() {
5575
- brandSplash();
5576
- intro("Welcome to Preflight!");
5577
- log.message("Let's get you set up. This takes about 30 seconds.");
5578
- if (!isLoggedIn()) {
5579
- const authChoice = await select({
5580
- message: "Step 1 of 2: Set up your account",
5581
- options: [
5582
- { value: "signup", label: "Open browser to sign up", hint: "Create a free account" },
5583
- { value: "login", label: "I already have an account", hint: "Log in" },
5584
- { value: "skip", label: "Skip for now", hint: "Scan works without login" }
5585
- ]
5586
- });
5587
- if (authChoice === null) return;
5588
- if (authChoice === "signup" || authChoice === "login") {
5589
- const s = spinner();
5590
- s.start("Opening browser...");
5591
- const result = await loginWithBrowser();
5592
- s.stop(result ? `Logged in as ${result.email}` : "Login skipped");
5659
+ async function showWelcomeScreen() {
5660
+ renderHeader();
5661
+ console.log(" Preflight scans your app for common issues that");
5662
+ console.log(" cause App Store rejections -- before you submit.");
5663
+ console.log();
5664
+ console.log(subtext(" How it works:"));
5665
+ console.log(subtext(" 1. Point us to your Xcode project"));
5666
+ console.log(subtext(" 2. We scan for 100+ rejection risks"));
5667
+ console.log(subtext(" 3. Get a detailed report with fixes"));
5668
+ console.log();
5669
+ const result = await select({
5670
+ message: "Ready?",
5671
+ options: [
5672
+ { value: "start", label: "Get Started" }
5673
+ ]
5674
+ });
5675
+ if (result === null) return false;
5676
+ markAsRun();
5677
+ return true;
5678
+ }
5679
+ async function showAuthScreen() {
5680
+ renderHeader();
5681
+ const authChoice = await select({
5682
+ message: "How would you like to get started?",
5683
+ options: [
5684
+ { value: "signup", label: "Create a free account", hint: "Sign up with email, GitHub, or Google" },
5685
+ { value: "login", label: "I already have an account", hint: "Log in with email, GitHub, or Google" }
5686
+ ]
5687
+ });
5688
+ if (authChoice === null) return false;
5689
+ if (authChoice === "signup") {
5690
+ const s2 = spinner();
5691
+ s2.start("Opening signup page in browser...");
5692
+ try {
5693
+ await loginWithBrowser("signup");
5694
+ } catch {
5695
+ s2.stop("Could not open browser");
5696
+ }
5697
+ s2.stop("Signup page opened");
5698
+ log.info("Create your account in the browser, then come back here.");
5699
+ console.log();
5700
+ const ready = await confirm("Done signing up? Ready to log in?");
5701
+ if (ready === null || !ready) {
5702
+ log.info(subtext("Run `preflight` anytime to come back."));
5703
+ return false;
5593
5704
  }
5594
- } else {
5595
- log.success("Already logged in. Skipping account setup.");
5596
5705
  }
5597
- const projects = findXcodeProjects();
5598
- const cwdProject = findProjectInDir(process.cwd());
5599
- if (projects.length > 0 || cwdProject) {
5600
- const allProjects = cwdProject ? [cwdProject, ...projects.filter((p) => p.path !== cwdProject.path)] : projects;
5601
- const choices = allProjects.slice(0, 5).map((proj) => ({
5602
- value: proj.path,
5603
- label: `${proj.name} (${proj.type === "xcworkspace" ? ".xcworkspace" : ".xcodeproj"})`,
5604
- hint: proj.path.replace(homedir(), "~")
5605
- }));
5606
- choices.push({
5607
- value: "__skip__",
5608
- label: "Skip - I'll scan later",
5609
- hint: ""
5610
- });
5611
- const projectChoice = await select({
5612
- message: "Step 2 of 2: Choose your Xcode project",
5613
- options: choices
5614
- });
5615
- if (projectChoice === null) return;
5616
- if (projectChoice !== "__skip__") {
5617
- setLastScannedPath(projectChoice);
5618
- log.success(`Project saved! Run ${brand("preflight scan")} to scan it.`);
5706
+ const s = spinner();
5707
+ s.start("Opening login page... Waiting for you to finish in browser.");
5708
+ try {
5709
+ const result = await loginWithBrowser("login");
5710
+ if (result) {
5711
+ s.stop(`Logged in as ${result.email}`);
5712
+ return true;
5713
+ } else {
5714
+ s.stop("Login timed out or was cancelled");
5715
+ log.warning("Run `preflight` anytime to try again.");
5716
+ return false;
5619
5717
  }
5718
+ } catch {
5719
+ s.stop("Could not open browser");
5720
+ log.warning("Run `preflight login` to try from the command line.");
5721
+ return false;
5722
+ }
5723
+ }
5724
+ async function runOnboarding() {
5725
+ if (!isLoggedIn()) {
5726
+ const welcomed = await showWelcomeScreen();
5727
+ if (!welcomed) return;
5728
+ const authenticated = await showAuthScreen();
5729
+ if (!authenticated) return;
5620
5730
  } else {
5621
- log.info("No Xcode projects found on your Mac.\nRun " + brand("preflight scan <path>") + " when you're ready.");
5731
+ markAsRun();
5732
+ log.success("Already logged in.");
5622
5733
  }
5623
- markAsRun();
5624
- outro("You're all set! Run " + brand("preflight") + " to get started.");
5625
5734
  }
5626
5735
 
5627
5736
  // src/commands/setup.ts
@@ -5631,7 +5740,7 @@ async function setupCommand() {
5631
5740
 
5632
5741
  // src/index.ts
5633
5742
  var program2 = new Command();
5634
- program2.name("preflight").description("Preflight - App Store Review Scanner").version("0.2.0");
5743
+ program2.name("preflight").description("Preflight - App Store Review Scanner").version("0.2.1");
5635
5744
  program2.command("login").description("Log in to Preflight (opens browser)").action(loginCommand);
5636
5745
  program2.command("logout").description("Log out and clear stored credentials").action(logoutCommand);
5637
5746
  program2.command("whoami").description("Show current user and credit balance").action(whoamiCommand);
@@ -5646,49 +5755,74 @@ program2.on("command:*", (operands) => {
5646
5755
  handleUnknownCommand(operands[0]);
5647
5756
  process.exitCode = 1;
5648
5757
  });
5758
+ async function fetchCredits() {
5759
+ try {
5760
+ const res = await apiRequest("/api/credits");
5761
+ if (!res.ok) return void 0;
5762
+ const data = await res.json();
5763
+ return data.credits ?? void 0;
5764
+ } catch {
5765
+ return void 0;
5766
+ }
5767
+ }
5768
+ async function openUrl(url) {
5769
+ try {
5770
+ const open = (await import("./open-A77P4RC4.js")).default;
5771
+ await open(url);
5772
+ } catch {
5773
+ console.log(subtext(` Visit: ${url}`));
5774
+ }
5775
+ }
5649
5776
  async function interactiveMenu() {
5650
5777
  if (!hasRunBefore()) {
5651
- await runOnboarding();
5652
- return;
5778
+ const welcomed = await showWelcomeScreen();
5779
+ if (!welcomed) return;
5653
5780
  }
5654
- intro();
5655
- showTagline();
5656
- const loggedIn = isLoggedIn();
5657
- const options = loggedIn ? [
5658
- { value: "scan", label: "Scan my app", hint: "Free preview" },
5659
- { value: "submit", label: "Submit for full AI analysis", hint: "Uses 1 credit" },
5660
- { value: "history", label: "View my reports", hint: "Past submissions" },
5661
- { value: "account", label: "Check account & credits", hint: "" },
5662
- { value: "help", label: "Help - show all commands", hint: "" }
5663
- ] : [
5664
- { value: "scan", label: "Scan my app", hint: "Free, no login needed" },
5665
- { value: "login", label: "Log in to your account", hint: "Opens browser" },
5666
- { value: "help", label: "Help - show all commands", hint: "" }
5667
- ];
5668
- const choice = await select({
5669
- message: "What would you like to do?",
5670
- options
5671
- });
5672
- if (choice === null) return;
5673
- switch (choice) {
5674
- case "scan":
5675
- await scanCommand();
5676
- break;
5677
- case "submit":
5678
- await submitCommand();
5679
- break;
5680
- case "login":
5681
- await loginCommand();
5682
- break;
5683
- case "history":
5684
- await historyCommand({});
5685
- break;
5686
- case "account":
5687
- await whoamiCommand();
5688
- break;
5689
- case "help":
5690
- program2.outputHelp();
5691
- break;
5781
+ if (!isLoggedIn()) {
5782
+ const authenticated = await showAuthScreen();
5783
+ if (!authenticated) return;
5784
+ }
5785
+ let cachedCredits;
5786
+ cachedCredits = await fetchCredits();
5787
+ while (true) {
5788
+ const { email } = getConfig();
5789
+ renderHeader(email, cachedCredits);
5790
+ const choice = await select({
5791
+ message: "What would you like to do?",
5792
+ options: [
5793
+ { value: "review", label: "New Review", hint: "Scan your app for App Store issues" },
5794
+ { value: "history", label: "View Reviews", hint: "See your past review reports" },
5795
+ { value: "buy", label: "Buy Credits", hint: "Get more credits at preflightlaunch.com" },
5796
+ { value: "logout", label: "Log Out" }
5797
+ ]
5798
+ });
5799
+ if (choice === null) {
5800
+ console.log();
5801
+ console.log(subtext(" See you next time!"));
5802
+ console.log();
5803
+ return;
5804
+ }
5805
+ switch (choice) {
5806
+ case "review":
5807
+ await submitCommand(void 0, {}, true);
5808
+ cachedCredits = await fetchCredits();
5809
+ break;
5810
+ case "history":
5811
+ await interactiveHistory();
5812
+ break;
5813
+ case "buy":
5814
+ await openUrl("https://preflightlaunch.com/pricing");
5815
+ log.info("Opened pricing page in browser.");
5816
+ await new Promise((r) => setTimeout(r, 2e3));
5817
+ cachedCredits = await fetchCredits();
5818
+ break;
5819
+ case "logout":
5820
+ clearAuth();
5821
+ const authenticated = await showAuthScreen();
5822
+ if (!authenticated) return;
5823
+ cachedCredits = await fetchCredits();
5824
+ break;
5825
+ }
5692
5826
  }
5693
5827
  }
5694
5828
  if (process.argv.length <= 2) {
@@ -3,9 +3,9 @@ import { createRequire } from "node:module";
3
3
  const require = createRequire(import.meta.url);
4
4
  import {
5
5
  submitCommand
6
- } from "./chunk-X5CBMYPG.js";
6
+ } from "./chunk-PMKDGQCB.js";
7
7
  import "./chunk-45JYNMSU.js";
8
8
  export {
9
9
  submitCommand
10
10
  };
11
- //# sourceMappingURL=submit-WANAECAV.js.map
11
+ //# sourceMappingURL=submit-HBKAOG3Y.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "preflightlaunch",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "type": "module",
5
5
  "description": "Preflight CLI - App Store Review Scanner from your terminal",
6
6
  "bin": {