use-pounce 0.1.0 → 0.2.0

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/bin/pounce.mjs CHANGED
@@ -204,7 +204,7 @@ async function ensureTunnelBinary(opts) {
204
204
  }
205
205
  if (!assetUrl) return { skipped: `no ${wanted} on any tunnel-v* release yet` };
206
206
  }
207
- process.stdout.write(dim(` downloading pounce-tunnel (${triple})…\n`));
207
+ process.stdout.write(dim(` downloading the remote-access component (${triple})…\n`));
208
208
  const res = await fetch(assetUrl, { signal: AbortSignal.timeout(120_000) });
209
209
  if (!res.ok) throw new Error(`download -> ${res.status}`);
210
210
  const buf = Buffer.from(await res.arrayBuffer());
@@ -300,12 +300,18 @@ async function waitForPhone(port) {
300
300
  }
301
301
 
302
302
  // --- commands -----------------------------------------------------------------
303
+ function printPairing(pairUrl, token, tunnel) {
304
+ const link = deepLink({ pairUrl, token, tunnel });
305
+ console.log(
306
+ `\n ${bold("Scan with the Pounce app")} ${dim("(Settings → Scan QR)")} ${bold("or your camera:")}\n`,
307
+ );
308
+ qrcode.generate(link, { small: true });
309
+ console.log(`\n ${dim("or open on the phone:")}\n ${dim(link)}\n`);
310
+ }
311
+
303
312
  async function cmdUp(opts, { wait }) {
304
313
  console.log(`\n${bold("🐾 Pounce")} ${dim(`v${PKG.version}`)} — pair your phone\n`);
305
314
 
306
- // Tunnel binary first: a bridge spawned after it exists picks it up itself.
307
- const tunnelState = await ensureTunnelBinary(opts);
308
-
309
315
  let reused = false;
310
316
  let pid = null;
311
317
  if (await bridgeAlive(opts.port)) {
@@ -328,28 +334,45 @@ async function cmdUp(opts, { wait }) {
328
334
  ` ${dim("bridge")} ${pairUrl} ${dim(reused ? "(already running)" : `(started · pid ${pid} · logs: pounce logs)`)}`,
329
335
  );
330
336
 
331
- let tunnel = null;
332
- if (tunnelState.skipped) {
337
+ // ONE code, shown as fast as possible. The remote identity is stable across
338
+ // restarts, so on any machine that has run before /ui already carries it and
339
+ // the QR is remote-capable instantly. Only a machine's very first run holds
340
+ // the QR briefly while remote access is minted — never a second code; if
341
+ // remote isn't ready in time, the LAN code still upgrades the phone to
342
+ // remote automatically after it pairs (the app captures /v1/pair on connect).
343
+ let tunnel = ui.tunnel?.nodeId ? ui.tunnel : null;
344
+ let remoteNote = null;
345
+ if (!tunnel && !opts.lan) {
346
+ console.log(` ${dim("Setting up remote access… (first run only — takes a few seconds)")}`);
347
+ const state = await ensureTunnelBinary(opts);
348
+ if (state.skipped) {
349
+ remoteNote = `${yellow("!")} Pairs over your Wi-Fi for now ${dim(`(remote access unavailable: ${state.skipped})`)}`;
350
+ } else {
351
+ tunnel = (await waitForTunnel(opts.port, token)).tunnel;
352
+ if (!tunnel) {
353
+ remoteNote = `${yellow("!")} Pairs over your Wi-Fi for now — remote access is still warming up and will switch on automatically after you pair`;
354
+ }
355
+ }
356
+ }
357
+ printPairing(pairUrl, token, tunnel);
358
+
359
+ if (opts.lan) {
333
360
  console.log(
334
- ` ${dim("tunnel")} ${yellow("off")} ${tunnelState.skipped}; QR pairs on this Wi-Fi only`,
361
+ ` ${dim("Wi-Fi-only mode (--lan): scan while on the same network as this machine")}`,
335
362
  );
336
- } else {
337
- const r = await waitForTunnel(opts.port, token);
338
- tunnel = r.tunnel;
363
+ } else if (tunnel) {
364
+ // Confirm the remote link quietly; the code on screen never changes (the
365
+ // identity is stable) — this only decides which status line to show.
366
+ const r = await waitForTunnel(opts.port, token, { timeoutMs: 12_000 });
339
367
  console.log(
340
- tunnel
341
- ? ` ${dim("tunnel")} ${green("ready")} — pairs from any network ${dim(`(iroh ${tunnel.nodeId.slice(0, 8)}…)`)}`
342
- : ` ${dim("tunnel")} ${yellow("off")} — ${r.why}; QR pairs on this Wi-Fi only`,
368
+ r.tunnel
369
+ ? ` ${green("")} Works from anywhere Wi-Fi at home, an encrypted tunnel everywhere else`
370
+ : ` ${yellow("!")} Pairs over your Wi-Fi for now the remote link is reconnecting in the background`,
343
371
  );
372
+ } else if (remoteNote) {
373
+ console.log(` ${remoteNote}`);
344
374
  }
345
375
 
346
- const link = deepLink({ pairUrl, token, tunnel });
347
- console.log(
348
- `\n ${bold("Scan with the Pounce app")} ${dim("(Settings → Scan QR)")} ${bold("or your camera:")}\n`,
349
- );
350
- qrcode.generate(link, { small: true });
351
- console.log(`\n ${dim("or open on the phone:")}\n ${dim(link)}\n`);
352
-
353
376
  if (!wait) return;
354
377
  console.log(
355
378
  ` Waiting for your phone… ${dim("(Ctrl-C is safe — the bridge keeps running; `pounce stop` stops it)")}`,
package/dist/launcher.mjs CHANGED
@@ -4725,7 +4725,7 @@ var server = http.createServer(async (req, res) => {
4725
4725
  return send(res, 200, {
4726
4726
  ...PAIR || {},
4727
4727
  deepLink: pairDeepLink(),
4728
- tunnel: tunnelInfo(),
4728
+ tunnel: tunnelEligible() ? tunnelInfo() : null,
4729
4729
  token: TOKEN,
4730
4730
  appVersion: APP_VERSION,
4731
4731
  daemonOk: !!(daemon && daemon.pid),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "use-pounce",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Pair your phone with this machine in one command: npx use-pounce starts the Pounce bridge in the background and prints a QR to scan. Works over SSH — pairing rides an iroh p2p tunnel, no port-forwarding.",
5
5
  "keywords": [
6
6
  "claude-code",