UNPKG

decocms

Version:

CLI for managing deco.chat apps & projects

124 lines 4.49 kB
import { spawn } from "child_process"; import { connect } from "@deco-cx/warp-node"; import chalk from "chalk"; import { getAppDomain, getConfig, readWranglerConfig, } from "../../lib/config.js"; import { createServer } from "net"; import process from "node:process"; function copyToClipboard(text) { try { let command; let args = []; switch (process.platform) { case "darwin": command = "pbcopy"; break; case "win32": command = "clip"; break; case "linux": command = "xclip"; args = ["-selection", "clipboard"]; break; default: return Promise.resolve(false); } return new Promise((resolve) => { const clipProcess = spawn(command, args, { stdio: "pipe" }); clipProcess.stdin.write(text); clipProcess.stdin.end(); clipProcess.on("close", (code) => { resolve(code === 0); }); clipProcess.on("error", () => { resolve(false); }); }); } catch { return Promise.resolve(false); } } async function findRunningAddr(port) { const LOCALHOST_ENDPOINTS = ["localhost", "127.0.0.1", "0.0.0.0"]; for (const endpoint of LOCALHOST_ENDPOINTS) { try { const server = createServer(); await new Promise((resolve, reject) => { server.listen(port, endpoint, () => { server.close(); resolve(); }); server.on("error", reject); }); // Port is available on this endpoint, continue checking others } catch { // Port is in use on this endpoint return endpoint; } } // Port is available on all endpoints return null; } async function waitForPort(port) { let addr = await findRunningAddr(port); if (addr) { return addr; } console.log(chalk.yellow(`Waiting for port ${port} to become available...`)); while (!addr) { await new Promise((resolve) => setTimeout(resolve, 1000)); addr = await findRunningAddr(port); } console.log(chalk.green(`Port ${port} is now available!`)); return addr; } async function monitorPortAvailability(port) { while (true) { const isAvailable = await findRunningAddr(port); if (!isAvailable) { console.log(chalk.red(`⚠️ Warning: Port ${port} is no longer available!`)); } await new Promise((resolve) => setTimeout(resolve, 2000)); // Check every 2 seconds } } async function register(port, domain, onBeforeRegister) { const server = `wss://${domain}`; try { // Start port monitoring in the background monitorPortAvailability(port).catch((err) => { console.error("Port monitoring error:", err); }); onBeforeRegister?.(); // Wait for port to become available before connecting const host = await waitForPort(port); const localAddr = `http://${host}:${port}`; const tunnel = await connect({ domain, localAddr, server, apiKey: process.env.DECO_TUNNEL_SERVER_TOKEN ?? "c309424a-2dc4-46fe-bfc7-a7c10df59477", }); await tunnel.registered; const serverUrl = `https://${domain}`; const copied = await copyToClipboard(serverUrl); console.log(`\nTunnel started \n -> 🌐 ${chalk.bold("Preview")}: ${chalk.cyan(serverUrl)}${copied ? chalk.dim(" (copied to clipboard)") : ""}`); await tunnel.closed; } catch (err) { console.log("Tunnel connection error, retrying in 500ms...", err); await new Promise((resolve) => setTimeout(resolve, 500)); return register(port, domain); } } export const link = async ({ port = 8787, onBeforeRegister, } = {}) => { // Get config to extract workspace and app const config = await getConfig({}); const wranglerConfig = await readWranglerConfig(); const app = typeof wranglerConfig.name === "string" ? wranglerConfig.name : "my-app"; // Generate app domain based on workspace and app name const appDomain = getAppDomain(config.workspace, app); await register(port, appDomain, onBeforeRegister); }; //# sourceMappingURL=link.js.map