peerband 0.1.0 → 0.1.1

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/README.md CHANGED
@@ -17,6 +17,18 @@ Your MCP client can list agents, read their status and bounded recent output, an
17
17
 
18
18
  ## Install
19
19
 
20
+ Recommended (Node.js 22.13+, Herdr 0.9.3+):
21
+
22
+ ```sh
23
+ npm install -g peerband && peerband login
24
+ ```
25
+
26
+ `peerband login` opens your browser so you can approve this machine, then checks for Herdr and offers to add the PeerBand plugin to it (`Add the PeerBand plugin to Herdr now? [Y/n]`). After that, `peerband relay` opens the PeerBand Relay tab. If Herdr is not installed yet, login tells you how to finish later (install Herdr from https://herdr.dev, then `peerband herdr install` and `peerband relay`). Use `peerband login --herdr-plugin install` to add the plugin without being asked, or `--herdr-plugin skip` to leave Herdr alone.
27
+
28
+ ### One-step install script (alternative)
29
+
30
+ The script checks Node, runs `npm install -g peerband`, then runs `peerband login --herdr-plugin install`.
31
+
20
32
  macOS and Linux:
21
33
 
22
34
  ```sh
@@ -29,25 +41,15 @@ Windows PowerShell:
29
41
  irm https://peerband.com/install.ps1 | iex
30
42
  ```
31
43
 
32
- Or with npm directly:
33
-
34
- ```sh
35
- npm install -g peerband
36
- ```
37
-
38
- The installers check Node, install the CLI, run `peerband login` (which opens your browser), and set up the Herdr plugin when Herdr is present.
39
-
40
- Headless machine or CI with no browser? Pass a one-time grant from the PeerBand dashboard instead: `curl -fsSL https://peerband.com/install.sh | sh -s -- --grant GRANT --name "my-server"`, or on Windows `$env:PEERBAND_GRANT = 'GRANT'; irm https://peerband.com/install.ps1 | iex`.
44
+ ### Headless machines and CI
41
45
 
42
- > PeerBand is not published to npm yet. To try a build, run `npm pack` in this repository and install with `PEERBAND_PACKAGE=/path/to/peerband-0.1.0.tgz` (a tarball path or git URL) set when running the installer.
46
+ No browser available? Pass a one-time grant from the PeerBand dashboard instead: `peerband login --grant GRANT` (or set `PEERBAND_GRANT`). With the scripts: `curl -fsSL https://peerband.com/install.sh | sh -s -- --grant GRANT --name "my-server"`, or on Windows `$env:PEERBAND_GRANT = 'GRANT'; irm https://peerband.com/install.ps1 | iex`.
43
47
 
44
48
  ## Quickstart
45
49
 
46
- 1. Install the CLI (above).
47
- 2. `peerband login` opens your browser. Sign in to PeerBand and confirm the code shown in your terminal.
48
- 3. `peerband herdr install` to add the plugin to Herdr.
49
- 4. `peerband relay` to open the PeerBand Relay tab, then press Space to connect.
50
- 5. Add the MCP server URL that `peerband relay` and `peerband status` print (`https://mcp.peerband.com/mcp`) to your MCP client: a custom connector in ChatGPT or Claude, or `claude mcp add --transport http peerband https://mcp.peerband.com/mcp` in Claude Code. Sign in and approve access when the client asks.
50
+ 1. `npm install -g peerband && peerband login`. Confirm the code shown in your terminal when your browser opens, and answer Y to adding the Herdr plugin.
51
+ 2. `peerband relay` to open the PeerBand Relay tab, then press Space to connect. (If you skipped the plugin: `peerband herdr install` first.)
52
+ 3. Add the MCP server URL that `peerband relay` and `peerband status` print (`https://mcp.peerband.com/mcp`) to your MCP client: a custom connector in ChatGPT or Claude, or `claude mcp add --transport http peerband https://mcp.peerband.com/mcp` in Claude Code. Sign in and approve access when the client asks.
51
53
 
52
54
  ## Logging in
53
55
 
@@ -82,7 +84,7 @@ The plugin adds a persistent tab titled `PeerBand Relay [On]` or `[Off]` that sh
82
84
 
83
85
  | Command | What it does |
84
86
  | --- | --- |
85
- | `peerband login` | Sign in through your browser. Flags: `--no-browser`, `--name NAME`, `--server URL`; headless/CI: `--grant GRANT` |
87
+ | `peerband login` | Sign in through your browser, then offer to add the Herdr plugin. Flags: `--no-browser`, `--name NAME`, `--server URL`, `--herdr-plugin ask\|install\|skip`; headless/CI: `--grant GRANT` |
86
88
  | `peerband logout` | Stop a running relay and delete the local credential. Does not revoke it on the server |
87
89
  | `peerband status` | Show login, relay and Herdr state. `--json` for machine-readable output |
88
90
  | `peerband connect` | Run the connector in the foreground. Flags: `--runtime herdr\|mock`, `--enable-writes`, `--config PATH`, `--server URL` |
package/dist/peerband.mjs CHANGED
@@ -14338,6 +14338,84 @@ function openBrowser(url, platform = process.platform) {
14338
14338
  });
14339
14339
  }
14340
14340
 
14341
+ // src/cli/prompt.ts
14342
+ var PromptAborted = class extends Error {
14343
+ constructor() {
14344
+ super("Prompt cancelled.");
14345
+ this.name = "PromptAborted";
14346
+ }
14347
+ };
14348
+ function askYesNo(stdin, stderr, prompt, defaultYes = true) {
14349
+ return new Promise((resolve3, reject) => {
14350
+ let value = "";
14351
+ const cleanup = () => {
14352
+ stdin.off("data", onData);
14353
+ stdin.pause();
14354
+ };
14355
+ const onData = (chunk) => {
14356
+ const text2 = typeof chunk === "string" ? chunk : chunk.toString("utf8");
14357
+ if (text2.includes("") || text2.includes("")) {
14358
+ cleanup();
14359
+ return reject(new PromptAborted());
14360
+ }
14361
+ const end = text2.search(/[\r\n]/);
14362
+ value += end === -1 ? text2 : text2.slice(0, end);
14363
+ if (end === -1) return;
14364
+ cleanup();
14365
+ const answer = value.trim().toLowerCase();
14366
+ resolve3(answer === "" ? defaultYes : answer === "y" || answer === "yes");
14367
+ };
14368
+ stderr.write(prompt);
14369
+ stdin.resume();
14370
+ stdin.on("data", onData);
14371
+ });
14372
+ }
14373
+
14374
+ // src/cli/commands/herdr-setup.ts
14375
+ var HERDR_PLUGIN_MODES = ["ask", "install", "skip"];
14376
+ async function herdrSetup(ctx, mode) {
14377
+ const s = ctx.out.stdout;
14378
+ const manual = `${s.dim("Next:")} peerband herdr install ${s.dim("then")} peerband relay`;
14379
+ const state = await pluginInstalled(ctx);
14380
+ if (!state.found) {
14381
+ ctx.out.print("");
14382
+ ctx.out.print(`${s.yellow(symbols.warn)} Herdr was not found. The PeerBand Relay tab needs Herdr 0.9.3 or newer.`);
14383
+ ctx.out.print(" 1. Install Herdr: https://herdr.dev");
14384
+ ctx.out.print(` 2. ${s.bold("peerband herdr install")} ${s.dim("then")} ${s.bold("peerband relay")}`);
14385
+ return;
14386
+ }
14387
+ if (state.installed) {
14388
+ ctx.out.print(`${s.dim("Next:")} peerband relay ${s.dim("(opens the PeerBand Relay tab in Herdr)")}`);
14389
+ return;
14390
+ }
14391
+ let install = mode === "install";
14392
+ if (mode === "ask" && ctx.stdin.isTTY && ctx.stderr.isTTY) {
14393
+ try {
14394
+ install = await askYesNo(ctx.stdin, ctx.stderr, "Add the PeerBand plugin to Herdr now? [Y/n] ");
14395
+ } catch (error) {
14396
+ if (!(error instanceof PromptAborted)) throw error;
14397
+ ctx.out.print(manual);
14398
+ return;
14399
+ }
14400
+ if (!install) {
14401
+ ctx.out.print(s.dim("Skipped. Add it later with `peerband herdr install`, then open the relay with `peerband relay`."));
14402
+ return;
14403
+ }
14404
+ }
14405
+ if (!install) {
14406
+ ctx.out.print(manual);
14407
+ return;
14408
+ }
14409
+ try {
14410
+ await installSpec.run({ values: {}, positionals: [] }, ctx);
14411
+ } catch (error) {
14412
+ const message = error.message;
14413
+ ctx.out.warn(`${s.yellow(symbols.warn)} Could not add the PeerBand plugin to Herdr: ${message}`);
14414
+ if (error instanceof CliError && error.hint) ctx.out.warn(` ${error.hint}`);
14415
+ ctx.out.print(s.dim("You are logged in. Add the plugin later with `peerband herdr install`, then run `peerband relay`."));
14416
+ }
14417
+ }
14418
+
14341
14419
  // src/cli/commands/login.ts
14342
14420
  async function persist(ctx, redeemed) {
14343
14421
  const credentials = { ...redeemed, enrolledAt: (/* @__PURE__ */ new Date()).toISOString() };
@@ -14456,22 +14534,27 @@ function mapFailure(error, serverUrl) {
14456
14534
  var spec3 = {
14457
14535
  name: "login",
14458
14536
  summary: "Connect this machine to your PeerBand account",
14459
- usage: "peerband login [--name NAME] [--no-browser] [--server URL]\n peerband login --grant GRANT [--name NAME] [--server URL]",
14460
- description: "Signs this machine in through your browser. login prints a link and a short code, opens the page (unless you are on SSH, a headless machine or pass --no-browser), and waits while you confirm the code in your PeerBand account. The credential is stored in your PeerBand config directory (readable only by you). Running login again replaces the existing enrollment.\n\nOn a headless machine or in CI, use a one-time grant instead: create it in the PeerBand dashboard and pass --grant GRANT (or set PEERBAND_GRANT).",
14537
+ usage: "peerband login [--name NAME] [--no-browser] [--server URL] [--herdr-plugin ask|install|skip]\n peerband login --grant GRANT [--name NAME] [--server URL]",
14538
+ description: "Signs this machine in through your browser. login prints a link and a short code, opens the page (unless you are on SSH, a headless machine or pass --no-browser), and waits while you confirm the code in your PeerBand account. The credential is stored in your PeerBand config directory (readable only by you). Running login again replaces the existing enrollment.\n\nOn a headless machine or in CI, use a one-time grant instead: create it in the PeerBand dashboard and pass --grant GRANT (or set PEERBAND_GRANT).\n\nWhen login succeeds it checks for Herdr. If the PeerBand plugin is missing it offers to add it (--herdr-plugin ask, the default; it only asks on a terminal), adds it without asking (install), or just prints the command (skip).",
14461
14539
  options: [
14462
14540
  { name: "no-browser", type: "boolean", description: "Do not open a browser; print the link and code only (SSH, containers)." },
14463
14541
  { name: "name", type: "string", valueName: "NAME", description: "Name shown for this machine in the dashboard. Default: this computer's hostname." },
14464
14542
  { name: "server", type: "string", valueName: "URL", description: "PeerBand server. Default: $PEERBAND_SERVER or https://peerband.com." },
14465
- { name: "grant", type: "string", valueName: "GRANT", description: "Headless/CI: enroll with a dashboard grant instead of the browser. Default: $PEERBAND_GRANT." }
14543
+ { name: "grant", type: "string", valueName: "GRANT", description: "Headless/CI: enroll with a dashboard grant instead of the browser. Default: $PEERBAND_GRANT." },
14544
+ { name: "herdr-plugin", type: "string", valueName: "ask|install|skip", description: "After login, add the PeerBand plugin to Herdr: ask (default; prompts on a terminal), install, or skip." }
14466
14545
  ],
14467
14546
  examples: [
14468
14547
  ["peerband login", "Open your browser, confirm the code, done"],
14548
+ ["peerband login --herdr-plugin install", "Also add the PeerBand plugin to Herdr without asking (used by the install scripts)"],
14469
14549
  ["peerband login --no-browser", "Print the link and code; open the link on any other device"],
14470
14550
  ["PEERBAND_GRANT=... peerband login --name work-laptop", "Headless/CI with a dashboard grant; keeps it out of the process list"],
14471
14551
  ["peerband login --server http://localhost:8000", "Against a local development server"]
14472
14552
  ],
14473
14553
  notes: ["The code you confirm in the browser must match the one shown here. The secret device code and any grant are never printed or stored. Plain HTTP is only accepted for localhost."],
14474
14554
  async run(parsed, ctx) {
14555
+ const rawMode = str(parsed, "herdr-plugin")?.trim().toLowerCase() ?? "ask";
14556
+ if (!HERDR_PLUGIN_MODES.includes(rawMode)) throw new UsageError(`Invalid --herdr-plugin value "${rawMode}".`, "Use --herdr-plugin ask, install or skip.");
14557
+ const herdrMode = rawMode;
14475
14558
  const serverUrl = resolveServerUrl(str(parsed, "server"), ctx.env);
14476
14559
  const name = str(parsed, "name")?.trim() || hostname();
14477
14560
  const grant = (str(parsed, "grant") ?? ctx.env.PEERBAND_GRANT)?.trim();
@@ -14483,7 +14566,7 @@ var spec3 = {
14483
14566
  if (previous) ctx.out.print(s.dim(`Replaced the previous login (${previous.machineName ?? previous.installationId} on ${previous.serverUrl}).`));
14484
14567
  const who = credentials.accountEmail ? ` (${credentials.accountEmail})` : "";
14485
14568
  ctx.out.print(`${s.green(symbols.ok)} Logged in as ${s.bold(credentials.machineName ?? name)}${who}`);
14486
- ctx.out.print(`${s.dim("Next:")} peerband herdr install ${s.dim("then")} peerband relay`);
14569
+ await herdrSetup(ctx, herdrMode);
14487
14570
  return 0;
14488
14571
  }
14489
14572
  };
@@ -14630,16 +14713,8 @@ function herdrGroupHelp(s) {
14630
14713
  `;
14631
14714
  }
14632
14715
 
14633
- // src/cli/prompt.ts
14634
- var PromptAborted = class extends Error {
14635
- constructor() {
14636
- super("Prompt cancelled.");
14637
- this.name = "PromptAborted";
14638
- }
14639
- };
14640
-
14641
14716
  // src/cli/cli.ts
14642
- var VERSION = true ? "0.1.0" : "0.0.0-dev";
14717
+ var VERSION = true ? "0.1.1" : "0.0.0-dev";
14643
14718
  function createContext(overrides = {}) {
14644
14719
  const env = overrides.env ?? process.env;
14645
14720
  const stdout = overrides.stdout ?? process.stdout;
package/herdr-plugin.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  id = "peerband"
2
2
  name = "PeerBand"
3
- version = "0.1.0"
3
+ version = "0.1.1"
4
4
  min_herdr_version = "0.9.3"
5
5
  description = "Reach your Herdr agents from any MCP client through your PeerBand account"
6
6
  platforms = ["linux", "macos", "windows"]
@@ -90,7 +90,7 @@ if (-not $pbOnPath) {
90
90
  Say ' ok peerband installed' Green
91
91
 
92
92
  Say '==> Logging in' Cyan
93
- $loginArgs = @('login')
93
+ $loginArgs = @('login', '--herdr-plugin', 'install')
94
94
  if ($pbName) { $loginArgs += @('--name', $pbName) }
95
95
  try {
96
96
  if ($pbGrant) { $env:PEERBAND_GRANT = $pbGrant }
@@ -102,20 +102,14 @@ try {
102
102
  }
103
103
  if ($loginCode -ne 0) { Fail "peerband login failed. Run 'peerband login' again, or on a headless machine use 'peerband login --grant GRANT' with a grant from your PeerBand dashboard." }
104
104
 
105
- Say '==> Installing the Herdr plugin' Cyan
105
+ # `peerband login --herdr-plugin install` has already added the plugin to Herdr when Herdr is present.
106
106
  $pbHasHerdr = [bool](Get-Command herdr -ErrorAction SilentlyContinue)
107
- if ($pbHasHerdr) {
108
- & $pbExe herdr install
109
- if ($LASTEXITCODE -ne 0) { Fail 'peerband herdr install failed. Fix the error above and run it again.' }
110
- } else {
111
- Say ' !! Herdr was not found. PeerBand reaches the agents you run in Herdr.' Yellow
112
- }
113
107
 
114
108
  Say ''
115
109
  if ($pbHasHerdr) {
116
110
  Say 'PeerBand is ready. Next:' Green
117
111
  Say ' peerband relay open the PeerBand Relay tab in Herdr'
118
- Say ' peerband status check your connection'
112
+ Say ' peerband status check your connection (and the Herdr plugin)'
119
113
  } else {
120
114
  Say 'PeerBand is installed and logged in. Next:' Green
121
115
  Say ' 1. Install Herdr 0.9.3 or newer: https://herdr.dev'
@@ -79,14 +79,14 @@ ok "peerband installed"
79
79
  step "Logging in"
80
80
  if [ -n "$grant" ]; then
81
81
  if [ -n "$name" ]; then
82
- PEERBAND_GRANT=$grant "$pb" login --name "$name" || die "peerband login failed. Get a fresh grant from your PeerBand dashboard."
82
+ PEERBAND_GRANT=$grant "$pb" login --herdr-plugin install --name "$name" || die "peerband login failed. Get a fresh grant from your PeerBand dashboard."
83
83
  else
84
- PEERBAND_GRANT=$grant "$pb" login || die "peerband login failed. Get a fresh grant from your PeerBand dashboard."
84
+ PEERBAND_GRANT=$grant "$pb" login --herdr-plugin install || die "peerband login failed. Get a fresh grant from your PeerBand dashboard."
85
85
  fi
86
86
  else
87
87
  # Browser sign-in. Under `curl | sh` stdin is the script itself; use the terminal instead.
88
- set --
89
- [ -z "$name" ] || set -- --name "$name"
88
+ set -- --herdr-plugin install
89
+ [ -z "$name" ] || set -- "$@" --name "$name"
90
90
  if (exec </dev/tty) 2>/dev/null; then
91
91
  "$pb" login "$@" </dev/tty
92
92
  else
@@ -95,10 +95,9 @@ else
95
95
  fi
96
96
  grant=''
97
97
 
98
- step "Installing the Herdr plugin"
98
+ # `peerband login --herdr-plugin install` has already added the plugin to Herdr when Herdr is present.
99
99
  if command -v herdr >/dev/null 2>&1; then
100
- "$pb" herdr install || die "peerband herdr install failed. Fix the error above and run it again."
101
- printf '\n%sPeerBand is ready.%s Next:\n peerband relay open the PeerBand Relay tab in Herdr\n peerband status check your connection\n' "$G" "$N"
100
+ printf '\n%sPeerBand is ready.%s Next:\n peerband relay open the PeerBand Relay tab in Herdr\n peerband status check your connection (and the Herdr plugin)\n' "$G" "$N"
102
101
  else
103
102
  warn "Herdr was not found. PeerBand reaches the agents you run in Herdr."
104
103
  printf '\n%sPeerBand is installed and logged in.%s Next:\n 1. Install Herdr 0.9.3 or newer: https://herdr.dev\n 2. peerband herdr install then peerband relay\n peerband status check your connection\n' "$G" "$N"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "peerband",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "PeerBand CLI and Herdr plugin: reach your local coding agents from any MCP client through your PeerBand account",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",