@toon-protocol/townhouse 0.1.1 → 0.1.3

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.js CHANGED
@@ -27,7 +27,7 @@ import {
27
27
  serviceFromContainerName,
28
28
  tailContainerLogs,
29
29
  writeHsConnectorConfig
30
- } from "./chunk-W33MEOPM.js";
30
+ } from "./chunk-QHFUIWEN.js";
31
31
  import "./chunk-5O4SBV5O.js";
32
32
  import {
33
33
  CONTAINER_PREFIX
@@ -46,7 +46,8 @@ import {
46
46
  existsSync,
47
47
  renameSync,
48
48
  rmSync,
49
- statSync
49
+ statSync,
50
+ realpathSync
50
51
  } from "fs";
51
52
  import { join, resolve, dirname } from "path";
52
53
  import { homedir } from "os";
@@ -2026,6 +2027,18 @@ Flags:
2026
2027
  If no flags given, starts all enabled nodes from config.`;
2027
2028
  var DEFAULT_CONFIG_DIR = join(homedir(), ".townhouse");
2028
2029
  var DEFAULT_CONFIG_PATH = join(DEFAULT_CONFIG_DIR, "config.yaml");
2030
+ function printInitNextStep(dir) {
2031
+ const isDefaultDir = dir === resolve(DEFAULT_CONFIG_DIR);
2032
+ const cmd = isDefaultDir ? "npx @toon-protocol/townhouse hs up" : `npx @toon-protocol/townhouse hs up -c ${join(dir, "config.yaml")}`;
2033
+ console.log("");
2034
+ console.log("Next \u2014 start your node:");
2035
+ console.log(` ${cmd}`);
2036
+ console.log("");
2037
+ console.log(
2038
+ "First run pulls container images and bootstraps a hidden service."
2039
+ );
2040
+ console.log("It can take a few minutes; progress is shown throughout.");
2041
+ }
2029
2042
  async function handleInit(force, configDir, password, preset, yes) {
2030
2043
  const dir = resolve(configDir ?? DEFAULT_CONFIG_DIR);
2031
2044
  const configPath = join(dir, "config.yaml");
@@ -2059,9 +2072,17 @@ async function handleInit(force, configDir, password, preset, yes) {
2059
2072
  console.log(`Config created at ${configPath}`);
2060
2073
  const walletPath = join(dir, "wallet.enc");
2061
2074
  if (existsSync(walletPath) && !force) {
2075
+ console.log("");
2062
2076
  console.log(
2063
- `Wallet already exists at ${walletPath}. Skipping wallet generation.`
2077
+ `Wallet already exists at ${walletPath} \u2014 keeping your existing keys.`
2064
2078
  );
2079
+ console.log(
2080
+ "Your seed phrase from the first run is still valid; nothing changed."
2081
+ );
2082
+ console.log(
2083
+ "(Re-run with --force to regenerate, which REPLACES your keys.)"
2084
+ );
2085
+ printInitNextStep(dir);
2065
2086
  return;
2066
2087
  }
2067
2088
  const walletPassword = password ?? process.env["TOWNHOUSE_WALLET_PASSWORD"];
@@ -2095,6 +2116,7 @@ async function handleInit(force, configDir, password, preset, yes) {
2095
2116
  console.log(` ${"".padEnd(6)} EVM: ${info.evmAddress}`);
2096
2117
  }
2097
2118
  walletManager.lock();
2119
+ printInitNextStep(dir);
2098
2120
  }
2099
2121
  async function handleSetup(configDir, port, noBrowser, dockerInstance, browserOpener) {
2100
2122
  const dir = resolve(configDir ?? DEFAULT_CONFIG_DIR);
@@ -3007,8 +3029,52 @@ function isAnonBootstrapTimeout(err) {
3007
3029
  ${err.stderr ?? ""}`;
3008
3030
  return /connector.*unhealthy|dependency.*connector.*fail/i.test(text);
3009
3031
  }
3032
+ async function attachDashboard(hostname) {
3033
+ if (!shouldRenderInk()) return;
3034
+ try {
3035
+ const { mountTui } = await import("./tui-QE3ZRZO3.js");
3036
+ const apiUrlOverride = process.env["HS_TOWNHOUSE_API_URL"];
3037
+ const mountOpts = apiUrlOverride !== void 0 ? { apiUrl: apiUrlOverride } : {};
3038
+ const instance = mountTui(mountOpts);
3039
+ await instance.waitUntilExit();
3040
+ } catch (tuiErr) {
3041
+ const detail = tuiErr instanceof Error ? tuiErr.message : String(tuiErr);
3042
+ console.error("");
3043
+ console.error(`Your node is live at ${hostname}.`);
3044
+ console.error(
3045
+ `The live dashboard could not open (${detail}) \u2014 this is a display issue, not a node issue. Your node keeps running.`
3046
+ );
3047
+ console.error(
3048
+ "Stop it anytime with: npx @toon-protocol/townhouse hs down"
3049
+ );
3050
+ }
3051
+ }
3010
3052
  async function handleHsUp(_configPath, configDir, config, docker, options) {
3011
3053
  const { password, force, skipPreflight, hsOverrides } = options;
3054
+ if (!force) {
3055
+ const adminClientFactory = hsOverrides?.createAdminClient ?? ((url, t) => new ConnectorAdminClient(url, t));
3056
+ const probe = adminClientFactory(HS_CONNECTOR_ADMIN_URL, 3e3);
3057
+ try {
3058
+ const existing = await probe.getHsHostname();
3059
+ if (existing.hostname !== null) {
3060
+ console.log(`Apex live at ${existing.hostname}`);
3061
+ _writeHostJson(configDir, {
3062
+ hostname: existing.hostname,
3063
+ publishedAt: existing.publishedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
3064
+ writtenAt: (/* @__PURE__ */ new Date()).toISOString()
3065
+ });
3066
+ await attachDashboard(existing.hostname);
3067
+ return;
3068
+ }
3069
+ } catch (probeErr) {
3070
+ const msg = probeErr instanceof Error ? probeErr.message : String(probeErr);
3071
+ if (msg.includes("anon-disabled")) {
3072
+ const { exitCode } = renderFailure(probeErr);
3073
+ process.exitCode = exitCode;
3074
+ return;
3075
+ }
3076
+ }
3077
+ }
3012
3078
  if (!skipPreflight) {
3013
3079
  const preflight = hsOverrides?.checkPortCollisions ?? ((d) => checkHsPortCollisions(d));
3014
3080
  try {
@@ -3042,7 +3108,7 @@ async function handleHsUp(_configPath, configDir, config, docker, options) {
3042
3108
  resolvedPassword = await promptPassword("Wallet password: ");
3043
3109
  } else {
3044
3110
  console.error(
3045
- "Wallet password required. Use --password flag or TOWNHOUSE_WALLET_PASSWORD env var."
3111
+ "Wallet password required, but no interactive terminal is available to prompt.\nPass --password <pw> or set TOWNHOUSE_WALLET_PASSWORD."
3046
3112
  );
3047
3113
  process.exitCode = 1;
3048
3114
  return;
@@ -3067,29 +3133,6 @@ async function handleHsUp(_configPath, configDir, config, docker, options) {
3067
3133
  }
3068
3134
  const ribbon = new OnboardingRibbon();
3069
3135
  try {
3070
- if (!force) {
3071
- const adminClientFactory = hsOverrides?.createAdminClient ?? ((url, t) => new ConnectorAdminClient(url, t));
3072
- const probe = adminClientFactory(HS_CONNECTOR_ADMIN_URL, 3e3);
3073
- try {
3074
- const existing = await probe.getHsHostname();
3075
- if (existing.hostname !== null) {
3076
- console.log(`Apex live at ${existing.hostname}`);
3077
- _writeHostJson(configDir, {
3078
- hostname: existing.hostname,
3079
- publishedAt: existing.publishedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
3080
- writtenAt: (/* @__PURE__ */ new Date()).toISOString()
3081
- });
3082
- return;
3083
- }
3084
- } catch (probeErr) {
3085
- const msg = probeErr instanceof Error ? probeErr.message : String(probeErr);
3086
- if (msg.includes("anon-disabled")) {
3087
- const { exitCode } = renderFailure(probeErr);
3088
- process.exitCode = exitCode;
3089
- return;
3090
- }
3091
- }
3092
- }
3093
3136
  writeHsConnectorConfig(configDir, config, { force });
3094
3137
  const materialize = hsOverrides?.materializeComposeTemplate ?? materializeComposeTemplate;
3095
3138
  const { composePath } = materialize("hs", { townhouseHome: configDir });
@@ -3119,6 +3162,7 @@ async function handleHsUp(_configPath, configDir, config, docker, options) {
3119
3162
  ribbon.start("bootstrap");
3120
3163
  }
3121
3164
  });
3165
+ ribbon.stop();
3122
3166
  if (typeof orch.pullImage === "function") {
3123
3167
  try {
3124
3168
  const apexImages = await collectApexImageRefs(configDir);
@@ -3132,11 +3176,18 @@ async function handleHsUp(_configPath, configDir, config, docker, options) {
3132
3176
  console.log(` [${pulled}/${apexImages.length}] ${ref}`);
3133
3177
  await orch.pullImage(ref);
3134
3178
  }
3179
+ } else {
3180
+ console.log(
3181
+ "No pinned image manifest found \u2014 Docker will pull images on demand."
3182
+ );
3183
+ console.log(
3184
+ "First start can take several minutes with limited progress output."
3185
+ );
3135
3186
  }
3136
3187
  } catch (pullErr) {
3137
3188
  const detail = pullErr instanceof Error ? pullErr.message : String(pullErr);
3138
- console.error(
3139
- `[townhouse hs up] pre-pull skipped (non-fatal, compose will retry): ${detail}`
3189
+ console.log(
3190
+ `Could not pre-pull images (${detail}). Docker will pull them during startup \u2014 this is normal and may take a few minutes.`
3140
3191
  );
3141
3192
  }
3142
3193
  }
@@ -3157,6 +3208,10 @@ async function handleHsUp(_configPath, configDir, config, docker, options) {
3157
3208
  resolve(config.wallet.encrypted_path)
3158
3209
  );
3159
3210
  process.env["TOWNHOUSE_DOCKER_GID"] = String(dockerSockGid);
3211
+ if (!bootstrapStarted) {
3212
+ bootstrapStarted = true;
3213
+ ribbon.start("bootstrap");
3214
+ }
3160
3215
  const MAX_ANON_RETRIES = 3;
3161
3216
  try {
3162
3217
  for (let attempt = 1; attempt <= MAX_ANON_RETRIES; attempt++) {
@@ -3230,13 +3285,7 @@ async function handleHsUp(_configPath, configDir, config, docker, options) {
3230
3285
  writtenAt: (/* @__PURE__ */ new Date()).toISOString()
3231
3286
  });
3232
3287
  ribbon.start("live", hostname);
3233
- if (shouldRenderInk()) {
3234
- const { mountTui } = await import("./tui-OIFXGBTL.js");
3235
- const apiUrlOverride = process.env["HS_TOWNHOUSE_API_URL"];
3236
- const mountOpts = apiUrlOverride !== void 0 ? { apiUrl: apiUrlOverride } : {};
3237
- const instance = mountTui(mountOpts);
3238
- await instance.waitUntilExit();
3239
- }
3288
+ await attachDashboard(hostname);
3240
3289
  } catch (err) {
3241
3290
  const { exitCode } = renderFailure(err);
3242
3291
  process.exitCode = exitCode;
@@ -3672,7 +3721,14 @@ async function main(argv, dockerInstance, browserOpener, hsOverrides, nodeComman
3672
3721
  }
3673
3722
  }
3674
3723
  var invokedFile = process.argv[1];
3675
- var invokedDirectly = typeof invokedFile === "string" && import.meta.url === pathToFileURL(invokedFile).href;
3724
+ var invokedDirectly = false;
3725
+ if (typeof invokedFile === "string") {
3726
+ try {
3727
+ invokedDirectly = import.meta.url === pathToFileURL(realpathSync(invokedFile)).href;
3728
+ } catch {
3729
+ invokedDirectly = import.meta.url === pathToFileURL(invokedFile).href;
3730
+ }
3731
+ }
3676
3732
  if (invokedDirectly) {
3677
3733
  main(process.argv.slice(2)).catch((error) => {
3678
3734
  if (error instanceof CliHelpRequested) {