closeclaw 3.0.13 → 3.0.15

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/cli.cjs CHANGED
@@ -7954,7 +7954,7 @@ function saveLicenseKey(key) {
7954
7954
  }
7955
7955
  async function commandOnboard() {
7956
7956
  const version = readPkgVersion();
7957
- const TOTAL_STEPS = 8;
7957
+ const TOTAL_STEPS = 9;
7958
7958
  console.log("");
7959
7959
  console.log(` ${c.cyan}${c.bold}\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557${c.reset}`);
7960
7960
  console.log(` ${c.cyan}${c.bold}\u2551 CloseClaw \u2014 Setup Wizard \u2551${c.reset}`);
@@ -8174,17 +8174,154 @@ async function commandOnboard() {
8174
8174
  result(INFO, `Enable later: ${c.dim}closeclaw onboard${c.reset} or edit ${c.dim}~/.closeclaw/config.json${c.reset}`);
8175
8175
  }
8176
8176
  }
8177
- step(8, TOTAL_STEPS, "Ready!");
8177
+ step(8, TOTAL_STEPS, "Domain & SSL (optional)");
8178
+ if (config.domain) {
8179
+ result(PASS, `Domain configured: ${c.bold}${config.domain}${c.reset}`);
8180
+ const caddyBin = (0, import_node_path6.join)(CONFIG_DIR, "bin", "caddy");
8181
+ if ((0, import_node_fs7.existsSync)(caddyBin)) {
8182
+ result(PASS, "Caddy installed");
8183
+ } else {
8184
+ result(WARN, "Caddy not installed \u2014 will download on first start with --domain");
8185
+ }
8186
+ } else {
8187
+ const setupDomain = await ask(` Set up a public domain with HTTPS? ${c.dim}(y/N)${c.reset} `);
8188
+ if (setupDomain.toLowerCase() === "y" || setupDomain.toLowerCase() === "yes") {
8189
+ console.log("");
8190
+ console.log(` ${c.bold}Before continuing, you need:${c.reset}`);
8191
+ console.log(` ${c.cyan}1.${c.reset} A server with a public IP address`);
8192
+ console.log(` ${c.cyan}2.${c.reset} A domain pointing to that IP`);
8193
+ console.log(` ${c.cyan}3.${c.reset} Ports 80 and 443 open in your firewall`);
8194
+ console.log("");
8195
+ console.log(` ${c.bold}How to add the DNS record:${c.reset}`);
8196
+ console.log("");
8197
+ console.log(` ${c.yellow}Hostinger:${c.reset}`);
8198
+ console.log(` ${c.dim}\u2192 hPanel \u2192 Domains \u2192 DNS/Nameservers \u2192 DNS Records${c.reset}`);
8199
+ console.log(` ${c.dim}\u2192 Add Record: Type=A, Name=app, Points to=YOUR_SERVER_IP${c.reset}`);
8200
+ console.log("");
8201
+ console.log(` ${c.yellow}AWS Route 53:${c.reset}`);
8202
+ console.log(` ${c.dim}\u2192 Route 53 \u2192 Hosted Zones \u2192 your domain \u2192 Create Record${c.reset}`);
8203
+ console.log(` ${c.dim}\u2192 Record type=A, Record name=app, Value=YOUR_SERVER_IP${c.reset}`);
8204
+ console.log("");
8205
+ console.log(` ${c.yellow}Cloudflare:${c.reset}`);
8206
+ console.log(` ${c.dim}\u2192 DNS \u2192 Records \u2192 Add Record${c.reset}`);
8207
+ console.log(` ${c.dim}\u2192 Type=A, Name=app, IPv4=YOUR_SERVER_IP, Proxy=OFF${c.reset}`);
8208
+ console.log("");
8209
+ console.log(` ${c.yellow}GoDaddy:${c.reset}`);
8210
+ console.log(` ${c.dim}\u2192 My Products \u2192 DNS \u2192 Add Record${c.reset}`);
8211
+ console.log(` ${c.dim}\u2192 Type=A, Name=app, Value=YOUR_SERVER_IP, TTL=600${c.reset}`);
8212
+ console.log("");
8213
+ console.log(` ${c.yellow}Namecheap:${c.reset}`);
8214
+ console.log(` ${c.dim}\u2192 Domain List \u2192 Manage \u2192 Advanced DNS \u2192 Add New Record${c.reset}`);
8215
+ console.log(` ${c.dim}\u2192 Type=A, Host=app, Value=YOUR_SERVER_IP${c.reset}`);
8216
+ console.log("");
8217
+ const domain = await ask(` Your domain (e.g. app.company.com): `);
8218
+ if (domain) {
8219
+ config.domain = domain;
8220
+ console.log("");
8221
+ result(INFO, `Checking DNS for ${domain}...`);
8222
+ try {
8223
+ const dns = require("dns");
8224
+ const resolved = await new Promise((resolve, reject) => {
8225
+ dns.resolve4(domain, (err, addresses) => {
8226
+ if (err) reject(err);
8227
+ else resolve(addresses);
8228
+ });
8229
+ });
8230
+ result(PASS, `DNS resolves to ${c.bold}${resolved.join(", ")}${c.reset}`);
8231
+ } catch {
8232
+ result(WARN, "DNS not resolving yet \u2014 it can take up to 48h to propagate");
8233
+ result(INFO, "You can still proceed \u2014 Caddy will retry on start");
8234
+ }
8235
+ result(INFO, "Installing Caddy (reverse proxy for auto-SSL)...");
8236
+ const caddyDir = (0, import_node_path6.join)(CONFIG_DIR, "bin");
8237
+ const caddyBin = (0, import_node_path6.join)(caddyDir, "caddy");
8238
+ if ((0, import_node_fs7.existsSync)(caddyBin)) {
8239
+ result(PASS, "Caddy already installed");
8240
+ } else {
8241
+ try {
8242
+ (0, import_node_fs7.mkdirSync)(caddyDir, { recursive: true });
8243
+ const os2 = require("os");
8244
+ const platform = os2.platform();
8245
+ const arch = os2.arch();
8246
+ const caddyOS = platform === "darwin" ? "mac" : "linux";
8247
+ const caddyArch = arch === "arm64" ? "arm64" : "amd64";
8248
+ const version2 = "2.9.1";
8249
+ const ext = platform === "darwin" ? "zip" : "tar.gz";
8250
+ const url = `https://github.com/caddyserver/caddy/releases/download/v${version2}/caddy_${version2}_${caddyOS}_${caddyArch}.${ext}`;
8251
+ result(INFO, `Downloading from ${c.dim}${url}${c.reset}`);
8252
+ if (ext === "tar.gz") {
8253
+ (0, import_node_child_process3.execSync)(`curl -fsSL "${url}" | tar xz -C "${caddyDir}" caddy`, { stdio: "pipe", timeout: 12e4 });
8254
+ } else {
8255
+ const tmpZip = (0, import_node_path6.join)(caddyDir, "caddy.zip");
8256
+ (0, import_node_child_process3.execSync)(`curl -fsSL -o "${tmpZip}" "${url}"`, { stdio: "pipe", timeout: 12e4 });
8257
+ (0, import_node_child_process3.execSync)(`unzip -o -j "${tmpZip}" caddy -d "${caddyDir}"`, { stdio: "pipe" });
8258
+ (0, import_node_fs7.unlinkSync)(tmpZip);
8259
+ }
8260
+ (0, import_node_child_process3.execSync)(`chmod +x "${caddyBin}"`);
8261
+ result(PASS, `Caddy installed at ${c.dim}${caddyBin}${c.reset}`);
8262
+ } catch (err) {
8263
+ result(FAIL, `Could not download Caddy: ${err.message?.split("\n")[0]}`);
8264
+ result(INFO, `Install manually: ${c.dim}https://caddyserver.com/download${c.reset}`);
8265
+ result(INFO, `Then copy the binary to ${c.dim}${caddyDir}/caddy${c.reset}`);
8266
+ }
8267
+ }
8268
+ console.log("");
8269
+ result(INFO, "Checking ports 80 and 443...");
8270
+ for (const p of [80, 443]) {
8271
+ try {
8272
+ const net = require("net");
8273
+ const inUse = await new Promise((resolve) => {
8274
+ const srv = net.createServer();
8275
+ srv.once("error", () => resolve(true));
8276
+ srv.once("listening", () => {
8277
+ srv.close();
8278
+ resolve(false);
8279
+ });
8280
+ srv.listen(p, "0.0.0.0");
8281
+ });
8282
+ if (inUse) {
8283
+ result(WARN, `Port ${p} is in use \u2014 Caddy needs it free`);
8284
+ } else {
8285
+ result(PASS, `Port ${p} available`);
8286
+ }
8287
+ } catch {
8288
+ result(WARN, `Could not check port ${p} (may need root/sudo)`);
8289
+ }
8290
+ }
8291
+ (0, import_node_fs7.mkdirSync)(CONFIG_DIR, { recursive: true });
8292
+ (0, import_node_fs7.writeFileSync)(configPath, JSON.stringify(config, null, 2));
8293
+ result(PASS, "Domain configuration saved");
8294
+ console.log("");
8295
+ console.log(` ${c.bold}To start with HTTPS:${c.reset}`);
8296
+ console.log(` ${c.cyan}closeclaw start --domain ${domain}${c.reset}`);
8297
+ console.log("");
8298
+ console.log(` ${c.dim}Caddy will auto-provision an SSL certificate from Let's Encrypt.${c.reset}`);
8299
+ console.log(` ${c.dim}First request may take ~5 seconds while the cert is issued.${c.reset}`);
8300
+ } else {
8301
+ result(SKIP, "Skipped");
8302
+ }
8303
+ } else {
8304
+ result(SKIP, "Localhost only \u2014 no HTTPS");
8305
+ result(INFO, `Add later: ${c.dim}closeclaw start --domain app.company.com${c.reset}`);
8306
+ }
8307
+ }
8308
+ step(9, TOTAL_STEPS, "Ready!");
8178
8309
  console.log("");
8179
8310
  console.log(` ${c.green}${c.bold}Setup complete.${c.reset} Start the server with:`);
8180
8311
  console.log("");
8181
- if (licenseKey) {
8312
+ if (config.domain) {
8182
8313
  console.log(` ${c.cyan}closeclaw start${c.reset}`);
8314
+ console.log("");
8315
+ console.log(` Then open ${c.bold}https://${config.domain}${c.reset} in your browser.`);
8316
+ } else if (licenseKey) {
8317
+ console.log(` ${c.cyan}closeclaw start${c.reset}`);
8318
+ console.log("");
8319
+ console.log(` Then open ${c.bold}http://localhost:3001${c.reset} in your browser.`);
8183
8320
  } else {
8184
8321
  console.log(` ${c.cyan}closeclaw start${c.reset} ${c.dim}(dev mode)${c.reset}`);
8322
+ console.log("");
8323
+ console.log(` Then open ${c.bold}http://localhost:3001${c.reset} in your browser.`);
8185
8324
  }
8186
- console.log("");
8187
- console.log(` Then open ${c.bold}http://localhost:3001${c.reset} in your browser.`);
8188
8325
  console.log(` First signup becomes admin.`);
8189
8326
  console.log("");
8190
8327
  const startNow = await ask(` Start the server now? ${c.dim}(Y/n)${c.reset} `);
@@ -8211,7 +8348,13 @@ async function commandStart(args) {
8211
8348
  const host2 = values.host;
8212
8349
  let licenseKey = values.key || getSavedLicenseKey() || "";
8213
8350
  const dataDir = values["data-dir"];
8214
- const domain = values.domain;
8351
+ let domain = values.domain;
8352
+ if (!domain) {
8353
+ try {
8354
+ domain = JSON.parse((0, import_node_fs7.readFileSync)((0, import_node_path6.join)(CONFIG_DIR, "config.json"), "utf-8")).domain || "";
8355
+ } catch {
8356
+ }
8357
+ }
8215
8358
  process.env.PORT = String(port2);
8216
8359
  process.env.HOST = host2;
8217
8360
  if (licenseKey) process.env.PLATFORM_LICENSE_KEY = licenseKey;
@@ -8267,17 +8410,72 @@ async function commandStart(args) {
8267
8410
  }
8268
8411
  await Promise.resolve().then(() => (init_index(), index_exports));
8269
8412
  writePid();
8413
+ let caddyProcess = null;
8414
+ const effectiveDomain = domain || (() => {
8415
+ try {
8416
+ return JSON.parse((0, import_node_fs7.readFileSync)((0, import_node_path6.join)(CONFIG_DIR, "config.json"), "utf-8")).domain;
8417
+ } catch {
8418
+ return "";
8419
+ }
8420
+ })();
8421
+ if (effectiveDomain) {
8422
+ const caddyBin = (0, import_node_path6.join)(CONFIG_DIR, "bin", "caddy");
8423
+ if ((0, import_node_fs7.existsSync)(caddyBin)) {
8424
+ console.log(` ${c.dim}Starting Caddy reverse proxy for ${effectiveDomain}...${c.reset}`);
8425
+ const { spawn: spawnChild } = require("child_process");
8426
+ caddyProcess = spawnChild(caddyBin, [
8427
+ "reverse-proxy",
8428
+ "--from",
8429
+ effectiveDomain,
8430
+ "--to",
8431
+ `localhost:${port2}`
8432
+ ], {
8433
+ stdio: ["ignore", "pipe", "pipe"],
8434
+ detached: false
8435
+ });
8436
+ caddyProcess.stdout?.on("data", (d) => {
8437
+ const msg = d.toString().trim();
8438
+ if (msg) console.log(` ${c.dim}[caddy]${c.reset} ${msg}`);
8439
+ });
8440
+ caddyProcess.stderr?.on("data", (d) => {
8441
+ const msg = d.toString().trim();
8442
+ if (msg) console.log(` ${c.dim}[caddy]${c.reset} ${msg}`);
8443
+ });
8444
+ caddyProcess.on("exit", (code) => {
8445
+ if (code) console.error(` ${c.red}[caddy] Exited with code ${code}${c.reset}`);
8446
+ });
8447
+ const killCaddy = () => {
8448
+ try {
8449
+ caddyProcess?.kill("SIGTERM");
8450
+ } catch {
8451
+ }
8452
+ };
8453
+ process.on("SIGTERM", killCaddy);
8454
+ process.on("SIGINT", killCaddy);
8455
+ process.on("exit", killCaddy);
8456
+ } else {
8457
+ console.log(` ${c.yellow}Caddy not found at ${caddyBin}${c.reset}`);
8458
+ console.log(` ${c.yellow}Run ${c.cyan}closeclaw onboard${c.reset}${c.yellow} to install Caddy, or download from https://caddyserver.com${c.reset}`);
8459
+ }
8460
+ }
8270
8461
  const version = readPkgVersion();
8271
8462
  const localUrl = `http://localhost:${port2}`;
8463
+ const publicUrl = effectiveDomain ? `https://${effectiveDomain}` : null;
8272
8464
  console.log("");
8273
8465
  console.log(` ${c.green}${c.bold}\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557${c.reset}`);
8274
8466
  console.log(` ${c.green}${c.bold}\u2551 CloseClaw v${version.padEnd(31)}\u2551${c.reset}`);
8275
8467
  console.log(` ${c.green}${c.bold}\u2551 \u2551${c.reset}`);
8468
+ if (publicUrl) {
8469
+ console.log(` ${c.green}${c.bold}\u2551${c.reset} ${c.cyan}${publicUrl.padEnd(40)}${c.reset}${c.green}${c.bold}\u2551${c.reset}`);
8470
+ }
8276
8471
  console.log(` ${c.green}${c.bold}\u2551${c.reset} ${c.cyan}${localUrl.padEnd(40)}${c.reset}${c.green}${c.bold}\u2551${c.reset}`);
8277
8472
  console.log(` ${c.green}${c.bold}\u2551${c.reset} ${c.dim}PID: ${String(process.pid).padEnd(36)}${c.reset}${c.green}${c.bold}\u2551${c.reset}`);
8278
8473
  if (openclawPath) {
8279
8474
  console.log(` ${c.green}${c.bold}\u2551${c.reset} ${c.dim}OpenClaw: port 18789${" ".repeat(21)}${c.reset}${c.green}${c.bold}\u2551${c.reset}`);
8280
8475
  }
8476
+ if (publicUrl) {
8477
+ console.log(` ${c.green}${c.bold}\u2551${c.reset} ${c.dim}SSL: auto (Let's Encrypt via Caddy)${" ".repeat(5)}${c.reset}${c.green}${c.bold}\u2551${c.reset}`);
8478
+ }
8281
8479
  console.log(` ${c.green}${c.bold}\u2551 \u2551${c.reset}`);
8282
8480
  console.log(` ${c.green}${c.bold}\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D${c.reset}`);
8283
8481
  console.log("");
package/dist/cli.jsc CHANGED
Binary file
package/dist/index.jsc CHANGED
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "closeclaw",
3
- "version": "3.0.13",
3
+ "version": "3.0.15",
4
4
  "description": "CloseClaw — AI-powered project management platform. One command, full stack.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",