itwillsync 1.3.1 → 1.3.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/index.js CHANGED
@@ -4172,7 +4172,7 @@ async function runSetupWizard() {
4172
4172
 
4173
4173
  // src/hub-client.ts
4174
4174
  import { spawn as spawn2 } from "child_process";
4175
- import { readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
4175
+ import { readFileSync as readFileSync2, existsSync as existsSync2, unlinkSync } from "fs";
4176
4176
  import { homedir as homedir2 } from "os";
4177
4177
  import { join as join4, dirname as dirname2 } from "path";
4178
4178
  import { fileURLToPath as fileURLToPath2 } from "url";
@@ -4219,13 +4219,70 @@ async function discoverHub() {
4219
4219
  req.end();
4220
4220
  });
4221
4221
  }
4222
+ async function getHubPidFromHealth() {
4223
+ return new Promise((resolve) => {
4224
+ const req = request(
4225
+ {
4226
+ hostname: "127.0.0.1",
4227
+ port: HUB_INTERNAL_PORT,
4228
+ path: "/api/health",
4229
+ method: "GET",
4230
+ timeout: 2e3
4231
+ },
4232
+ (res) => {
4233
+ let data = "";
4234
+ res.on("data", (chunk) => {
4235
+ data += chunk;
4236
+ });
4237
+ res.on("end", () => {
4238
+ try {
4239
+ const json = JSON.parse(data);
4240
+ resolve(json.pid && typeof json.pid === "number" ? json.pid : null);
4241
+ } catch {
4242
+ resolve(null);
4243
+ }
4244
+ });
4245
+ }
4246
+ );
4247
+ req.on("error", () => resolve(null));
4248
+ req.on("timeout", () => {
4249
+ req.destroy();
4250
+ resolve(null);
4251
+ });
4252
+ req.end();
4253
+ });
4254
+ }
4255
+ async function killStaleHub() {
4256
+ const pid = await getHubPidFromHealth();
4257
+ if (!pid) return false;
4258
+ try {
4259
+ process.kill(pid, "SIGTERM");
4260
+ } catch {
4261
+ return false;
4262
+ }
4263
+ for (let i = 0; i < 20; i++) {
4264
+ await new Promise((r) => setTimeout(r, 200));
4265
+ const stillRunning = await discoverHub();
4266
+ if (!stillRunning) break;
4267
+ }
4268
+ const hubDir = getHubDir();
4269
+ try {
4270
+ unlinkSync(join4(hubDir, "hub.json"));
4271
+ } catch {
4272
+ }
4273
+ try {
4274
+ unlinkSync(join4(hubDir, "hub.pid"));
4275
+ } catch {
4276
+ }
4277
+ return true;
4278
+ }
4222
4279
  async function spawnHub() {
4223
4280
  const __dirname = dirname2(fileURLToPath2(import.meta.url));
4224
4281
  const hubPath = join4(__dirname, "hub", "daemon.js");
4225
4282
  return new Promise((resolve, reject) => {
4226
4283
  const child = spawn2("node", [hubPath], {
4227
4284
  detached: true,
4228
- stdio: ["ignore", "pipe", "ignore"],
4285
+ stdio: ["ignore", "pipe", "pipe"],
4229
4286
  env: {
4230
4287
  ...process.env,
4231
4288
  // Pass config dir to hub
@@ -4234,6 +4291,7 @@ async function spawnHub() {
4234
4291
  });
4235
4292
  child.unref();
4236
4293
  let output = "";
4294
+ let stderrOutput = "";
4237
4295
  const timeout = setTimeout(() => {
4238
4296
  reject(new Error("Hub daemon startup timed out"));
4239
4297
  }, 1e4);
@@ -4242,9 +4300,13 @@ async function spawnHub() {
4242
4300
  if (output.includes("hub:ready:")) {
4243
4301
  clearTimeout(timeout);
4244
4302
  child.stdout?.destroy();
4303
+ child.stderr?.destroy();
4245
4304
  resolve();
4246
4305
  }
4247
4306
  });
4307
+ child.stderr?.on("data", (data) => {
4308
+ stderrOutput += data.toString();
4309
+ });
4248
4310
  child.on("error", (err) => {
4249
4311
  clearTimeout(timeout);
4250
4312
  reject(new Error(`Failed to spawn hub daemon: ${err.message}`));
@@ -4252,7 +4314,11 @@ async function spawnHub() {
4252
4314
  child.on("exit", (code) => {
4253
4315
  clearTimeout(timeout);
4254
4316
  if (code !== null && code !== 0) {
4255
- reject(new Error(`Hub daemon exited with code ${code}`));
4317
+ const detail = stderrOutput.trim();
4318
+ reject(new Error(
4319
+ `Hub daemon exited with code ${code}${detail ? `:
4320
+ ${detail}` : ""}`
4321
+ ));
4256
4322
  }
4257
4323
  });
4258
4324
  });
@@ -4364,11 +4430,19 @@ async function listSessions() {
4364
4430
  req.end();
4365
4431
  });
4366
4432
  }
4367
- function stopHub() {
4433
+ async function stopHub() {
4368
4434
  const config = getHubConfig();
4369
- if (!config) return false;
4435
+ if (config) {
4436
+ try {
4437
+ process.kill(config.pid, "SIGTERM");
4438
+ return true;
4439
+ } catch {
4440
+ }
4441
+ }
4442
+ const pid = await getHubPidFromHealth();
4443
+ if (!pid) return false;
4370
4444
  try {
4371
- process.kill(config.pid, "SIGTERM");
4445
+ process.kill(pid, "SIGTERM");
4372
4446
  return true;
4373
4447
  } catch {
4374
4448
  return false;
@@ -4537,11 +4611,15 @@ function preventSleep() {
4537
4611
  async function ensureHub() {
4538
4612
  const hubRunning = await discoverHub();
4539
4613
  if (hubRunning) {
4540
- return false;
4614
+ const config = getHubConfig();
4615
+ if (config) {
4616
+ return false;
4617
+ }
4618
+ console.warn(" Detected stale hub daemon (no config). Restarting...");
4619
+ await killStaleHub();
4541
4620
  }
4542
4621
  try {
4543
4622
  await spawnHub();
4544
- await new Promise((resolve) => setTimeout(resolve, 500));
4545
4623
  return true;
4546
4624
  } catch (err) {
4547
4625
  console.warn(`
@@ -4554,11 +4632,11 @@ async function handleHubCommand(options) {
4554
4632
  const hubConfig = getHubConfig();
4555
4633
  const hubRunning = await discoverHub();
4556
4634
  if (options.hubStop) {
4557
- if (!hubRunning || !hubConfig) {
4635
+ if (!hubRunning) {
4558
4636
  console.log("\n No hub daemon is running.\n");
4559
4637
  return;
4560
4638
  }
4561
- const stopped = stopHub();
4639
+ const stopped = await stopHub();
4562
4640
  if (stopped) {
4563
4641
  console.log("\n Hub daemon stopped.\n");
4564
4642
  } else {
@@ -4585,11 +4663,16 @@ async function handleHubCommand(options) {
4585
4663
  return;
4586
4664
  }
4587
4665
  if (options.hubInfo) {
4588
- if (!hubRunning || !hubConfig) {
4666
+ if (!hubRunning) {
4589
4667
  console.log("\n No hub daemon is running.");
4590
4668
  console.log(" Start a session with: itwillsync -- <agent>\n");
4591
4669
  return;
4592
4670
  }
4671
+ if (!hubConfig) {
4672
+ console.log("\n Hub daemon is running but its config is missing (stale state).");
4673
+ console.log(" Run: itwillsync hub stop\n");
4674
+ return;
4675
+ }
4593
4676
  let networkingMode = "local";
4594
4677
  if (options.tailscale) {
4595
4678
  networkingMode = "tailscale";
@@ -4685,12 +4768,12 @@ async function main() {
4685
4768
  }
4686
4769
  }
4687
4770
  const dashboardUrl = hubConfig ? `http://${ip}:${HUB_EXTERNAL_PORT}?token=${hubConfig.masterToken}` : null;
4688
- if (isFirstSession && dashboardUrl && !options.noQr) {
4771
+ if (dashboardUrl && !options.noQr) {
4772
+ if (!isFirstSession) {
4773
+ console.log(`
4774
+ Session "${cmd}" registered with hub.`);
4775
+ }
4689
4776
  displayQR(dashboardUrl);
4690
- console.log(` Dashboard: ${dashboardUrl}`);
4691
- } else if (isFirstSession && !options.noQr) {
4692
- const directUrl = `http://${ip}:${port}?token=${token}`;
4693
- displayQR(directUrl);
4694
4777
  } else if (dashboardUrl) {
4695
4778
  console.log(`
4696
4779
  Session "${cmd}" registered with hub.`);
@@ -4726,6 +4809,10 @@ async function main() {
4726
4809
  process.stdout.on("resize", handleResize);
4727
4810
  handleResize();
4728
4811
  async function cleanup() {
4812
+ process.stdout.write(
4813
+ "\x1B[>0u\x1B[?2004l\x1B[?1000l\x1B[?1002l\x1B[?1003l\x1B[?1006l\x1B[?25h\x1B[?1049l"
4814
+ // Exit alternate screen buffer (if active)
4815
+ );
4729
4816
  if (process.stdin.isTTY) {
4730
4817
  process.stdin.setRawMode(false);
4731
4818
  }