ship-em 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/README.md CHANGED
@@ -7,7 +7,7 @@ Deploy apps built with AI coding tools (Claude, Cursor, Bolt, Lovable) in 60 sec
7
7
  ## Quick Start
8
8
 
9
9
  ```bash
10
- npx shipem
10
+ npx ship-em
11
11
  ```
12
12
 
13
13
  That's it. Shipem detects your framework, builds your app, and deploys it to a live URL.
@@ -19,17 +19,17 @@ Next.js, Vite, React, Astro, SvelteKit, Nuxt, Remix, Gatsby, Express, FastAPI, F
19
19
  ## Commands
20
20
 
21
21
  ```bash
22
- npx shipem # Deploy your app
23
- npx shipem login # Authenticate with GitHub
24
- npx shipem logout # Log out
25
- npx shipem status # Check deployment status
26
- npx shipem logs # View build logs
27
- npx shipem down # Take your app offline
22
+ npx ship-em # Deploy your app
23
+ npx ship-em login # Authenticate with GitHub
24
+ npx ship-em logout # Log out
25
+ npx ship-em status # Check deployment status
26
+ npx ship-em logs # View build logs
27
+ npx ship-em down # Take your app offline
28
28
  ```
29
29
 
30
30
  ## How it works
31
31
 
32
- 1. **Run `npx shipem`** in your project directory
32
+ 1. **Run `npx ship-em`** in your project directory
33
33
  2. **AI detects your stack** — framework, build command, env vars
34
34
  3. **Your app is live** — deployed to Cloudflare's global network
35
35
 
package/dist/index.js CHANGED
@@ -268,9 +268,9 @@ ${url}`);
268
268
  console.log("");
269
269
  }
270
270
  console.log(` ${brand.bold("Next steps:")}`);
271
- console.log(` ${brand.gray("Update")} \u2192 ${chalk.cyan("npx shipem")}`);
272
- console.log(` ${brand.gray("Status")} \u2192 ${chalk.cyan("npx shipem status")}`);
273
- console.log(` ${brand.gray("Remove")} \u2192 ${chalk.cyan("npx shipem down")}`);
271
+ console.log(` ${brand.gray("Update")} \u2192 ${chalk.cyan("npx ship-em")}`);
272
+ console.log(` ${brand.gray("Status")} \u2192 ${chalk.cyan("npx ship-em status")}`);
273
+ console.log(` ${brand.gray("Remove")} \u2192 ${chalk.cyan("npx ship-em down")}`);
274
274
  console.log("");
275
275
  },
276
276
  // Format bytes
@@ -348,9 +348,9 @@ ${url}`);
348
348
  console.log("");
349
349
  }
350
350
  console.log(` ${brand.bold("Next steps:")}`);
351
- console.log(` ${brand.gray("Update")} \u2192 ${chalk.cyan("npx shipem")}`);
352
- console.log(` ${brand.gray("Status")} \u2192 ${chalk.cyan("npx shipem status")}`);
353
- console.log(` ${brand.gray("Remove")} \u2192 ${chalk.cyan("npx shipem down")}`);
351
+ console.log(` ${brand.gray("Update")} \u2192 ${chalk.cyan("npx ship-em")}`);
352
+ console.log(` ${brand.gray("Status")} \u2192 ${chalk.cyan("npx ship-em status")}`);
353
+ console.log(` ${brand.gray("Remove")} \u2192 ${chalk.cyan("npx ship-em down")}`);
354
354
  console.log("");
355
355
  },
356
356
  // Rich status display for deploy history
@@ -1439,7 +1439,7 @@ async function fixCommand(options = {}) {
1439
1439
  ui.dim("Suggestions:");
1440
1440
  ui.dim(" 1. Paste the error into your AI coding tool for a fix");
1441
1441
  ui.dim(" 2. Check the error output above for clues");
1442
- ui.dim(" 3. Run `npx shipem --verbose` for detailed output");
1442
+ ui.dim(" 3. Run `npx ship-em --verbose` for detailed output");
1443
1443
  ui.br();
1444
1444
  return;
1445
1445
  }
@@ -1449,7 +1449,7 @@ async function fixCommand(options = {}) {
1449
1449
  if (buildResult.success) {
1450
1450
  ui.br();
1451
1451
  ui.success("Fixed! Build passes now.");
1452
- ui.dim("Run `npx shipem` to deploy.");
1452
+ ui.dim("Run `npx ship-em` to deploy.");
1453
1453
  ui.br();
1454
1454
  } else {
1455
1455
  ui.br();
@@ -2086,7 +2086,7 @@ function showBuildErrorHelp(errMsg) {
2086
2086
  ui.friendlyError(
2087
2087
  "Your app failed to build",
2088
2088
  errMsg.split("\n")[0] ?? errMsg,
2089
- "Fix the error above, then run `npx shipem` again",
2089
+ "Fix the error above, then run `npx ship-em` again",
2090
2090
  "Tip: If your AI tool generated this code, paste the error back to it for a fix."
2091
2091
  );
2092
2092
  }
@@ -2109,13 +2109,45 @@ async function deployCommand(options) {
2109
2109
  ui.banner();
2110
2110
  }
2111
2111
  const isAnonymous = !getSessionToken() && !getCloudflareCredentials();
2112
+ let sessionToken = getSessionToken();
2112
2113
  if (isFirstRun) {
2113
2114
  console.log(` ${chalk4.white.bold("Welcome to Shipem!")} \u2728`);
2114
2115
  console.log("");
2115
- console.log(` ${chalk4.dim("No account needed. Deploying your app now...")}`);
2116
+ }
2117
+ if (isAnonymous) {
2118
+ console.log(` ${chalk4.dim("To deploy, you need either Cloudflare credentials or a Shipem login.")}`);
2119
+ console.log("");
2120
+ console.log(` ${chalk4.cyan("Option 1:")} Set Cloudflare credentials (free account)`);
2121
+ console.log(` ${chalk4.dim("export CLOUDFLARE_API_TOKEN=your_token")}`);
2122
+ console.log(` ${chalk4.dim("export CLOUDFLARE_ACCOUNT_ID=your_account_id")}`);
2123
+ console.log(` ${chalk4.dim("Get these from: https://dash.cloudflare.com/profile/api-tokens")}`);
2124
+ console.log("");
2125
+ console.log(` ${chalk4.cyan("Option 2:")} Log in to Shipem`);
2126
+ console.log(` ${chalk4.dim("npx ship-em login")}`);
2127
+ console.log("");
2128
+ const { action } = await inquirer.prompt([
2129
+ {
2130
+ type: "list",
2131
+ name: "action",
2132
+ message: "What would you like to do?",
2133
+ choices: [
2134
+ { name: "Log in with GitHub (sets up deployment)", value: "login" },
2135
+ { name: "Quit (I'll set up Cloudflare credentials)", value: "quit" }
2136
+ ]
2137
+ }
2138
+ ]);
2139
+ if (action === "quit") {
2140
+ process.exit(0);
2141
+ }
2142
+ await loginCommand({ skipBanner: true });
2143
+ sessionToken = getSessionToken();
2144
+ if (!sessionToken) {
2145
+ throw new AuthError("Login failed. Please try again with: npx ship-em login");
2146
+ }
2147
+ console.log("");
2148
+ console.log(` ${chalk4.hex("#22C55E")("\u2713")} ${chalk4.bold("Signed in! Continuing deploy...")}`);
2116
2149
  console.log("");
2117
2150
  }
2118
- let sessionToken = getSessionToken();
2119
2151
  phaseStart = Date.now();
2120
2152
  ui.section("Scanning project...");
2121
2153
  ui.br();
@@ -2484,99 +2516,7 @@ async function deployCommand(options) {
2484
2516
  }
2485
2517
  }
2486
2518
  } else {
2487
- ui.br();
2488
- ui.warn("No Cloudflare credentials found and not logged in.");
2489
- ui.br();
2490
- console.log(` ${chalk4.bold("To deploy, choose one option:")}`);
2491
- console.log("");
2492
- console.log(` ${chalk4.cyan("Option 1:")} Set Cloudflare credentials (free account)`);
2493
- console.log(` ${chalk4.dim("export CLOUDFLARE_API_TOKEN=your_token")}`);
2494
- console.log(` ${chalk4.dim("export CLOUDFLARE_ACCOUNT_ID=your_account_id")}`);
2495
- console.log(` ${chalk4.dim("Get these from: https://dash.cloudflare.com/profile/api-tokens")}`);
2496
- console.log("");
2497
- console.log(` ${chalk4.cyan("Option 2:")} Log in to Shipem`);
2498
- console.log(` ${chalk4.dim("npx shipem login")}`);
2499
- console.log("");
2500
- const { action } = await inquirer.prompt([
2501
- {
2502
- type: "list",
2503
- name: "action",
2504
- message: "What would you like to do?",
2505
- choices: [
2506
- { name: "Log in with GitHub (sets up deployment)", value: "login" },
2507
- { name: "Quit (I'll set up Cloudflare credentials)", value: "quit" }
2508
- ]
2509
- }
2510
- ]);
2511
- if (action === "quit") {
2512
- process.exit(0);
2513
- }
2514
- await loginCommand({ skipBanner: true });
2515
- sessionToken = getSessionToken();
2516
- if (!sessionToken) {
2517
- throw new AuthError("Login failed. Please try again with: shipem login");
2518
- }
2519
- console.log("");
2520
- console.log(` ${chalk4.hex("#22C55E")("\u2713")} ${chalk4.bold("Signed in!")}`);
2521
- console.log("");
2522
- const tarPath = join8(tmpdir(), `shipem-${Date.now()}.tar.gz`);
2523
- try {
2524
- const outputPath = join8(cwd, projectConfig.outputDirectory);
2525
- warnIfEnvFilesInOutputDir(outputPath);
2526
- const stats = countOutputFiles(outputPath);
2527
- deployFileCount = stats.fileCount;
2528
- deployTotalBytes = stats.totalBytes;
2529
- const packagingSpinner = ui.spinner("Packaging files...");
2530
- await tarCreate(
2531
- {
2532
- gzip: true,
2533
- file: tarPath,
2534
- cwd: outputPath,
2535
- filter: (filePath) => !shouldExclude(filePath, cwd)
2536
- },
2537
- ["."]
2538
- );
2539
- packagingSpinner.succeed(
2540
- `Packaged ${deployFileCount} files (${ui.formatBytes(deployTotalBytes)})`
2541
- );
2542
- const uploadSpinner = ui.spinner("Uploading to cloud...");
2543
- const form = new FormData2();
2544
- form.append("config", JSON.stringify({
2545
- name: projectConfig.name,
2546
- framework: projectConfig.framework,
2547
- deployTarget: projectConfig.deployTarget,
2548
- outputDirectory: projectConfig.outputDirectory
2549
- }));
2550
- form.append("artifacts", createReadStream(tarPath), {
2551
- filename: "artifacts.tar.gz",
2552
- contentType: "application/gzip"
2553
- });
2554
- const response = await apiRequest({
2555
- method: "post",
2556
- url: `${SHIPEM_API_URL}/projects/deploy`,
2557
- data: form,
2558
- headers: {
2559
- ...form.getHeaders(),
2560
- Authorization: `Bearer ${sessionToken}`
2561
- },
2562
- maxBodyLength: Infinity,
2563
- timeout: DEPLOY_TIMEOUT_MS2
2564
- });
2565
- uploadSpinner.succeed("Deployed successfully");
2566
- liveUrl = response.data.url;
2567
- projectId = response.data.projectId;
2568
- } catch (err) {
2569
- if (err instanceof AuthError || err instanceof NetworkError || err instanceof DeployError) {
2570
- throw err;
2571
- }
2572
- const msg = err instanceof Error ? err.message : String(err);
2573
- throw new DeployError(`Deployment failed: ${msg}`, { cause: err });
2574
- } finally {
2575
- try {
2576
- rmSync(tarPath);
2577
- } catch {
2578
- }
2579
- }
2519
+ throw new AuthError("No Cloudflare credentials found and not logged in. Run: npx ship-em login");
2580
2520
  }
2581
2521
  phases.push({ name: "Upload", durationMs: Date.now() - phaseStart });
2582
2522
  const elapsedSec = (Date.now() - startTime) / 1e3;
@@ -2857,8 +2797,8 @@ async function templatesCommand() {
2857
2797
  console.log("");
2858
2798
  }
2859
2799
  console.log(` ${brand.bold("Quick start:")}`);
2860
- console.log(` ${chalk5.cyan("npx shipem init --template astro-blog")}`);
2861
- console.log(` ${chalk5.cyan("npx shipem init --template nextjs-saas")}`);
2800
+ console.log(` ${chalk5.cyan("npx ship-em init --template astro-blog")}`);
2801
+ console.log(` ${chalk5.cyan("npx ship-em init --template nextjs-saas")}`);
2862
2802
  console.log("");
2863
2803
  }
2864
2804
 
@@ -3010,7 +2950,7 @@ async function initCommand(options = {}) {
3010
2950
  console.log("");
3011
2951
  console.log(` ${brand.green.bold("Ready!")} Now run:`);
3012
2952
  console.log("");
3013
- console.log(` ${chalk6.cyan(`cd ${projectName} && npx shipem`)}`);
2953
+ console.log(` ${chalk6.cyan(`cd ${projectName} && npx ship-em`)}`);
3014
2954
  console.log("");
3015
2955
  }
3016
2956
  async function scaffoldFromTemplate(tpl, options) {
@@ -3091,7 +3031,7 @@ async function scaffoldFromTemplate(tpl, options) {
3091
3031
  console.log("");
3092
3032
  console.log(` ${brand.green.bold("Ready!")} Now run:`);
3093
3033
  console.log("");
3094
- console.log(` ${chalk6.cyan(`cd ${projectName} && npx shipem`)}`);
3034
+ console.log(` ${chalk6.cyan(`cd ${projectName} && npx ship-em`)}`);
3095
3035
  console.log("");
3096
3036
  }
3097
3037
  function createMinimalProject(projectName, description) {
@@ -4076,8 +4016,8 @@ async function previewCommand(options = {}) {
4076
4016
  console.log("");
4077
4017
  console.log(` ${brand.bold("Next steps:")}`);
4078
4018
  console.log(` ${brand.gray("Share")} \u2192 Send the preview URL to teammates`);
4079
- console.log(` ${brand.gray("Update")} \u2192 ${chalk13.cyan("npx shipem preview")} (re-deploy this branch)`);
4080
- console.log(` ${brand.gray("Promote")} \u2192 Merge to main and ${chalk13.cyan("npx shipem deploy")}`);
4019
+ console.log(` ${brand.gray("Update")} \u2192 ${chalk13.cyan("npx ship-em preview")} (re-deploy this branch)`);
4020
+ console.log(` ${brand.gray("Promote")} \u2192 Merge to main and ${chalk13.cyan("npx ship-em deploy")}`);
4081
4021
  console.log("");
4082
4022
  }
4083
4023
 
@@ -4122,22 +4062,22 @@ program.name("shipem").description(
4122
4062
  return "";
4123
4063
  }).addHelpText("after", `
4124
4064
  Quick start:
4125
- npx shipem Deploy your app
4126
- npx shipem preview Deploy a preview from your branch
4127
- npx shipem watch Watch for changes and auto-deploy
4128
- npx shipem init Bootstrap a new project
4129
- npx shipem init -t <t> Init from a template
4130
- npx shipem templates List available templates
4131
- npx shipem fix Auto-fix build errors
4132
- npx shipem env Check environment variables
4133
- npx shipem dev Start dev server with deploy shortcut
4134
- npx shipem status Check deployment status
4135
- npx shipem history Show deploy history across projects
4136
- npx shipem monitor Monitor site uptime
4137
- npx shipem hooks Manage git deploy hooks
4138
- npx shipem config Manage global settings
4139
- npx shipem login Authenticate with GitHub
4140
- npx shipem down Take your app offline
4065
+ npx ship-em Deploy your app
4066
+ npx ship-em preview Deploy a preview from your branch
4067
+ npx ship-em watch Watch for changes and auto-deploy
4068
+ npx ship-em init Bootstrap a new project
4069
+ npx ship-em init -t <t> Init from a template
4070
+ npx ship-em templates List available templates
4071
+ npx ship-em fix Auto-fix build errors
4072
+ npx ship-em env Check environment variables
4073
+ npx ship-em dev Start dev server with deploy shortcut
4074
+ npx ship-em status Check deployment status
4075
+ npx ship-em history Show deploy history across projects
4076
+ npx ship-em monitor Monitor site uptime
4077
+ npx ship-em hooks Manage git deploy hooks
4078
+ npx ship-em config Manage global settings
4079
+ npx ship-em login Authenticate with GitHub
4080
+ npx ship-em down Take your app offline
4141
4081
  `);
4142
4082
  program.command("deploy", { isDefault: true }).description("Detect and deploy your app (default command)").option("-y, --yes", "Skip all prompts and use defaults").option("-n, --name <name>", "Override app name").option("--skip-build", "Skip the build step (deploy existing output directory)").option("--turbo", "Skip the build step (alias for --skip-build)").option("--package <name>", "Deploy a specific package from a monorepo").option("--direct", "Deploy directly using your Cloudflare credentials (bypass Shipem API)").option("--verbose", "Show detailed debug output").option("--json", "Output machine-readable JSON instead of human-friendly text").option("--notify [url]", "Send deploy notification to webhook").option("--quiet", "Minimal output (just URL on success)").action(async (options) => {
4143
4083
  globalVerbose = options.verbose ?? false;
package/dist/lib.js CHANGED
@@ -708,9 +708,9 @@ ${url}`);
708
708
  console.log("");
709
709
  }
710
710
  console.log(` ${brand.bold("Next steps:")}`);
711
- console.log(` ${brand.gray("Update")} \u2192 ${chalk.cyan("npx shipem")}`);
712
- console.log(` ${brand.gray("Status")} \u2192 ${chalk.cyan("npx shipem status")}`);
713
- console.log(` ${brand.gray("Remove")} \u2192 ${chalk.cyan("npx shipem down")}`);
711
+ console.log(` ${brand.gray("Update")} \u2192 ${chalk.cyan("npx ship-em")}`);
712
+ console.log(` ${brand.gray("Status")} \u2192 ${chalk.cyan("npx ship-em status")}`);
713
+ console.log(` ${brand.gray("Remove")} \u2192 ${chalk.cyan("npx ship-em down")}`);
714
714
  console.log("");
715
715
  },
716
716
  // Format bytes
@@ -788,9 +788,9 @@ ${url}`);
788
788
  console.log("");
789
789
  }
790
790
  console.log(` ${brand.bold("Next steps:")}`);
791
- console.log(` ${brand.gray("Update")} \u2192 ${chalk.cyan("npx shipem")}`);
792
- console.log(` ${brand.gray("Status")} \u2192 ${chalk.cyan("npx shipem status")}`);
793
- console.log(` ${brand.gray("Remove")} \u2192 ${chalk.cyan("npx shipem down")}`);
791
+ console.log(` ${brand.gray("Update")} \u2192 ${chalk.cyan("npx ship-em")}`);
792
+ console.log(` ${brand.gray("Status")} \u2192 ${chalk.cyan("npx ship-em status")}`);
793
+ console.log(` ${brand.gray("Remove")} \u2192 ${chalk.cyan("npx ship-em down")}`);
794
794
  console.log("");
795
795
  },
796
796
  // Rich status display for deploy history
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ship-em",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "One-command deployment for apps built by AI coding tools",
5
5
  "type": "module",
6
6
  "bin": {