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 +8 -8
- package/dist/index.js +66 -126
- package/dist/lib.js +6 -6
- package/package.json +1 -1
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
|
|
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
|
|
23
|
-
npx
|
|
24
|
-
npx
|
|
25
|
-
npx
|
|
26
|
-
npx
|
|
27
|
-
npx
|
|
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
|
|
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
|
|
272
|
-
console.log(` ${brand.gray("Status")} \u2192 ${chalk.cyan("npx
|
|
273
|
-
console.log(` ${brand.gray("Remove")} \u2192 ${chalk.cyan("npx
|
|
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
|
|
352
|
-
console.log(` ${brand.gray("Status")} \u2192 ${chalk.cyan("npx
|
|
353
|
-
console.log(` ${brand.gray("Remove")} \u2192 ${chalk.cyan("npx
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
2861
|
-
console.log(` ${chalk5.cyan("npx
|
|
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
|
|
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
|
|
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
|
|
4080
|
-
console.log(` ${brand.gray("Promote")} \u2192 Merge to main and ${chalk13.cyan("npx
|
|
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
|
|
4126
|
-
npx
|
|
4127
|
-
npx
|
|
4128
|
-
npx
|
|
4129
|
-
npx
|
|
4130
|
-
npx
|
|
4131
|
-
npx
|
|
4132
|
-
npx
|
|
4133
|
-
npx
|
|
4134
|
-
npx
|
|
4135
|
-
npx
|
|
4136
|
-
npx
|
|
4137
|
-
npx
|
|
4138
|
-
npx
|
|
4139
|
-
npx
|
|
4140
|
-
npx
|
|
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
|
|
712
|
-
console.log(` ${brand.gray("Status")} \u2192 ${chalk.cyan("npx
|
|
713
|
-
console.log(` ${brand.gray("Remove")} \u2192 ${chalk.cyan("npx
|
|
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
|
|
792
|
-
console.log(` ${brand.gray("Status")} \u2192 ${chalk.cyan("npx
|
|
793
|
-
console.log(` ${brand.gray("Remove")} \u2192 ${chalk.cyan("npx
|
|
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
|