@agentdeck/bridge 1.0.8 → 1.0.10

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.
Files changed (63) hide show
  1. package/dist/ask-gate.d.ts +113 -0
  2. package/dist/ask-gate.d.ts.map +1 -0
  3. package/dist/ask-gate.js +110 -0
  4. package/dist/ask-gate.js.map +1 -0
  5. package/dist/auth.d.ts +8 -0
  6. package/dist/auth.d.ts.map +1 -1
  7. package/dist/auth.js +15 -0
  8. package/dist/auth.js.map +1 -1
  9. package/dist/awaiting-overlay.d.ts +54 -6
  10. package/dist/awaiting-overlay.d.ts.map +1 -1
  11. package/dist/awaiting-overlay.js +109 -26
  12. package/dist/awaiting-overlay.js.map +1 -1
  13. package/dist/broadcast.d.ts +1 -1
  14. package/dist/broadcast.d.ts.map +1 -1
  15. package/dist/broadcast.js +10 -7
  16. package/dist/broadcast.js.map +1 -1
  17. package/dist/cli.d.ts.map +1 -1
  18. package/dist/cli.js +74 -4
  19. package/dist/cli.js.map +1 -1
  20. package/dist/daemon-server.d.ts.map +1 -1
  21. package/dist/daemon-server.js +239 -20
  22. package/dist/daemon-server.js.map +1 -1
  23. package/dist/daemon.js +1 -1
  24. package/dist/daemon.js.map +1 -1
  25. package/dist/http-auth-gate.d.ts +40 -0
  26. package/dist/http-auth-gate.d.ts.map +1 -0
  27. package/dist/http-auth-gate.js +46 -0
  28. package/dist/http-auth-gate.js.map +1 -0
  29. package/dist/index.d.ts +3 -0
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +15 -9
  32. package/dist/index.js.map +1 -1
  33. package/dist/mdns.d.ts +7 -1
  34. package/dist/mdns.d.ts.map +1 -1
  35. package/dist/mdns.js +7 -3
  36. package/dist/mdns.js.map +1 -1
  37. package/dist/modules/broadcast-module.d.ts.map +1 -1
  38. package/dist/modules/broadcast-module.js +3 -1
  39. package/dist/modules/broadcast-module.js.map +1 -1
  40. package/dist/modules/mdns-module.d.ts.map +1 -1
  41. package/dist/modules/mdns-module.js +3 -1
  42. package/dist/modules/mdns-module.js.map +1 -1
  43. package/dist/observed-inject.d.ts +21 -5
  44. package/dist/observed-inject.d.ts.map +1 -1
  45. package/dist/observed-inject.js +85 -22
  46. package/dist/observed-inject.js.map +1 -1
  47. package/dist/observed-steering.d.ts +38 -0
  48. package/dist/observed-steering.d.ts.map +1 -1
  49. package/dist/observed-steering.js +53 -0
  50. package/dist/observed-steering.js.map +1 -1
  51. package/dist/passive-observer.d.ts +9 -0
  52. package/dist/passive-observer.d.ts.map +1 -1
  53. package/dist/passive-observer.js +90 -5
  54. package/dist/passive-observer.js.map +1 -1
  55. package/dist/permission-resolver.d.ts +11 -0
  56. package/dist/permission-resolver.d.ts.map +1 -1
  57. package/dist/permission-resolver.js +23 -3
  58. package/dist/permission-resolver.js.map +1 -1
  59. package/dist/session-registry.d.ts +23 -2
  60. package/dist/session-registry.d.ts.map +1 -1
  61. package/dist/session-registry.js +56 -5
  62. package/dist/session-registry.js.map +1 -1
  63. package/package.json +3 -3
package/dist/broadcast.js CHANGED
@@ -14,10 +14,12 @@ import { getLanIp } from '@agentdeck/shared';
14
14
  * (and 255.255.255.255 as a fallback) on UDP port 9121 every 2 seconds.
15
15
  * Subnet broadcast is unicast-friendly IGMP-wise and is forwarded by every
16
16
  * WiFi AP we have seen, so the device's UDP listener picks it up even when
17
- * mDNS is blocked. The beacon reuses the same fields the device needs from
18
- * the mDNS TXT records (ip / port / project / agent / token) so the existing
19
- * selection logic in mdns_discovery.cpp and udp_discovery.cpp share the same
20
- * BridgeInfo contract.
17
+ * mDNS is blocked. The beacon contains discovery metadata only (ip / port /
18
+ * project / agent) plus `authRequired`, matching the unauthenticated /health
19
+ * semantics: discovery says "a daemon is here and pairing is required" and
20
+ * nothing more. It must never contain the pairing token: UDP broadcast is
21
+ * visible to every peer on the segment, just like multicast mDNS. A client
22
+ * must already hold a token obtained through explicit pairing/provisioning.
21
23
  */
22
24
  const UDP_DISCOVERY_PORT = 9121;
23
25
  const UDP_BROADCAST_INTERVAL_MS = 2_000;
@@ -34,7 +36,7 @@ function computeSubnetBroadcast(ip) {
34
36
  }
35
37
  return `${parts[0]}.${parts[1]}.${parts[2]}.255`;
36
38
  }
37
- export function advertiseUdpBroadcast(port, projectName, agentType, token) {
39
+ export function advertiseUdpBroadcast(port, projectName, agentType) {
38
40
  let stopped = false;
39
41
  let socket = null;
40
42
  let timer = null;
@@ -74,9 +76,10 @@ export function advertiseUdpBroadcast(port, projectName, agentType, token) {
74
76
  port,
75
77
  project: projectName,
76
78
  agent: agentType,
79
+ // Same signal the unauthenticated /health carries: tells a client to
80
+ // open its pairing flow instead of expecting a self-serve credential.
81
+ authRequired: true,
77
82
  };
78
- if (token)
79
- payload.token = token;
80
83
  const buf = Buffer.from(JSON.stringify(payload));
81
84
  // Limited broadcast — accepted by every AP, never routed across subnets.
82
85
  // This is the primary path when IGMP Snooping drops multicast.
@@ -1 +1 @@
1
- {"version":3,"file":"broadcast.js","sourceRoot":"","sources":["../src/broadcast.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,YAAY,CAAC;AAE/B,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAE7C;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,kBAAkB,GAAG,IAAI,CAAC;AAChC,MAAM,yBAAyB,GAAG,KAAK,CAAC;AACxC,MAAM,oBAAoB,GAAG,GAAG,CAAC,CAAC,kDAAkD;AAEpF,oFAAoF;AACpF,SAAS,sBAAsB,CAAC,EAAU;IACxC,MAAM,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC5B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACpC,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QACpB,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,GAAG;YAAE,OAAO,IAAI,CAAC;IAC5D,CAAC;IACD,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;AACnD,CAAC;AAED,MAAM,UAAU,qBAAqB,CACnC,IAAY,EACZ,WAAmB,EACnB,SAAoB,EACpB,KAAc;IAEd,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,MAAM,GAAwB,IAAI,CAAC;IACvC,IAAI,KAAK,GAA0C,IAAI,CAAC;IACxD,IAAI,YAAY,GAAyC,IAAI,CAAC;IAE9D,IAAI,CAAC;QACH,MAAM,GAAG,KAAK,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/D,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAU,EAAE,EAAE;YAChC,KAAK,CAAC,WAAW,EAAE,2BAA2B,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QAC/D,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;YACxB,2EAA2E;YAC3E,6EAA6E;QAC/E,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;YACf,IAAI,CAAC;gBACH,MAAM,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC;YAC7B,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,KAAK,CAAC,WAAW,EAAE,wBAAwB,GAAG,EAAE,CAAC,CAAC;YACpD,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,KAAK,CAAC,WAAW,EAAE,8BAA8B,GAAG,EAAE,CAAC,CAAC;QACxD,MAAM,GAAG,IAAI,CAAC;IAChB,CAAC;IAED,MAAM,QAAQ,GAAG,GAAS,EAAE;QAC1B,IAAI,OAAO,IAAI,CAAC,MAAM;YAAE,OAAO;QAC/B,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAC;QACzB,IAAI,CAAC,KAAK,IAAI,KAAK,KAAK,WAAW,EAAE,CAAC;YACpC,OAAO;QACT,CAAC;QAED,MAAM,OAAO,GAAoC;YAC/C,CAAC,EAAE,CAAC;YACJ,EAAE,EAAE,KAAK;YACT,IAAI;YACJ,OAAO,EAAE,WAAW;YACpB,KAAK,EAAE,SAAS;SACjB,CAAC;QACF,IAAI,KAAK;YAAE,OAAO,CAAC,KAAK,GAAG,KAAK,CAAC;QACjC,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;QAEjD,yEAAyE;QACzE,+DAA+D;QAC/D,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,CAAC,GAAG,EAAE,EAAE;YAC9D,IAAI,GAAG;gBAAE,KAAK,CAAC,WAAW,EAAE,kCAAkC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QAC/E,CAAC,CAAC,CAAC;QAEH,wEAAwE;QACxE,wEAAwE;QACxE,mEAAmE;QACnE,MAAM,MAAM,GAAG,sBAAsB,CAAC,KAAK,CAAC,CAAC;QAC7C,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,kBAAkB,EAAE,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE;gBACnD,IAAI,GAAG;oBAAE,KAAK,CAAC,WAAW,EAAE,iCAAiC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;YAC9E,CAAC,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC;IAEF,GAAG,CAAC,4CAA4C,kBAAkB,cAAc,WAAW,GAAG,CAAC,CAAC;IAChG,YAAY,GAAG,UAAU,CAAC,QAAQ,EAAE,oBAAoB,CAAC,CAAC;IAC1D,KAAK,GAAG,WAAW,CAAC,QAAQ,EAAE,yBAAyB,CAAC,CAAC;IAEzD,OAAO,GAAG,EAAE;QACV,OAAO,GAAG,IAAI,CAAC;QACf,IAAI,YAAY,EAAE,CAAC;YACjB,YAAY,CAAC,YAAY,CAAC,CAAC;YAC3B,YAAY,GAAG,IAAI,CAAC;QACtB,CAAC;QACD,IAAI,KAAK,EAAE,CAAC;YACV,aAAa,CAAC,KAAK,CAAC,CAAC;YACrB,KAAK,GAAG,IAAI,CAAC;QACf,CAAC;QACD,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,CAAC;gBACH,MAAM,CAAC,KAAK,EAAE,CAAC;YACjB,CAAC;YAAC,MAAM,CAAC;gBACP,yCAAyC;YAC3C,CAAC;YACD,MAAM,GAAG,IAAI,CAAC;QAChB,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"broadcast.js","sourceRoot":"","sources":["../src/broadcast.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAwB,MAAM,WAAW,CAAC;AAyP1D,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAM,CAAC;AAOtF;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAElE;AA+BD,gFAAgF;AAChF,QAAA,MAAM,cAAc,EAAE,MAAM,CAAC,aAAa,GAAG,WAAW,GAAG,UAAU,EAAE,MAAM,CAI5E,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,CAsBjE;AAKD;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EAAE,EACd,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,EAAE,CAyBV;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,OAAO,cAAc,EACtC,OAAO,EAAE,MAAM,EACf,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,CAIR;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,OAAO,cAAc,EACtC,OAAO,EAAE,MAAM,EACf,cAAc,EAAE,OAAO,EACvB,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,CAER;AAID,QAAA,MAAM,OAAO,SAAgB,CAAC;AAgqE9B,wBAAsB,MAAM,CAAC,IAAI,GAAE,MAAM,EAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAMzE;AAED,OAAO,EAAE,OAAO,EAAE,CAAC"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAwB,MAAM,WAAW,CAAC;AAyP1D,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAM,CAAC;AAOtF;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAElE;AA+BD,gFAAgF;AAChF,QAAA,MAAM,cAAc,EAAE,MAAM,CAAC,aAAa,GAAG,WAAW,GAAG,UAAU,EAAE,MAAM,CAI5E,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,CAsBjE;AAKD;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EAAE,EACd,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,EAAE,CAyBV;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,OAAO,cAAc,EACtC,OAAO,EAAE,MAAM,EACf,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,CAIR;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,OAAO,cAAc,EACtC,OAAO,EAAE,MAAM,EACf,cAAc,EAAE,OAAO,EACvB,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,CAER;AAID,QAAA,MAAM,OAAO,SAAgB,CAAC;AAquE9B,wBAAsB,MAAM,CAAC,IAAI,GAAE,MAAM,EAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAMzE;AAED,OAAO,EAAE,OAAO,EAAE,CAAC"}
package/dist/cli.js CHANGED
@@ -365,6 +365,7 @@ program
365
365
  .option('--wake-word', 'Enable wake word voice assistant ("오픈클로")')
366
366
  .option('--remote-daemon', 'Opt in to attaching this session to a daemon on another machine (gates --daemon-host and mDNS discovery)')
367
367
  .option('--daemon-host <host>', 'Explicit remote daemon endpoint (host or host:port); requires --remote-daemon')
368
+ .option('--daemon-token <token>', 'Pairing token of the remote hub (~/.agentdeck/auth-token there); or set AGENTDECK_DAEMON_TOKEN')
368
369
  .option('--no-env-args', 'Ignore AGENTDECK_COMMANDER_ARGS and AGENTDECK_<AGENT>_ARGS for this invocation')
369
370
  .option('--weight <n>', 'Deck/tab sort order override (integer, lower first; default 0)', parseWeight)
370
371
  .action(async (opts) => {
@@ -380,6 +381,7 @@ program
380
381
  wakeWord: !!opts.wakeWord,
381
382
  remoteDaemon: !!opts.remoteDaemon,
382
383
  daemonHost: opts.daemonHost,
384
+ daemonToken: opts.daemonToken,
383
385
  weight: opts.weight,
384
386
  modules: opts.local ? { mdns: false, adb: false, serial: false, pixoo: false, timebox: false } : {
385
387
  mdns: false, // daemon-only — session bridges never advertise mDNS
@@ -402,6 +404,7 @@ program
402
404
  .option('--no-codex-hooks', 'Skip ~/.codex/config.toml hook install')
403
405
  .option('--remote-daemon', 'Opt in to attaching this session to a daemon on another machine (gates --daemon-host and mDNS discovery)')
404
406
  .option('--daemon-host <host>', 'Explicit remote daemon endpoint (host or host:port); requires --remote-daemon')
407
+ .option('--daemon-token <token>', 'Pairing token of the remote hub (~/.agentdeck/auth-token there); or set AGENTDECK_DAEMON_TOKEN')
405
408
  .option('--no-env-args', 'Ignore AGENTDECK_COMMANDER_ARGS and AGENTDECK_<AGENT>_ARGS for this invocation')
406
409
  .option('--weight <n>', 'Deck/tab sort order override (integer, lower first; default 0)', parseWeight)
407
410
  .action(async (opts) => {
@@ -439,6 +442,7 @@ program
439
442
  postit: opts.postit !== false,
440
443
  remoteDaemon: !!opts.remoteDaemon,
441
444
  daemonHost: opts.daemonHost,
445
+ daemonToken: opts.daemonToken,
442
446
  weight: opts.weight,
443
447
  modules: opts.local ? { mdns: false, adb: false, serial: false, pixoo: false, timebox: false } : {
444
448
  mdns: false, // daemon-only
@@ -461,6 +465,7 @@ program
461
465
  .option('--no-opencode-hooks', 'Skip OpenCode observer plugin install')
462
466
  .option('--remote-daemon', 'Opt in to attaching this session to a daemon on another machine (gates --daemon-host and mDNS discovery)')
463
467
  .option('--daemon-host <host>', 'Explicit remote daemon endpoint (host or host:port); requires --remote-daemon')
468
+ .option('--daemon-token <token>', 'Pairing token of the remote hub (~/.agentdeck/auth-token there); or set AGENTDECK_DAEMON_TOKEN')
464
469
  .option('--no-env-args', 'Ignore AGENTDECK_COMMANDER_ARGS and AGENTDECK_<AGENT>_ARGS for this invocation')
465
470
  .option('--weight <n>', 'Deck/tab sort order override (integer, lower first; default 0)', parseWeight)
466
471
  .action(async (opts) => {
@@ -497,6 +502,7 @@ program
497
502
  postit: opts.postit !== false,
498
503
  remoteDaemon: !!opts.remoteDaemon,
499
504
  daemonHost: opts.daemonHost,
505
+ daemonToken: opts.daemonToken,
500
506
  weight: opts.weight,
501
507
  modules: opts.local ? { mdns: false, adb: false, serial: false, pixoo: false, timebox: false } : {
502
508
  mdns: false,
@@ -515,6 +521,7 @@ program
515
521
  .option('--local', 'Disable all device modules')
516
522
  .option('--remote-daemon', 'Opt in to attaching this session to a daemon on another machine (gates --daemon-host and mDNS discovery)')
517
523
  .option('--daemon-host <host>', 'Explicit remote daemon endpoint (host or host:port); requires --remote-daemon')
524
+ .option('--daemon-token <token>', 'Pairing token of the remote hub (~/.agentdeck/auth-token there); or set AGENTDECK_DAEMON_TOKEN')
518
525
  // Commander-layer half only: monitor spawns no agent command to weave into.
519
526
  .option('--no-env-args', 'Ignore AGENTDECK_COMMANDER_ARGS for this invocation')
520
527
  .option('--weight <n>', 'Deck/tab sort order override (integer, lower first; default 0)', parseWeight)
@@ -530,6 +537,7 @@ program
530
537
  debug: opts.debug,
531
538
  remoteDaemon: !!opts.remoteDaemon,
532
539
  daemonHost: opts.daemonHost,
540
+ daemonToken: opts.daemonToken,
533
541
  weight: opts.weight,
534
542
  modules: opts.local ? { mdns: false, adb: false, serial: false, pixoo: false, timebox: false } : undefined,
535
543
  });
@@ -550,7 +558,7 @@ daemon
550
558
  .option('-f, --foreground', 'Run in foreground (default: background fork)')
551
559
  .option('--wake-word', 'Enable wake word voice assistant ("오픈클로")')
552
560
  .action(async (opts) => {
553
- const { findExistingDaemon, probeDaemonHealth, readDaemonInfo, removeDaemonInfo, removeDaemonSession, requestDaemonStandDown, requestDaemonShutdown, waitForDaemonExit } = await import('./session-registry.js');
561
+ const { findExistingDaemon, probeDaemonHealth, readDaemonInfo, removeDaemonInfo, removeDaemonSession, requestDaemonStandDown, requestDaemonShutdown, waitForDaemonExit, waitForPortBindable } = await import('./session-registry.js');
554
562
  // Reverse two-tier upgrade path: the macOS app may already own the canonical
555
563
  // port with its in-process Swift daemon (Tier 1 — limited: no ADB devices,
556
564
  // no subscription usage, …). A plain "already running → exit" would strand
@@ -571,13 +579,52 @@ daemon
571
579
  await requestDaemonShutdown(targetPort);
572
580
  acked = true; // shutdown is best-effort (no ack body); rely on the exit wait
573
581
  }
574
- if (acked && await waitForDaemonExit(targetPort, 12000)) {
582
+ // Two conditions, not one. `waitForDaemonExit` proves the app stopped
583
+ // answering; `waitForPortBindable` proves the socket is actually gone.
584
+ // The app releases the port with NWListener.cancel(), which returns
585
+ // before the teardown completes — binding on the first signal alone is
586
+ // what silently demoted this daemon to the 9121 fallback and left the
587
+ // canonical port ownerless.
588
+ if (acked
589
+ && await waitForDaemonExit(targetPort, 12000)
590
+ // 30s, and that is not padding. Cancelling the app's NWListener does
591
+ // not free the port: macOS keeps a NECP reservation on it for ~14s
592
+ // afterwards, during which `lsof` shows no sockets at all and bind()
593
+ // still returns EADDRINUSE (measured 2026-08-06 — bindable at ~17s).
594
+ // Anything shorter gives up while the kernel, not the app, is holding
595
+ // the port. The app's takeover-yield window is longer still (45s), so
596
+ // it stays off the port until well after this wait has claimed it.
597
+ && await waitForPortBindable(targetPort, 30000)) {
575
598
  log(`App daemon yielded port ${targetPort}. Starting CLI daemon…`);
576
599
  // fall through — port is clear, proceed to bind below
577
600
  }
578
601
  else {
579
- log(`The AgentDeck app did not yield port ${targetPort} in time. Quit the app and retry 'agentdeck daemon start', or start on a different port with -p.`);
580
- process.exit(1);
602
+ // The app yielded but the PORT did not come back, and that is often
603
+ // not the app's doing: a WiFi device that has gone to sleep with bytes
604
+ // still queued to it leaves its socket in LAST_ACK on this port, and
605
+ // TCP holds that until it exhausts retransmits — minutes, sometimes.
606
+ // Network.framework exposes no SO_LINGER, so the app cannot RST out of
607
+ // it (measured 2026-08-06: two sleeping ESP32 boards, 11901 and 4
608
+ // bytes queued, were the only things left on 9120).
609
+ //
610
+ // Carry on rather than exiting. The daemon binds a fallback port and
611
+ // writes it to daemon.json, which is what every client resolves
612
+ // through and what the app itself re-reads when its yield window ends
613
+ // — so the deck keeps working. What must NOT happen is this being
614
+ // silent, which is how the canonical port ended up owned by nobody
615
+ // with the CLI reporting success.
616
+ log(`The AgentDeck app yielded port ${targetPort} but the port has not been released `
617
+ + `(usually a sleeping WiFi device holding a half-closed socket on it).`);
618
+ if (opts.port) {
619
+ // The port was ASKED for, not defaulted to. Quietly binding a
620
+ // different one would answer a question the user did not ask;
621
+ // an explicit choice fails loudly instead.
622
+ log(`Quit the AgentDeck app and retry, or choose another port.`);
623
+ process.exit(1);
624
+ }
625
+ log(`Starting on a fallback port instead — clients resolve it from daemon.json. `
626
+ + `For the canonical port, quit the AgentDeck app and retry, or pass -p.`);
627
+ // fall through to normal port selection
581
628
  }
582
629
  }
583
630
  else {
@@ -1287,6 +1334,29 @@ program
1287
1334
  // qrcode not available
1288
1335
  }
1289
1336
  });
1337
+ program
1338
+ .command('token')
1339
+ .description('Show or rotate the pairing token (~/.agentdeck/auth-token)')
1340
+ .argument('[action]', "'show' (default) prints the token; 'rotate' replaces it", 'show')
1341
+ .action(async (action) => {
1342
+ const { getOrCreateToken, rotateToken } = await import('./auth.js');
1343
+ if (action === 'rotate') {
1344
+ const token = rotateToken();
1345
+ log(`New pairing token: ${token}`);
1346
+ log('All paired clients must re-pair: rescan the QR on companion apps (agentdeck qr),');
1347
+ log('re-provision WiFi ESP32 boards (they re-provision automatically over USB serial),');
1348
+ log('and update AGENTDECK_DAEMON_TOKEN on any remote workers.');
1349
+ log('Restart the daemon (agentdeck daemon restart) so it picks up the new token.');
1350
+ }
1351
+ else if (action === 'show') {
1352
+ log(getOrCreateToken());
1353
+ log('(Anyone with this token can control your sessions over the LAN — treat it like a password.)');
1354
+ }
1355
+ else {
1356
+ console.error(`Unknown action '${action}' — use 'show' or 'rotate'.`);
1357
+ process.exitCode = 1;
1358
+ }
1359
+ });
1290
1360
  program
1291
1361
  .command('inject-test')
1292
1362
  .description('Test observed-answer injection into a terminal or app (tuning aid)')