claudemesh-cli 1.0.0-alpha.13 → 1.0.0-alpha.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.
@@ -4331,30 +4331,84 @@ var exports_list = {};
4331
4331
  __export(exports_list, {
4332
4332
  runList: () => runList
4333
4333
  });
4334
- function runList() {
4334
+ async function runList() {
4335
4335
  const config = readConfig();
4336
- if (config.meshes.length === 0) {
4337
- console.log("No meshes joined yet.");
4338
- console.log("");
4339
- console.log("Join one with: claudemesh join https://claudemesh.com/join/<token>");
4340
- console.log(`Config file: ${getConfigPath()}`);
4336
+ const auth = getStoredToken();
4337
+ let serverMeshes = [];
4338
+ if (auth) {
4339
+ try {
4340
+ let userId = "";
4341
+ try {
4342
+ const payload = JSON.parse(Buffer.from(auth.session_token.split(".")[1], "base64url").toString());
4343
+ userId = payload.sub ?? "";
4344
+ } catch {}
4345
+ if (userId) {
4346
+ const res = await request({
4347
+ path: `/cli/meshes?user_id=${userId}`,
4348
+ baseUrl: BROKER_HTTP2
4349
+ });
4350
+ serverMeshes = res.meshes ?? [];
4351
+ }
4352
+ } catch {}
4353
+ }
4354
+ const localSlugs = new Set(config.meshes.map((m) => m.slug));
4355
+ const serverSlugs = new Set(serverMeshes.map((m) => m.slug));
4356
+ const allSlugs = new Set([...localSlugs, ...serverSlugs]);
4357
+ if (allSlugs.size === 0) {
4358
+ console.log(`
4359
+ No meshes yet.
4360
+ `);
4361
+ console.log(" Create one: claudemesh mesh create <name>");
4362
+ console.log(` Join one: claudemesh mesh add <invite-url>
4363
+ `);
4341
4364
  return;
4342
4365
  }
4343
- console.log(`Joined meshes (${config.meshes.length}):`);
4366
+ console.log(`
4367
+ Your meshes:
4368
+ `);
4369
+ for (const slug of allSlugs) {
4370
+ const local = config.meshes.find((m) => m.slug === slug);
4371
+ const server = serverMeshes.find((m) => m.slug === slug);
4372
+ const name = server?.name ?? local?.name ?? slug;
4373
+ const role = server?.role ?? "member";
4374
+ const isOwner = server?.is_owner ?? false;
4375
+ const roleLabel = isOwner ? "owner" : role;
4376
+ const memberCount = server?.member_count;
4377
+ const activePeers = server?.active_peers ?? 0;
4378
+ const inLocal = localSlugs.has(slug);
4379
+ const inServer = serverSlugs.has(slug);
4380
+ let status;
4381
+ let icon;
4382
+ if (inLocal && inServer) {
4383
+ icon = green("●");
4384
+ status = activePeers > 0 ? green(`${activePeers} online`) : dim("synced");
4385
+ } else if (inLocal && !inServer) {
4386
+ icon = yellow("●");
4387
+ status = yellow("local only");
4388
+ } else {
4389
+ icon = dim("○");
4390
+ status = dim("not added locally");
4391
+ }
4392
+ const memberInfo = memberCount ? dim(`${memberCount} member${memberCount !== 1 ? "s" : ""}`) : "";
4393
+ const parts = [roleLabel, memberInfo, status].filter(Boolean);
4394
+ console.log(` ${icon} ${bold(name)} ${dim(slug)}`);
4395
+ console.log(` ${parts.join(" · ")}`);
4396
+ }
4344
4397
  console.log("");
4345
- for (const m of config.meshes) {
4346
- console.log(` ${m.name} (${m.slug})`);
4347
- console.log(` mesh id: ${m.meshId}`);
4348
- console.log(` member id: ${m.memberId}`);
4349
- console.log(` pubkey: ${m.pubkey.slice(0, 16)}…`);
4350
- console.log(` broker: ${m.brokerUrl}`);
4351
- console.log(` joined: ${m.joinedAt}`);
4352
- console.log("");
4398
+ if (serverMeshes.some((m) => !localSlugs.has(m.slug))) {
4399
+ console.log(dim(" ○ = server only — run `claudemesh mesh add` to use locally"));
4353
4400
  }
4354
- console.log(`Config: ${getConfigPath()}`);
4401
+ console.log(dim(` Config: ${getConfigPath()}`));
4402
+ console.log("");
4355
4403
  }
4404
+ var BROKER_HTTP2;
4356
4405
  var init_list2 = __esm(() => {
4357
4406
  init_facade();
4407
+ init_facade6();
4408
+ init_facade3();
4409
+ init_urls();
4410
+ init_styles();
4411
+ BROKER_HTTP2 = URLS.BROKER.replace("wss://", "https://").replace("ws://", "http://").replace("/ws", "");
4358
4412
  });
4359
4413
 
4360
4414
  // src/commands/delete-mesh.ts
@@ -4368,19 +4422,38 @@ function prompt(question) {
4368
4422
  return new Promise((resolve) => {
4369
4423
  rl.question(question, (a) => {
4370
4424
  rl.close();
4371
- resolve(a.trim().toLowerCase());
4425
+ resolve(a.trim());
4372
4426
  });
4373
4427
  });
4374
4428
  }
4429
+ function getUserId(token) {
4430
+ try {
4431
+ const payload = JSON.parse(Buffer.from(token.split(".")[1], "base64url").toString());
4432
+ return payload.sub ?? "";
4433
+ } catch {
4434
+ return "";
4435
+ }
4436
+ }
4437
+ async function isOwner(slug, userId) {
4438
+ try {
4439
+ const res = await request({
4440
+ path: `/cli/meshes?user_id=${userId}`,
4441
+ baseUrl: BROKER_HTTP3
4442
+ });
4443
+ return res.meshes?.find((m) => m.slug === slug)?.is_owner ?? false;
4444
+ } catch {
4445
+ return false;
4446
+ }
4447
+ }
4375
4448
  async function deleteMesh(slug, opts = {}) {
4449
+ const config = readConfig();
4376
4450
  if (!slug) {
4377
- const config = readConfig();
4378
4451
  if (config.meshes.length === 0) {
4379
- console.error(" No meshes to delete.");
4452
+ console.error(" No meshes to remove.");
4380
4453
  return EXIT.NOT_FOUND;
4381
4454
  }
4382
4455
  console.log(`
4383
- Select mesh to delete:
4456
+ Select mesh to remove:
4384
4457
  `);
4385
4458
  config.meshes.forEach((m, i) => {
4386
4459
  console.log(` ${bold(String(i + 1) + ")")} ${m.slug} ${dim("(" + m.name + ")")}`);
@@ -4394,48 +4467,72 @@ async function deleteMesh(slug, opts = {}) {
4394
4467
  }
4395
4468
  slug = config.meshes[idx].slug;
4396
4469
  }
4470
+ const auth = getStoredToken();
4471
+ const userId = auth ? getUserId(auth.session_token) : "";
4472
+ const ownerCheck = userId ? await isOwner(slug, userId) : false;
4397
4473
  if (!opts.yes) {
4398
4474
  console.log(`
4399
- ${red("Warning:")} This will permanently delete ${bold(slug)}.`);
4400
- const answer = await prompt(` Type "${slug}" to confirm: `);
4401
- if (answer !== slug.toLowerCase()) {
4402
- console.log(" Cancelled.");
4403
- return EXIT.USER_CANCELLED;
4404
- }
4405
- }
4406
- const auth = getStoredToken();
4407
- if (auth) {
4408
- try {
4409
- let userId = "";
4410
- try {
4411
- const payload = JSON.parse(Buffer.from(auth.session_token.split(".")[1], "base64url").toString());
4412
- userId = payload.sub ?? "";
4413
- } catch {}
4414
- if (userId) {
4415
- await request({
4416
- path: `/cli/mesh/${slug}`,
4417
- method: "DELETE",
4418
- body: { user_id: userId },
4419
- baseUrl: BROKER_HTTP2
4420
- });
4421
- console.log(` ${green(icons.check)} Deleted "${slug}" from server.`);
4422
- }
4423
- } catch (err) {
4424
- const msg = err instanceof Error ? err.message : String(err);
4425
- if (msg.includes("403")) {
4426
- console.log(` ${dim("Not the owner — removing from local config only.")}`);
4427
- } else if (msg.includes("404")) {
4428
- console.log(` ${dim("Mesh not found on server removing locally.")}`);
4429
- } else {
4430
- console.log(` ${dim("Could not delete from server: " + msg)}`);
4475
+ ${bold(slug)}
4476
+ `);
4477
+ if (ownerCheck) {
4478
+ console.log(` ${bold("1)")} Remove from this device only ${dim("(keep on server)")}`);
4479
+ console.log(` ${bold("2)")} ${red("Delete everywhere")} ${dim("(removes for all members)")}`);
4480
+ console.log(` ${bold("3)")} Cancel`);
4481
+ console.log("");
4482
+ const choice = await prompt(" Choice [1]: ") || "1";
4483
+ if (choice === "3") {
4484
+ console.log(" Cancelled.");
4485
+ return EXIT.USER_CANCELLED;
4486
+ }
4487
+ if (choice === "2") {
4488
+ console.log(`
4489
+ ${red("Warning:")} This will delete ${bold(slug)} for all members.`);
4490
+ const confirm = await prompt(` Type "${slug}" to confirm: `);
4491
+ if (confirm.toLowerCase() !== slug.toLowerCase()) {
4492
+ console.log(" Cancelled.");
4493
+ return EXIT.USER_CANCELLED;
4494
+ }
4495
+ try {
4496
+ await request({
4497
+ path: `/cli/mesh/${slug}`,
4498
+ method: "DELETE",
4499
+ body: { user_id: userId },
4500
+ baseUrl: BROKER_HTTP3
4501
+ });
4502
+ console.log(` ${green(icons.check)} Deleted "${slug}" from server.`);
4503
+ } catch (err) {
4504
+ const msg = err instanceof Error ? err.message : String(err);
4505
+ console.error(` ${icons.cross} Server delete failed: ${msg}`);
4506
+ }
4507
+ leaveMesh(slug);
4508
+ console.log(` ${green(icons.check)} Removed from local config.`);
4509
+ return EXIT.SUCCESS;
4510
+ }
4511
+ } else {
4512
+ console.log(` ${bold("1)")} Remove from this device ${dim("(you can re-add later)")}`);
4513
+ console.log(` ${bold("2)")} Cancel`);
4514
+ if (!ownerCheck && userId) {
4515
+ console.log(dim(`
4516
+ ${yellow(icons.warn)} Only the mesh owner can delete it from the server.`));
4517
+ }
4518
+ console.log("");
4519
+ const choice = await prompt(" Choice [1]: ") || "1";
4520
+ if (choice === "2") {
4521
+ console.log(" Cancelled.");
4522
+ return EXIT.USER_CANCELLED;
4431
4523
  }
4432
4524
  }
4433
4525
  }
4434
- leaveMesh(slug);
4435
- console.log(` ${green(icons.check)} Removed "${slug}" from local config.`);
4526
+ const removed = leaveMesh(slug);
4527
+ if (removed) {
4528
+ console.log(` ${green(icons.check)} Removed "${slug}" from this device.`);
4529
+ console.log(dim(` Re-add anytime with: claudemesh mesh add <invite-url>`));
4530
+ } else {
4531
+ console.error(` Mesh "${slug}" not found in local config.`);
4532
+ }
4436
4533
  return EXIT.SUCCESS;
4437
4534
  }
4438
- var BROKER_HTTP2;
4535
+ var BROKER_HTTP3;
4439
4536
  var init_delete_mesh = __esm(() => {
4440
4537
  init_facade();
4441
4538
  init_facade9();
@@ -4444,7 +4541,7 @@ var init_delete_mesh = __esm(() => {
4444
4541
  init_urls();
4445
4542
  init_styles();
4446
4543
  init_exit_codes();
4447
- BROKER_HTTP2 = URLS.BROKER.replace("wss://", "https://").replace("ws://", "http://").replace("/ws", "");
4544
+ BROKER_HTTP3 = URLS.BROKER.replace("wss://", "https://").replace("ws://", "http://").replace("/ws", "");
4448
4545
  });
4449
4546
 
4450
4547
  // src/commands/rename.ts
@@ -8540,7 +8637,7 @@ async function runStatus() {
8540
8637
  const useColor = !process.env.NO_COLOR && process.env.TERM !== "dumb" && process.stdout.isTTY;
8541
8638
  const dim2 = (s) => useColor ? `\x1B[2m${s}\x1B[22m` : s;
8542
8639
  const green3 = (s) => useColor ? `\x1B[32m${s}\x1B[39m` : s;
8543
- const red2 = (s) => useColor ? `\x1B[31m${s}\x1B[39m` : s;
8640
+ const red3 = (s) => useColor ? `\x1B[31m${s}\x1B[39m` : s;
8544
8641
  console.log(`claudemesh status (v${VERSION})`);
8545
8642
  console.log("─".repeat(60));
8546
8643
  const configPath = getConfigPath();
@@ -8573,7 +8670,7 @@ async function runStatus() {
8573
8670
  if (probe.ok) {
8574
8671
  console.log(green3("reachable"));
8575
8672
  } else {
8576
- console.log(red2(`unreachable (${probe.error})`));
8673
+ console.log(red3(`unreachable (${probe.error})`));
8577
8674
  }
8578
8675
  }
8579
8676
  console.log("");
@@ -8587,7 +8684,7 @@ async function runStatus() {
8587
8684
  process.exit(0);
8588
8685
  } else {
8589
8686
  const broken = results.filter((r) => !r.reachable).length;
8590
- console.log(red2(`${broken} of ${results.length} mesh(es) unreachable.`));
8687
+ console.log(red3(`${broken} of ${results.length} mesh(es) unreachable.`));
8591
8688
  process.exit(1);
8592
8689
  }
8593
8690
  }
@@ -8751,7 +8848,7 @@ async function runDoctor() {
8751
8848
  const useColor = !process.env.NO_COLOR && process.env.TERM !== "dumb" && process.stdout.isTTY;
8752
8849
  const dim2 = (s) => useColor ? `\x1B[2m${s}\x1B[22m` : s;
8753
8850
  const green3 = (s) => useColor ? `\x1B[32m${s}\x1B[39m` : s;
8754
- const red2 = (s) => useColor ? `\x1B[31m${s}\x1B[39m` : s;
8851
+ const red3 = (s) => useColor ? `\x1B[31m${s}\x1B[39m` : s;
8755
8852
  console.log(`claudemesh doctor (v${VERSION})`);
8756
8853
  console.log("─".repeat(60));
8757
8854
  const checks = [
@@ -8763,7 +8860,7 @@ async function runDoctor() {
8763
8860
  checkKeypairs()
8764
8861
  ];
8765
8862
  for (const c of checks) {
8766
- const mark = c.pass ? green3("✓") : red2("✗");
8863
+ const mark = c.pass ? green3("✓") : red3("✗");
8767
8864
  const detail = c.detail ? dim2(` (${c.detail})`) : "";
8768
8865
  console.log(`${mark} ${c.name}${detail}`);
8769
8866
  if (!c.pass && c.fix) {
@@ -8776,7 +8873,7 @@ async function runDoctor() {
8776
8873
  console.log(green3("All checks passed."));
8777
8874
  process.exit(0);
8778
8875
  } else {
8779
- console.log(red2(`${failing.length} check(s) failed.`));
8876
+ console.log(red3(`${failing.length} check(s) failed.`));
8780
8877
  process.exit(1);
8781
8878
  }
8782
8879
  }
@@ -9616,7 +9713,7 @@ async function handleMeshSubcommand() {
9616
9713
  case "list":
9617
9714
  case "ls": {
9618
9715
  const { runList: runList2 } = await Promise.resolve().then(() => (init_list2(), exports_list));
9619
- runList2();
9716
+ await runList2();
9620
9717
  break;
9621
9718
  }
9622
9719
  case "delete":
@@ -9822,4 +9919,4 @@ main().catch((err) => {
9822
9919
  process.exit(EXIT.INTERNAL_ERROR);
9823
9920
  });
9824
9921
 
9825
- //# debugId=D73642E29B6C764B64756E2164756E21
9922
+ //# debugId=CB53B1FA4B95E06264756E2164756E21