diff --git a/nemoclaw/src/index.ts b/nemoclaw/src/index.ts index 8a8ac9f09..2572d0800 100644 --- a/nemoclaw/src/index.ts +++ b/nemoclaw/src/index.ts @@ -252,14 +252,25 @@ export default function register(api: OpenClawPluginApi): void { const bannerProvider = onboardCfg ? describeOnboardProvider(onboardCfg) : "NVIDIA Endpoints"; const bannerModel = onboardCfg?.model ?? "nvidia/nemotron-3-super-120b-a12b"; + const bannerPrefix = 13; // length of " Endpoint: " / " Provider: " / " Model: " + const bannerMinInner = 53; + const bannerMaxValueLen = Math.max( + bannerEndpoint.length, + bannerProvider.length, + bannerModel.length, + ); + const bannerInner = Math.max(bannerMinInner, bannerPrefix + bannerMaxValueLen + 2); + const bannerPad = (s: string) => s + " ".repeat(bannerInner - s.length); + const bannerHBar = "─".repeat(bannerInner); + api.logger.info(""); - api.logger.info(" ┌─────────────────────────────────────────────────────┐"); - api.logger.info(" │ NemoClaw registered │"); - api.logger.info(" │ │"); - api.logger.info(` │ Endpoint: ${bannerEndpoint.padEnd(40)}│`); - api.logger.info(` │ Provider: ${bannerProvider.padEnd(40)}│`); - api.logger.info(` │ Model: ${bannerModel.padEnd(40)}│`); - api.logger.info(" │ Slash: /nemoclaw │"); - api.logger.info(" └─────────────────────────────────────────────────────┘"); + api.logger.info(` ┌${bannerHBar}┐`); + api.logger.info(` │${bannerPad(" NemoClaw registered")}│`); + api.logger.info(` │${" ".repeat(bannerInner)}│`); + api.logger.info(` │${bannerPad(" Endpoint: " + bannerEndpoint)}│`); + api.logger.info(` │${bannerPad(" Provider: " + bannerProvider)}│`); + api.logger.info(` │${bannerPad(" Model: " + bannerModel)}│`); + api.logger.info(` │${bannerPad(" Slash: /nemoclaw")}│`); + api.logger.info(` └${bannerHBar}┘`); api.logger.info(""); } diff --git a/scripts/start-services.sh b/scripts/start-services.sh index 0c64d1341..825d1d401 100755 --- a/scripts/start-services.sh +++ b/scripts/start-services.sh @@ -179,29 +179,36 @@ do_start() { fi # Print banner - echo "" - echo " ┌─────────────────────────────────────────────────────┐" - echo " │ NemoClaw Services │" - echo " │ │" - local tunnel_url="" if [ -f "$PIDDIR/cloudflared.log" ]; then tunnel_url="$(grep -o 'https://[a-z0-9-]*\.trycloudflare\.com' "$PIDDIR/cloudflared.log" 2>/dev/null | head -1 || true)" fi + # Expand box width if the URL is longer than the default inner width (53) + local min_inner=53 + local inner=$min_inner if [ -n "$tunnel_url" ]; then - printf " │ Public URL: %-40s│\n" "$tunnel_url" + local url_inner=$((${#tunnel_url} + 17)) # " Public URL: " = 15 chars + 2 trailing spaces + [ "$url_inner" -gt "$inner" ] && inner=$url_inner fi + local h_bar="" + for ((i = 0; i < inner; i++)); do h_bar+="─"; done + echo "" + printf " ┌%s┐\n" "$h_bar" + printf " │ NemoClaw Services%-*s│\n" $((inner - 19)) "" + printf " │%-*s│\n" "$inner" "" + if [ -n "$tunnel_url" ]; then + printf " │ Public URL: %-*s│\n" $((inner - 15)) "$tunnel_url" + fi if is_running telegram-bridge; then - echo " │ Telegram: bridge running │" + printf " │ Telegram: bridge running%-*s│\n" $((inner - 29)) "" else - echo " │ Telegram: not started (no token) │" + printf " │ Telegram: not started (no token)%-*s│\n" $((inner - 37)) "" fi - - echo " │ │" - echo " │ Run 'openshell term' to monitor egress approvals │" - echo " └─────────────────────────────────────────────────────┘" + printf " │%-*s│\n" "$inner" "" + printf " │ Run 'openshell term' to monitor egress approvals%-*s│\n" $((inner - 50)) "" + printf " └%s┘\n" "$h_bar" echo "" } diff --git a/src/lib/services.ts b/src/lib/services.ts index 9582a5921..378a350bf 100644 --- a/src/lib/services.ts +++ b/src/lib/services.ts @@ -334,11 +334,6 @@ export async function startAll(opts: ServiceOptions = {}): Promise { } // Banner - console.log(""); - console.log(" ┌─────────────────────────────────────────────────────┐"); - console.log(" │ NemoClaw Services │"); - console.log(" │ │"); - let tunnelUrl = ""; const cfLogFile = join(pidDir, "cloudflared.log"); if (isRunning(pidDir, "cloudflared") && existsSync(cfLogFile)) { @@ -349,19 +344,31 @@ export async function startAll(opts: ServiceOptions = {}): Promise { } } - if (tunnelUrl) { - console.log(` │ Public URL: ${tunnelUrl.padEnd(40)}│`); - } + const titleText = " NemoClaw Services"; + const urlPrefix = " Public URL: "; + const telegramText = isRunning(pidDir, "telegram-bridge") + ? " Telegram: bridge running" + : " Telegram: not started (no token)"; + const footerText = " Run 'openshell term' to monitor egress approvals"; - if (isRunning(pidDir, "telegram-bridge")) { - console.log(" │ Telegram: bridge running │"); - } else { - console.log(" │ Telegram: not started (no token) │"); - } + // Expand box width if the URL is longer than the default inner width (53) + const minInner = 53; + const inner = tunnelUrl ? Math.max(minInner, urlPrefix.length + tunnelUrl.length + 2) : minInner; - console.log(" │ │"); - console.log(" │ Run 'openshell term' to monitor egress approvals │"); - console.log(" └─────────────────────────────────────────────────────┘"); + const pad = (s: string) => s + " ".repeat(inner - s.length); + const hBar = "─".repeat(inner); + + console.log(""); + console.log(` ┌${hBar}┐`); + console.log(` │${pad(titleText)}│`); + console.log(` │${" ".repeat(inner)}│`); + if (tunnelUrl) { + console.log(` │${pad(urlPrefix + tunnelUrl)}│`); + } + console.log(` │${pad(telegramText)}│`); + console.log(` │${" ".repeat(inner)}│`); + console.log(` │${pad(footerText)}│`); + console.log(` └${hBar}┘`); console.log(""); }