shiplocal 0.1.0 → 0.1.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.
package/README.md ADDED
@@ -0,0 +1,117 @@
1
+ # ShipLocal CLI
2
+
3
+ > From localhost to client-ready.
4
+
5
+ Share your local dev server with clients via a public URL. Clients can leave visual feedback on the live preview.
6
+
7
+ **Dashboard:** https://app.shiplocal.cloud
8
+ **Website:** https://shiplocal.cloud
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ npm install -g shiplocal
14
+ ```
15
+
16
+ Requires **Node.js 20+**.
17
+
18
+ ## Quick start
19
+
20
+ ### 1. Log in
21
+
22
+ ```bash
23
+ shiplocal login
24
+ ```
25
+
26
+ Create an account at https://app.shiplocal.cloud if you don't have one.
27
+
28
+ No `export` needed — the CLI defaults to **https://shiplocal.cloud** and saves your server URL when you log in.
29
+
30
+ ### 2. Start your local app
31
+
32
+ Run your project locally on a port — for example **3000**:
33
+
34
+ ```bash
35
+ npm run dev
36
+ ```
37
+
38
+ ### 3. Open a tunnel
39
+
40
+ ```bash
41
+ shiplocal 3000
42
+ ```
43
+
44
+ You'll get output like:
45
+
46
+ ```
47
+ 🚀 ShipLocal running
48
+
49
+ Local: http://localhost:3000
50
+ Public: https://happy-lion.shiplocal.cloud
51
+
52
+ Share this with your client.
53
+ ```
54
+
55
+ Share the **Public** URL with your client. Keep this terminal open while they view the preview.
56
+
57
+ ### 4. Client feedback
58
+
59
+ Clients open the public URL, click **💬**, pick an element, and leave feedback. You see comments and screenshots on your [dashboard](https://app.shiplocal.cloud/dashboard).
60
+
61
+ ## Commands
62
+
63
+ | Command | Description |
64
+ | ---------------------------------- | --------------------------------- |
65
+ | `shiplocal login` | Authenticate with ShipLocal Cloud |
66
+ | `shiplocal logout` | Remove saved credentials |
67
+ | `shiplocal 3000` | Tunnel local port 3000 |
68
+ | `shiplocal 3000 --password secret` | Password-protect the preview URL |
69
+
70
+ ## Environment variables (optional)
71
+
72
+ | Variable | When to use |
73
+ | ------------------- | -------------------------------------------------------------- |
74
+ | `SHIPLOCAL_API_URL` | Self-hosted server or local dev (e.g. `http://localhost:4000`) |
75
+ | `SHIPLOCAL_TOKEN` | Skip `login` by providing an API token directly |
76
+
77
+ Credentials are saved to `~/.shiplocal/config.json` including the API URL from your last login.
78
+
79
+ **You do not need `export SHIPLOCAL_API_URL` for ShipLocal Cloud** — only set it if you use a custom server.
80
+
81
+ ### Local development against your own server
82
+
83
+ ```bash
84
+ export SHIPLOCAL_API_URL=http://localhost:4000
85
+ shiplocal login
86
+ shiplocal 3000
87
+ ```
88
+
89
+ ## Self-hosting
90
+
91
+ ```bash
92
+ export SHIPLOCAL_API_URL=https://your-server.example.com
93
+ shiplocal login
94
+ ```
95
+
96
+ See the [self-hosting guide](https://github.com/ship-local/shiplocal/blob/main/docs/self-hosting.md).
97
+
98
+ ## Troubleshooting
99
+
100
+ **`command not found: shiplocal`**
101
+ Run `npm bin -g` and add that directory to your PATH.
102
+
103
+ **Blank page on public URL**
104
+ Make sure your local app is running on the port you passed to the CLI.
105
+
106
+ **`Not authenticated`**
107
+ Run `shiplocal login` first.
108
+
109
+ ## Links
110
+
111
+ - [GitHub](https://github.com/ship-local/shiplocal)
112
+ - [Dashboard](https://app.shiplocal.cloud)
113
+ - [Issues](https://github.com/ship-local/shiplocal/issues)
114
+
115
+ ## License
116
+
117
+ MIT
package/dist/index.js CHANGED
@@ -4444,8 +4444,16 @@ async function clearConfig() {
4444
4444
  } catch {
4445
4445
  }
4446
4446
  }
4447
- function resolveApiUrl() {
4448
- return process.env["SHIPLOCAL_API_URL"] ?? "http://localhost:4000";
4447
+ var DEFAULT_CLOUD_API_URL = "https://shiplocal.cloud";
4448
+ async function resolveApiUrlAsync() {
4449
+ if (process.env["SHIPLOCAL_API_URL"]) {
4450
+ return process.env["SHIPLOCAL_API_URL"];
4451
+ }
4452
+ const config = await loadConfig();
4453
+ if (config?.apiUrl) {
4454
+ return config.apiUrl;
4455
+ }
4456
+ return DEFAULT_CLOUD_API_URL;
4449
4457
  }
4450
4458
  async function resolveToken() {
4451
4459
  if (process.env["SHIPLOCAL_TOKEN"]) return process.env["SHIPLOCAL_TOKEN"];
@@ -4479,16 +4487,17 @@ function formatServerConnectionError(apiUrl, err) {
4479
4487
  }
4480
4488
  return err instanceof Error ? err.message : "Request failed";
4481
4489
  }
4482
- async function postJson(path, body, apiUrl = resolveApiUrl()) {
4490
+ async function postJson(path, body, apiUrl) {
4491
+ const baseUrl = apiUrl ?? await resolveApiUrlAsync();
4483
4492
  let response;
4484
4493
  try {
4485
- response = await fetch(`${apiUrl}${path}`, {
4494
+ response = await fetch(`${baseUrl}${path}`, {
4486
4495
  method: "POST",
4487
4496
  headers: { "Content-Type": "application/json" },
4488
4497
  body: JSON.stringify(body)
4489
4498
  });
4490
4499
  } catch (err) {
4491
- throw new Error(formatServerConnectionError(apiUrl, err));
4500
+ throw new Error(formatServerConnectionError(baseUrl, err));
4492
4501
  }
4493
4502
  const data = await response.json().catch(() => ({}));
4494
4503
  return { response, data };
@@ -4519,10 +4528,10 @@ function isLocalPortOpen(port, timeoutMs = 2e3) {
4519
4528
 
4520
4529
  // src/index.ts
4521
4530
  var program = new Command();
4522
- program.name("shiplocal").description("Share localhost with clients in seconds").version("0.1.0");
4531
+ program.name("shiplocal").description("Share localhost with clients in seconds").version("0.1.2");
4523
4532
  program.command("login").description("Authenticate with ShipLocal Cloud").action(async () => {
4524
4533
  const rl = createInterface({ input, output });
4525
- const apiUrl = resolveApiUrl();
4534
+ const apiUrl = await resolveApiUrlAsync();
4526
4535
  try {
4527
4536
  const email = await rl.question("Email: ");
4528
4537
  const password = await rl.question("Password: ");
@@ -4556,7 +4565,7 @@ program.argument("[port]", "Local port to expose", String(DEFAULT_TUNNEL_PORT)).
4556
4565
  console.error("Error: port must be a number between 1 and 65535");
4557
4566
  process.exit(1);
4558
4567
  }
4559
- const serverUrl = resolveApiUrl();
4568
+ const serverUrl = await resolveApiUrlAsync();
4560
4569
  const token = await resolveToken();
4561
4570
  if (!token) {
4562
4571
  console.error("Not authenticated. Run `shiplocal login` first.");
@@ -4612,7 +4621,7 @@ program.argument("[port]", "Local port to expose", String(DEFAULT_TUNNEL_PORT)).
4612
4621
  try {
4613
4622
  await client.connect();
4614
4623
  } catch (err) {
4615
- const apiUrl = resolveApiUrl();
4624
+ const apiUrl = await resolveApiUrlAsync();
4616
4625
  const message = err instanceof Error && err.message.includes("ECONNREFUSED") ? [
4617
4626
  `Cannot reach ShipLocal server at ${apiUrl}.`,
4618
4627
  "",