@geohar/un-bien 0.9.0 → 0.14.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.
Files changed (136) hide show
  1. package/README.md +99 -130
  2. package/dist/actions/handlers.js.map +1 -1
  3. package/dist/actions/registry.js.map +1 -1
  4. package/dist/bin/launcher.js.map +1 -1
  5. package/dist/commands/deps.d.ts +156 -0
  6. package/dist/commands/deps.js +2 -0
  7. package/dist/commands/deps.js.map +1 -0
  8. package/dist/commands/fork_link.d.ts +4 -0
  9. package/dist/commands/fork_link.js +45 -0
  10. package/dist/commands/fork_link.js.map +1 -0
  11. package/dist/commands/housekeeping.d.ts +34 -0
  12. package/dist/commands/housekeeping.js +275 -0
  13. package/dist/commands/housekeeping.js.map +1 -0
  14. package/dist/commands/info.d.ts +43 -0
  15. package/dist/commands/info.js +127 -0
  16. package/dist/commands/info.js.map +1 -0
  17. package/dist/commands/lifecycle.d.ts +45 -0
  18. package/dist/commands/lifecycle.js +708 -0
  19. package/dist/commands/lifecycle.js.map +1 -0
  20. package/dist/commands/pairing.d.ts +26 -0
  21. package/dist/commands/pairing.js +167 -0
  22. package/dist/commands/pairing.js.map +1 -0
  23. package/dist/commands/register.d.ts +12 -0
  24. package/dist/commands/register.js +255 -0
  25. package/dist/commands/register.js.map +1 -0
  26. package/dist/commands/relay.d.ts +28 -0
  27. package/dist/commands/relay.js +81 -0
  28. package/dist/commands/relay.js.map +1 -0
  29. package/dist/commands/session_ops.d.ts +52 -0
  30. package/dist/commands/session_ops.js +75 -0
  31. package/dist/commands/session_ops.js.map +1 -0
  32. package/dist/config.d.ts +2 -1
  33. package/dist/config.js.map +1 -1
  34. package/dist/daemon/install.d.ts +4 -3
  35. package/dist/daemon/install.js +7 -1
  36. package/dist/daemon/install.js.map +1 -1
  37. package/dist/enrich_tool_args.js.map +1 -1
  38. package/dist/extension_ui_bridge.js.map +1 -1
  39. package/dist/image_codec.js.map +1 -1
  40. package/dist/index.d.ts +32 -101
  41. package/dist/index.js +818 -2867
  42. package/dist/index.js.map +1 -1
  43. package/dist/launch.d.ts +9 -1
  44. package/dist/launch.js +39 -4
  45. package/dist/launch.js.map +1 -1
  46. package/dist/launcher/launcher.js +21 -2
  47. package/dist/launcher/launcher.js.map +1 -1
  48. package/dist/mcp/mesh_result.js.map +1 -1
  49. package/dist/mcp/mesh_server.js.map +1 -1
  50. package/dist/mesh/canonical.js +1 -1
  51. package/dist/mesh/canonical.js.map +1 -1
  52. package/dist/mesh/client.js.map +1 -1
  53. package/dist/mesh/encoding.js.map +1 -1
  54. package/dist/mesh/self_revoke.js.map +1 -1
  55. package/dist/mesh/siblings.js.map +1 -1
  56. package/dist/mesh/verify.js +1 -1
  57. package/dist/mesh/verify.js.map +1 -1
  58. package/dist/pairing/crypto.js +0 -1
  59. package/dist/pairing/crypto.js.map +1 -1
  60. package/dist/pairing/peer_trust.js.map +1 -1
  61. package/dist/pairing/qr.js.map +1 -1
  62. package/dist/pairing/storage.js +4 -3
  63. package/dist/pairing/storage.js.map +1 -1
  64. package/dist/panel_bridge.js +1 -0
  65. package/dist/panel_bridge.js.map +1 -1
  66. package/dist/paths.d.ts +22 -8
  67. package/dist/paths.js +37 -10
  68. package/dist/paths.js.map +1 -1
  69. package/dist/protocol/codec.js.map +1 -1
  70. package/dist/rooms.js +1 -4
  71. package/dist/rooms.js.map +1 -1
  72. package/dist/session/bridge.js.map +1 -1
  73. package/dist/session/broker.js.map +1 -1
  74. package/dist/session/broker_remote.js.map +1 -1
  75. package/dist/session/capabilities.d.ts +11 -0
  76. package/dist/session/capabilities.js +35 -0
  77. package/dist/session/capabilities.js.map +1 -0
  78. package/dist/session/cwd_lock.js +3 -3
  79. package/dist/session/cwd_lock.js.map +1 -1
  80. package/dist/session/debug_log.js +13 -3
  81. package/dist/session/debug_log.js.map +1 -1
  82. package/dist/session/envelope.js.map +1 -1
  83. package/dist/session/global_config.d.ts +1 -1
  84. package/dist/session/global_config.js +1 -1
  85. package/dist/session/global_config.js.map +1 -1
  86. package/dist/session/ipc.d.ts +2 -2
  87. package/dist/session/ipc.js.map +1 -1
  88. package/dist/session/leader_election.js.map +1 -1
  89. package/dist/session/local_config.d.ts +37 -0
  90. package/dist/session/local_config.js +45 -1
  91. package/dist/session/local_config.js.map +1 -1
  92. package/dist/session/mesh_node.d.ts +1 -1
  93. package/dist/session/mesh_node.js +1 -1
  94. package/dist/session/mesh_node.js.map +1 -1
  95. package/dist/session/peer.js.map +1 -1
  96. package/dist/session/peer_inventory.js.map +1 -1
  97. package/dist/session/peer_limits.js.map +1 -1
  98. package/dist/session/received_images.d.ts +63 -0
  99. package/dist/session/received_images.js +273 -0
  100. package/dist/session/received_images.js.map +1 -0
  101. package/dist/session/relay_lifecycle.d.ts +219 -0
  102. package/dist/session/relay_lifecycle.js +764 -0
  103. package/dist/session/relay_lifecycle.js.map +1 -0
  104. package/dist/session/rpc_envelope.d.ts +10 -0
  105. package/dist/session/rpc_envelope.js +9 -0
  106. package/dist/session/rpc_envelope.js.map +1 -1
  107. package/dist/session/rpc_handlers.d.ts +53 -0
  108. package/dist/session/rpc_handlers.js +268 -0
  109. package/dist/session/rpc_handlers.js.map +1 -0
  110. package/dist/session/rpc_inbound.d.ts +41 -6
  111. package/dist/session/rpc_inbound.js +55 -0
  112. package/dist/session/rpc_inbound.js.map +1 -1
  113. package/dist/session/setup_wizard.js.map +1 -1
  114. package/dist/session/tools.js.map +1 -1
  115. package/dist/session/wizard.js.map +1 -1
  116. package/dist/state_migration.d.ts +45 -0
  117. package/dist/state_migration.js +104 -0
  118. package/dist/state_migration.js.map +1 -0
  119. package/dist/subagent_rooms.d.ts +19 -0
  120. package/dist/subagent_rooms.js +146 -14
  121. package/dist/subagent_rooms.js.map +1 -1
  122. package/dist/test_hooks.d.ts +180 -0
  123. package/dist/test_hooks.js +98 -0
  124. package/dist/test_hooks.js.map +1 -0
  125. package/dist/transport/peer_channel.d.ts +11 -0
  126. package/dist/transport/peer_channel.js +34 -11
  127. package/dist/transport/peer_channel.js.map +1 -1
  128. package/dist/transport/pi_forward_client.js.map +1 -1
  129. package/dist/transport/relay_client.d.ts +5 -2
  130. package/dist/transport/relay_client.js +6 -3
  131. package/dist/transport/relay_client.js.map +1 -1
  132. package/dist/ui/footer.js.map +1 -1
  133. package/docs/daemon.md +160 -170
  134. package/package.json +1 -1
  135. package/service-templates/launchd.plist.template +5 -4
  136. package/service-templates/systemd.service.template +3 -2
package/README.md CHANGED
@@ -100,7 +100,7 @@ current turn, and receive any later reply through the inbox/turn flow with
100
100
  `re` correlating it to the original message id.
101
101
 
102
102
  Peers on the same machine talk over a Unix domain socket at
103
- `~/.pi/un-bien/sessions/<session-name>/broker.sock`. When sibling PCs are paired,
103
+ `~/.local/state/un-bien/sessions/<session-name>/broker.sock`. When sibling PCs are paired,
104
104
  a leader-capable Extension or MCP participant bridges the opaque cross-PC
105
105
  addresses over the relay; local-only use stays on UDS when relay access is off.
106
106
  Useful for splitting work across roles (`backend`, `frontend`, `tests`,
@@ -240,7 +240,7 @@ Once the relay is up (`/unbien relay status` shows `started` or `paired`):
240
240
 
241
241
  A QR code is printed in the terminal. Scan it with the Un Bien mobile app.
242
242
  Pairing is **per machine** — once a device is paired, every Pi process on
243
- this machine accepts it (it lives in `~/.pi/un-bien/peers.json`).
243
+ this machine accepts it (it lives in `~/.local/state/un-bien/peers.json`).
244
244
 
245
245
  To list paired devices:
246
246
 
@@ -374,7 +374,7 @@ original message id.
374
374
 
375
375
  The wire format is a 5-field envelope `{ from, to, id, re, body }` serialized
376
376
  as one JSON line per message. The leader's broker writes an `audit.jsonl`
377
- log at `~/.pi/un-bien/sessions/<name>/audit.jsonl` for postmortem inspection.
377
+ log at `~/.local/state/un-bien/sessions/<name>/audit.jsonl` for postmortem inspection.
378
378
 
379
379
  Useful commands:
380
380
 
@@ -409,56 +409,17 @@ real name to the peer.
409
409
  | `/unbien relay url <url>` | Same as `set-relay` |
410
410
  | `/unbien config` | Show the effective relay URL and its source (env / config / unset) |
411
411
 
412
- ### Daemon fleet (one supervisor, N background Pis — see [Daemon mode](#daemon-mode))
413
-
414
- | Command | Description |
415
- | ------------------------------------------- | ----------------------------------------------------------------------------- |
416
- | `/unbien create <cwd> [--name X]` | Register a folder as a daemon |
417
- | `/unbien remove <id>` | Unregister a daemon (local config preserved) |
418
- | `/unbien daemons` | List registered daemons + state |
419
- | `/unbien daemon start` | Start every registered daemon |
420
- | `/unbien daemon stop` | Stop every running daemon (`/unbien stop` stops only the local terminal) |
421
- | `/unbien daemon restart` | Stop + start all daemons |
422
- | `/unbien daemon status` | Detailed runtime status (pid, uptime, restart count) |
423
- | `/unbien daemon send <id> "<text>"` | Send a prompt to a specific daemon |
424
- | `/unbien cron add <id> "<expr>" "<prompt>"` | Schedule a recurring prompt (`--tz`, `--wake`, `--no-skip-busy`, `--catchup`) |
425
- | `/unbien cron list` | List scheduled jobs (schedule, enabled, next run, last status) |
426
- | `/unbien cron run <jobId>` | Fire a job now (ignores its schedule) |
427
- | `/unbien cron enable\|disable <jobId>` | Toggle a job on/off |
428
- | `/unbien cron remove <jobId>` | Delete a job |
429
- | `/unbien cron log [<jobId>] [--tail N]` | Read the fire/skip audit log |
430
- | `/unbien install` | Install `pi-supervisord` as a system service |
431
- | `/unbien uninstall` | Remove the system service (registry preserved) |
412
+ ### Service install & remote launch (see [Remote launch](#remote-launch))
413
+
414
+ | Command | Description |
415
+ | ------------------- | ---------------------------------------------------------------------------------- |
416
+ | `/unbien install` | Install the **unbien-launcher** daemon as a system service + link the `unbien` CLI |
417
+ | `/unbien uninstall` | Remove the launcher service + CLI links (pairing and config preserved) |
432
418
 
433
419
  All commands above work both as Pi slash commands (interactive) and as
434
420
  shell-level `unbien <subcommand>` when the package is installed
435
421
  globally (`npm install -g @geohar/un-bien`).
436
422
 
437
- ### Scheduled prompts (`cron`)
438
-
439
- `unbien cron` schedules **recurring prompts** to daemons through the
440
- supervisor — e.g. a daily "summarise new PRs". Output flows fire-and-forget to
441
- the mesh/app like any prompt; the cron layer only audits the dispatch.
442
-
443
- - **Schedule** is a cron expression (croner syntax; an optional 6th _seconds_
444
- field is supported), with an optional IANA timezone via `--tz`:
445
-
446
- ```sh
447
- unbien cron add a1b2c3d4 "0 9 * * *" "Summarise new PRs" --tz America/Sao_Paulo
448
- ```
449
-
450
- - **Minimum interval is 60s** — more frequent schedules are rejected (guards
451
- token cost + pileup). A fire is **skipped when the daemon is mid-turn**
452
- (`--no-skip-busy` to override); `--wake` starts a stopped daemon first;
453
- `--catchup` runs once on supervisor start if the previous run was missed.
454
- - **Prerequisite**: the supervisor must run as a service (`unbien install`).
455
- Without it there is no scheduler, and `cron` commands say so instead of
456
- silently pretending to schedule.
457
- - **Audit**: every fire **and** every skip appends one line to
458
- `~/.pi/un-bien/cron.jsonl` with a `result` of `delivered`,
459
- `woke_and_delivered`, `deliver_failed`, `skipped_busy`, `skipped_down`, or
460
- `skipped_disabled` — read it with `unbien cron log`.
461
-
462
423
  ### Footer + title
463
424
 
464
425
  - `📡 local (N)` — current agent session and peer count (local mesh)
@@ -472,105 +433,101 @@ tabs.
472
433
 
473
434
  ---
474
435
 
475
- ## Daemon mode
436
+ ## Remote launch
476
437
 
477
- When you want a Pi to keep running in the background (responding to
478
- mobile prompts at 3am, processing cron jobs, monitoring a folder while
479
- you're not at the keyboard), promote it to a **daemon** managed by a
480
- single OS-level supervisor.
438
+ The **unbien-launcher** is a lightweight mesh peer **not** a Pi session — that
439
+ lets a paired app reach your machine even when no Pi is running. It reuses the
440
+ machine's un-bien config (identity + relay), joins the machine-level control
441
+ room, advertises `remote_launch`, and spawns a `pi` window via `tmux` (default)
442
+ or `herdr` when the app asks for one.
481
443
 
482
444
  See [`docs/daemon.md`](./docs/daemon.md) for troubleshooting.
483
445
 
484
446
  ### One-time setup
485
447
 
486
448
  ```bash
487
- # Install the package globally so `unbien` and `pi-supervisord`
488
- # are on your PATH (`pi install npm:@geohar/un-bien` alone makes the Pi
489
- # extension available but does NOT expose the CLI binaries — see
449
+ # Install the package globally so the `unbien` CLI is on your PATH
450
+ # (`pi install npm:@geohar/un-bien` alone makes the Pi extension available
451
+ # but does NOT expose the CLI binary — see
490
452
  # https://docs.npmjs.com/cli/v10/configuring-npm/package-json#bin).
491
453
  npm install -g @geohar/un-bien
492
454
 
493
- # Install the supervisor as a user-level system service. Linux uses
455
+ # Install the launcher as a user-level system service. Linux uses
494
456
  # systemd --user; macOS uses launchd LaunchAgent. Both auto-start at
495
457
  # login and survive reboots.
496
458
  unbien install
497
459
  ```
498
460
 
461
+ Or from inside Pi: `/unbien install` — same service, plus it links the
462
+ `unbien` CLI into `~/.local/bin` so no global npm install is needed.
463
+
499
464
  The `install` command:
500
465
 
501
- - Writes `~/.config/systemd/user/unbien-supervisord.service` (Linux)
502
- or `~/Library/LaunchAgents/dev.unbien.supervisord.plist` (macOS)
466
+ - Writes `~/.config/systemd/user/unbien-launcher.service` (Linux) or
467
+ `~/Library/LaunchAgents/dev.unbien.launcher.plist` (macOS); on Windows it
468
+ registers a Task Scheduler task (`RemotePiLauncher`) and prompts for
469
+ elevation once
503
470
  - Activates it via `systemctl --user enable --now` or `launchctl bootstrap`
504
- - The supervisor starts immediately and re-starts on every login
471
+ - The launcher starts immediately and re-starts on every login
505
472
 
506
- ### Per-folder workflow
473
+ A standalone package exists for running the launcher in the foreground, no
474
+ service: `npm install -g @geohar/un-bien-launcher` → `unbien-launcher`. Same
475
+ daemon, shipped as its own binary (see the
476
+ [launcher README](../launcher/README.md)).
507
477
 
508
- For each agent you want to keep alive 24/7:
478
+ ### Enable remote launch
509
479
 
510
- ```bash
511
- # 1. Configure the agent interactively first (one time).
512
- cd ~/Movies
513
- pi # /unbien → setup wizard, /unbien pair, etc
480
+ The launcher only spawns sessions where remote launch is **opted in**:
514
481
 
515
- # 2. Promote to a daemon. The id is derived from the cwd
516
- # (sha256(realpath)[:8]), stable across machines.
517
- unbien create ~/Movies --name "Video Editor"
518
- # → Daemon registered: id=4e39152d name="Video Editor" cwd=/Users/x/Movies
482
+ - per directory: `allow_remote_launch: true` in `<cwd>/.pi/un-bien/config.json`
483
+ - or machine-wide: `defaults.allow_remote_launch: true` in the global config
484
+ (`~/.pi/extensions/un-bien.json`)
519
485
 
520
- # 3. Start it (supervisor spawns `pi --mode rpc` for this folder).
521
- unbien daemon start
522
- ```
523
-
524
- Now you can:
486
+ The spawn backend is machine-wide: `launch.backend` in the global config
487
+ `tmux` (default; one shared `un-bien` tmux session, a window per launched Pi)
488
+ or `herdr`. The backend binary must be on PATH: `unbien install` snapshots
489
+ your PATH at install time, so re-run it after installing new tools.
525
490
 
526
- ```bash
527
- unbien daemons # list + state
528
- unbien daemon status # uptime, pid, restart count
529
- unbien daemon send 4e39152d "Cut the first 30 seconds of latest clip"
530
- unbien daemon stop # stop all
531
- unbien daemon restart # restart all
532
- ```
491
+ ### Launch names
533
492
 
534
- The agent receives the prompt as if a user typed it; its response flows
535
- back through the relay/mesh you configured during interactive setup
536
- mobile app sees it live, other agents on the same machine can see it
537
- via the local UDS mesh.
493
+ A `session_launch` request may carry a `name`. On the tmux backend the
494
+ launched Pi gets it as `pi -n <name>` pi's native session display name
495
+ which the extension resolves as the session-scoped agent name: the mesh
496
+ join, cwd lock, `room_meta`, and the app's session tile all show it instead
497
+ of the path-derived default. It is per-process (never inherited by
498
+ subagents or child processes) and never persisted — a manual `pi` in the
499
+ same directory keeps its configured `agent_name`. `/unbien rename` still
500
+ wins once it runs. The herdr backend uses the name as the workspace label
501
+ only until it can pass args through to pi.
538
502
 
539
503
  ### Removing or uninstalling
540
504
 
541
505
  ```bash
542
- unbien remove <id> # unregister one daemon (config preserved)
543
- unbien uninstall # remove the supervisor service (registry kept)
506
+ unbien uninstall # remove the launcher service (pairing + config kept)
544
507
  ```
545
508
 
546
- `uninstall` is reversible — re-running `install` later brings every
547
- registered daemon back. To wipe the registry entirely, `rm
548
- ~/.pi/un-bien/daemons.json`.
509
+ `uninstall` is reversible — re-running `install` later brings the launcher
510
+ back. Pairing and identity live in the state root and are never touched.
549
511
 
550
512
  ### Where to find logs
551
513
 
552
- | Platform | Command |
553
- | -------- | -------------------------------------------- |
554
- | Linux | `journalctl --user -u unbien-supervisord -f` |
555
- | macOS | `tail -f ~/.pi/un-bien/supervisord.log` |
556
-
557
- Each spawned daemon's stderr is forwarded into the supervisor's log
558
- with a `[<cwd>]` prefix, so a single log stream shows every agent.
514
+ | Platform | Command |
515
+ | -------- | ------------------------------------------------------- |
516
+ | Linux | `journalctl --user -u unbien-launcher -f` |
517
+ | macOS | `tail -f ~/.local/state/un-bien/launcher.log` |
518
+ | Windows | `Get-Content ~\.local\state\un-bien\launcher.log -Wait` |
559
519
 
560
520
  ### Caveats
561
521
 
562
- - **Tool execution is not gated.** Daemons inherit the same Pi config
563
- the interactive run uses Bash, Edit, Write etc. all execute without
564
- prompting. Configure Pi's tool permissions to taste before promoting
565
- a folder to daemon.
566
- - **Pairing still happens interactively.** Daemons don't show a QR
567
- themselves; the keypair + paired devices come from the prior `pi`
568
- session in the same folder.
569
- - **Single supervisor.** If `pi-supervisord` crashes all daemons go
570
- down with it. systemd/launchd restarts it within seconds; daemons
571
- come back automatically.
572
- - **One daemon per cwd.** The `roomIdForCwd` derivation makes daemons
573
- by-path; two daemons in the same folder is rejected at `create` time.
522
+ - **Pairing still happens interactively.** The launcher doesn't show a QR; it
523
+ only trusts owners already paired via `/unbien pair` in a Pi session on
524
+ this machine.
525
+ - **Tool execution is not gated.** A launched Pi inherits the same Pi config
526
+ an interactive run uses Bash, Edit, Write etc. all execute without
527
+ prompting. Configure Pi's tool permissions to taste before enabling
528
+ `allow_remote_launch`.
529
+ - **Same identity, same relay.** The launcher reads the machine's global
530
+ config `/unbien config` shows exactly what it will use.
574
531
 
575
532
  ---
576
533
 
@@ -578,17 +535,14 @@ with a `[<cwd>]` prefix, so a single log stream shows every agent.
578
535
 
579
536
  ### Files
580
537
 
581
- | Path | Scope | What's in it |
582
- | --------------------------------------------- | ----------------- | ------------------------------------------------------------------------------ |
583
- | `~/.pi/extensions/un-bien.json` | Per-user (global) | `relay` URL, `defaults`, `identity`, `debug` — the global settings (see below) |
584
- | `<cwd>/.pi/un-bien/config.json` | Per-directory | `agent_name`, `auto_start_relay`, `allow_remote_launch` |
585
- | `~/.pi/un-bien/identity.json` | Per-machine | Paired identity keypair (file identity backend; `0600`) |
586
- | `~/.pi/un-bien/peers.json` | Per-machine | Paired mobile devices |
587
- | `~/.pi/un-bien/sessions/<name>/` | Per-session | Broker socket + `audit.jsonl` |
588
- | `~/.pi/un-bien/daemons.json` | Per-machine | Daemon registry |
589
- | `~/.pi/un-bien/cron.json` · `cron.jsonl` | Per-machine | Cron registry + fire/skip audit log |
590
- | `~/.pi/un-bien/supervisor.sock` | Per-machine | Supervisor control socket |
591
- | `~/.pi/un-bien/skills/agent-network/SKILL.md` | Per-user | Agent skill the LLM reads |
538
+ | Path | Scope | What's in it |
539
+ | ------------------------------------------------------ | ----------------- | ------------------------------------------------------------------------------ |
540
+ | `~/.pi/extensions/un-bien.json` | Per-user (global) | `relay` URL, `defaults`, `identity`, `debug` — the global settings (see below) |
541
+ | `<cwd>/.pi/un-bien/config.json` | Per-directory | `agent_name`, `auto_start_relay`, `allow_remote_launch` |
542
+ | `~/.local/state/un-bien/identity.json` | Per-machine | Paired identity keypair (file identity backend; `0600`) |
543
+ | `~/.local/state/un-bien/peers.json` | Per-machine | Paired mobile devices |
544
+ | `~/.local/state/un-bien/sessions/<name>/` | Per-session | Broker socket + `audit.jsonl` |
545
+ | `~/.local/state/un-bien/skills/agent-network/SKILL.md` | Per-user | Agent skill the LLM reads |
592
546
 
593
547
  ### Global settings — `~/.pi/extensions/un-bien.json`
594
548
 
@@ -606,13 +560,19 @@ fields are optional:
606
560
  // Pin auto-start once instead of dropping a file into every repo.
607
561
  "defaults": { "auto_start_relay": true },
608
562
 
563
+ // Remote-launch backend a `session_launch` request uses on THIS machine:
564
+ // "tmux" (default; one shared tmux session, a window per launched Pi) or
565
+ // "herdr". Machine-wide choice — whether remote launch is enabled at all
566
+ // stays the per-cwd `allow_remote_launch` flag.
567
+ "launch": { "backend": "tmux" },
568
+
609
569
  // Machine-identity storage. `storage` selects the PRIMARY backend for this
610
570
  // Pi's long-term Ed25519 seed: "keychain" (OS-secured, default) or "file"
611
571
  // (a 0600 seed file — the SSH-private-key model: cat-able, portable,
612
572
  // works headless). `path` overrides the file-backend location
613
- // (default ~/.pi/un-bien/identity.json). The unselected backend is still
573
+ // (default ~/.local/state/un-bien/identity.json). The unselected backend is still
614
574
  // READ to recover an existing identity, but never written.
615
- "identity": { "storage": "keychain", "path": "~/.pi/un-bien/identity.json" },
575
+ "identity": { "storage": "keychain", "path": "~/.local/state/un-bien/identity.json" },
616
576
 
617
577
  // File-based diagnostic logs. Off by default. Read from config (not env)
618
578
  // because the daemon fork usually runs detached without a shell's env.
@@ -635,14 +595,23 @@ Written by the `/unbien` setup wizard:
635
595
  | Variable | Purpose |
636
596
  | ---------------------- | -------------------------------------------------------------------------------------------------------------------------- |
637
597
  | `UNBIEN_RELAY` | Relay URL override (highest precedence, ahead of the config file). CI / one-off use. |
638
- | `UNBIEN_DIR` | Absolute override of the **state** dir itself (no `.pi/un-bien` suffix). The knob for an XDG-style relocation. |
639
- | `UNBIEN_HOME` | Stand-in `$HOME`; state lives at `<UNBIEN_HOME>/.pi/un-bien`. When both are set, `UNBIEN_DIR` wins. |
598
+ | `UNBIEN_STATE_DIR` | Absolute override of the **state** dir itself. The knob for relocating state. Highest precedence of the state knobs. |
599
+ | `UNBIEN_DIR` | Legacy absolute override of the **state** dir itself (no suffix appended). Lower precedence than `UNBIEN_STATE_DIR`. |
600
+ | `UNBIEN_HOME` | Legacy stand-in `$HOME`; state lives at `<UNBIEN_HOME>/.pi/un-bien`. Lower precedence than `UNBIEN_DIR`. |
640
601
  | `PI_CODING_AGENT_DIR` | The Pi host's settings root (default `~/.pi`); the global config lives at `<PI_CODING_AGENT_DIR>/extensions/un-bien.json`. |
641
- | `UNBIEN_DIRECT_CONFIG` | Inline per-cwd config (JSON) instead of a `.pi/un-bien/config.json` file — used by the daemon supervisor. |
642
-
643
- `UNBIEN_DIR`/`UNBIEN_HOME` relocate un-bien **state** (sessions, daemon
644
- registries, cwd locks, paired identity). `PI_CODING_AGENT_DIR` relocates only
645
- the global **config**, so it can sit beside the coding agent's own settings.
602
+ | `UNBIEN_DIRECT_CONFIG` | Inline per-cwd config (JSON) instead of a `.pi/un-bien/config.json` file — CI / one-off use. |
603
+
604
+ The state root resolves as `UNBIEN_STATE_DIR` > `UNBIEN_DIR` > `UNBIEN_HOME` >
605
+ `${XDG_STATE_HOME:-~/.local/state}/un-bien` the XDG-style default. `UNBIEN_DIR`/
606
+ `UNBIEN_HOME` relocate un-bien **state** (sessions, cwd locks, paired identity) and stay honored for older deployments. On first use
607
+ of the new default, a one-time best-effort migration moves any legacy
608
+ `~/.pi/un-bien` state (peers.json included) into the new root.
609
+ `PI_CODING_AGENT_DIR` relocates only the global **config**, so it can sit
610
+ beside the coding agent's own settings.
611
+
612
+ **Deprecated**: the relay no longer defaults its SQLite DB to a CWD-relative
613
+ `data/mesh.db` — see the relay README for its `UNBIEN_MESH_DB_PATH` /
614
+ state-root resolution.
646
615
 
647
616
  Override the relay for a single run without persisting:
648
617
 
@@ -1 +1 @@
1
- {"version":3,"file":"handlers.js","sourceRoot":"","sources":["../../src/actions/handlers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAuEH;8EAC8E;AAC9E,MAAM,UAAU,aAAa,CAAC,KAAiB;IAC7C,OAAO;QACL,EAAE,EAAE,KAAK,CAAC,EAAE;QACZ,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,cAAc,EAAE,KAAK,CAAC,aAAa;QACnC,gEAAgE;QAChE,2EAA2E;QAC3E,wDAAwD;QACxD,MAAM,EAAE,KAAK,CAAC,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,IAAI,KAAK;KAChD,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"handlers.js","sourceRoot":"","sources":["../../src/actions/handlers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAuEH;8EAC8E;AAC9E,MAAM,UAAU,aAAa,CAAC,KAAiB;IAC7C,OAAO;QACL,EAAE,EAAE,KAAK,CAAC,EAAE;QACZ,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,cAAc,EAAE,KAAK,CAAC,aAAa;QACnC,gEAAgE;QAChE,2EAA2E;QAC3E,wDAAwD;QACxD,MAAM,EAAE,KAAK,CAAC,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,IAAI,KAAK;KAChD,CAAA;AACH,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/actions/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AASH,IAAI,iBAAiB,GAA+B,IAAI,CAAC;AAEzD;;;GAGG;AACH,MAAM,aAAa,GAAwB;IACzC,OAAO,KAAI,CAAC;IACZ,YAAY;QACV,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,IAAI;QACF,OAAO,SAAS,CAAC;IACnB,CAAC;CACF,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CACjC,GAA+B;IAE/B,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,GAAG,EAAE,aAAa,CAAC;QAChC,IAAI,IAAI,EAAE,CAAC;YACT,iBAAiB,GAAG,IAAI,CAAC;YACzB,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,qEAAqE;IACvE,CAAC;IACD,OAAO,iBAAiB,IAAI,aAAa,CAAC;AAC5C,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,2BAA2B;IACzC,iBAAiB,GAAG,IAAI,CAAC;AAC3B,CAAC"}
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/actions/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AASH,IAAI,iBAAiB,GAA+B,IAAI,CAAA;AAExD;;;GAGG;AACH,MAAM,aAAa,GAAwB;IACzC,OAAO,KAAI,CAAC;IACZ,YAAY;QACV,OAAO,EAAE,CAAA;IACX,CAAC;IACD,IAAI;QACF,OAAO,SAAS,CAAA;IAClB,CAAC;CACF,CAAA;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CACjC,GAA+B;IAE/B,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,GAAG,EAAE,aAAa,CAAA;QAC/B,IAAI,IAAI,EAAE,CAAC;YACT,iBAAiB,GAAG,IAAI,CAAA;YACxB,OAAO,IAAI,CAAA;QACb,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,qEAAqE;IACvE,CAAC;IACD,OAAO,iBAAiB,IAAI,aAAa,CAAA;AAC3C,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,2BAA2B;IACzC,iBAAiB,GAAG,IAAI,CAAA;AAC1B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"launcher.js","sourceRoot":"","sources":["../../src/bin/launcher.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAExD,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,MAAM,aAAa,EAAE,CAAC;IACrC,sCAAsC;IACtC,OAAO,CAAC,GAAG,CACT,gDAAgD,MAAM,CAAC,MAAM,GAAG;QAC9D,QAAQ,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,qBAAqB,CACvD,CAAC;IAEF,IAAI,YAAY,GAAG,KAAK,CAAC;IACzB,MAAM,QAAQ,GAAG,CAAC,MAAc,EAAE,EAAE;QAClC,IAAI,YAAY;YAAE,OAAO;QACzB,YAAY,GAAG,IAAI,CAAC;QACpB,sCAAsC;QACtC,OAAO,CAAC,GAAG,CAAC,sBAAsB,MAAM,kBAAkB,CAAC,CAAC;QAC5D,MAAM,CAAC,IAAI,EAAE,CAAC;QACd,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC;IACF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC/C,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;AACnD,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IAC5B,sCAAsC;IACtC,OAAO,CAAC,KAAK,CACX,6BAA6B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAChF,CAAC;IACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"launcher.js","sourceRoot":"","sources":["../../src/bin/launcher.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAA;AAEvD,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,MAAM,aAAa,EAAE,CAAA;IACpC,sCAAsC;IACtC,OAAO,CAAC,GAAG,CACT,gDAAgD,MAAM,CAAC,MAAM,GAAG;QAC9D,QAAQ,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,qBAAqB,CACvD,CAAA;IAED,IAAI,YAAY,GAAG,KAAK,CAAA;IACxB,MAAM,QAAQ,GAAG,CAAC,MAAc,EAAE,EAAE;QAClC,IAAI,YAAY;YAAE,OAAM;QACxB,YAAY,GAAG,IAAI,CAAA;QACnB,sCAAsC;QACtC,OAAO,CAAC,GAAG,CAAC,sBAAsB,MAAM,kBAAkB,CAAC,CAAA;QAC3D,MAAM,CAAC,IAAI,EAAE,CAAA;QACb,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC,CAAA;IACD,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAA;IAC9C,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAA;AAClD,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IAC5B,sCAAsC;IACtC,OAAO,CAAC,KAAK,CACX,6BAA6B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAChF,CAAA;IACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACjB,CAAC,CAAC,CAAA"}
@@ -0,0 +1,156 @@
1
+ import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
2
+ import type { Ed25519Keypair } from "../pairing/crypto.js";
3
+ import type { SelfRevoke } from "../mesh/self_revoke.js";
4
+ import type { MeshTopologySnapshot } from "../mesh/siblings.js";
5
+ import type { Envelope } from "../session/envelope.js";
6
+ import type { MeshNode } from "../session/mesh_node.js";
7
+ import type { AcquiredLock } from "../session/cwd_lock.js";
8
+ import type { RelayClient } from "../transport/relay_client.js";
9
+ import type { PlainPeerChannel } from "../transport/peer_channel.js";
10
+ import type { ThinkingLevel } from "../protocol/types.js";
11
+ /** App↔Pi room meta projected by the ROOT session (see index.ts `_myRoomMeta`). */
12
+ export type RoomMeta = {
13
+ name: string;
14
+ cwd: string;
15
+ model?: string;
16
+ thinking?: ThinkingLevel;
17
+ working?: boolean;
18
+ sessionId?: string;
19
+ };
20
+ /**
21
+ * The seam between index.ts (composition root) and the `/unbien` command
22
+ * modules.
23
+ *
24
+ * index.ts owns every piece of mutable module state and every lifecycle
25
+ * helper the command handlers touch. Command modules MUST NOT import from
26
+ * `../index.js` (that would be a circular import); instead index.ts builds
27
+ * one `CommandDeps` object (accessor closures over its module state plus
28
+ * direct function references) and passes it to `registerUnbienCommands`,
29
+ * which threads it into every handler that needs it.
30
+ *
31
+ * Members are added exactly as the moved code requires them — nothing
32
+ * speculative. Mutable state that commands write is a get/set pair; state
33
+ * they only read is `readonly`.
34
+ */
35
+ export interface CommandDeps {
36
+ /** Remote state machine: `idle` → `started` (`paired` is derived). */
37
+ state: "idle" | "started";
38
+ /** Live relay WS connection, null when off. */
39
+ relay: RelayClient | null;
40
+ /** URL used by the current relay connection (http(s):// canonical form). */
41
+ relayUrl: string | null;
42
+ /** Local UDS mesh node, null when not joined. */
43
+ meshNode: MeshNode | null;
44
+ /** Mesh session name (LOCAL_SESSION_NAME once joined). */
45
+ sessionName: string | null;
46
+ /** Authoritative local-mesh peer count (broker `list_peers`). */
47
+ sessionPeerCount: number;
48
+ /** Cached Ed25519 identity of this connect cycle. */
49
+ cachedEd25519: Ed25519Keypair | null;
50
+ /** Last-known model name (root session projection). */
51
+ currentModel: string | undefined;
52
+ /** Last-known thinking level (root session projection). */
53
+ currentThinking: ThinkingLevel | undefined;
54
+ /** THE App↔Pi room id for the current chat session. */
55
+ myRoomId: string | null;
56
+ /** Room meta hello payload persisted for reconnect replay. */
57
+ myRoomMeta: RoomMeta | null;
58
+ /** Shortid of the most recently attached peer (UX hint only). */
59
+ peerShort: string;
60
+ /** Epoch of this Pi process's session (stamped on first start). */
61
+ sessionStartedAt: number | null;
62
+ /** Per-(cwd,name) singleton lock backing the mesh registration. */
63
+ cwdLock: AcquiredLock | null;
64
+ /** Name the cwd-lock actually reserved (`name` or `name#N`). */
65
+ lockedName: string | null;
66
+ /** Relay candidate generation (invalidates in-flight starts/stops). */
67
+ relayLifecycleGeneration: number;
68
+ /** True when a relay start was deferred because no session id existed yet
69
+ * (design 01M1CAW0); the root session_start re-runs the start once the id
70
+ * is available. */
71
+ relayStartDeferred: boolean;
72
+ /** Root replacement authority epoch (session replacements). */
73
+ rootLifecycleGeneration: number;
74
+ /** Mesh join candidate generation. */
75
+ meshJoinGeneration: number;
76
+ /** SelfRevoke topology producer for the relay path. */
77
+ selfRevoke: SelfRevoke | null;
78
+ /** Epoch guarding the SelfRevoke producer identity. */
79
+ selfRevokeEpoch: number;
80
+ /** Producer epoch that last published a verified topology. */
81
+ selfRevokeTopologyReadyEpoch: number;
82
+ /** Last topology snapshot published by the producer. */
83
+ selfRevokeTopology: MeshTopologySnapshot | null;
84
+ /** Auto-listener dispose fn for the current relay connection. */
85
+ stopAutoListener: (() => void) | null;
86
+ /** Cached "any device is paired machine-wide" flag (peers.json). */
87
+ readonly hasGlobalPairings: boolean;
88
+ /** Connected owner channels, keyed by canonical owner pubkey. */
89
+ readonly activePeers: Map<string, PlainPeerChannel>;
90
+ /** Set by session_shutdown; blocks outgoing candidates until rearm. */
91
+ readonly disposed: boolean;
92
+ /** Always-fresh session_start ctx (model/registry fallback). */
93
+ readonly lastEventCtx: Pick<ExtensionContext, "compact" | "abort" | "ui"> | null;
94
+ /** The ROOT session's ExtensionAPI (never a subagent child's). */
95
+ readonly pi: ExtensionAPI | null;
96
+ /** The root session's pi sessionId, null pre-sessionManager. */
97
+ readonly rootSessionId: string | null;
98
+ /** True while `generation` is the current root lifecycle epoch. */
99
+ isCurrentRootLifecycle(generation: number): boolean;
100
+ /** Friendly (configured or derived) agent name for a cwd. */
101
+ displayName(cwd: string): string;
102
+ /** Room id for the current chat session, derived from the stable pi
103
+ * session id — or null when no session id exists yet, in which case the
104
+ * caller must NOT announce/join a room (design 01M1CAW0; the cwd-derived
105
+ * fallback is retired). */
106
+ deriveRoomId(cwd: string, name: string): string | null;
107
+ /** Friendly model name for room_meta. */
108
+ currentModelName(): string | undefined;
109
+ /** The ROOT session's state record (turn/agentRun/sessionManager). */
110
+ rootState(): {
111
+ sessionManager?: {
112
+ getSessionId(): string;
113
+ } | null;
114
+ };
115
+ /** Full teardown of relay-side state (relay off, bridge detached). */
116
+ goIdle(): void;
117
+ /** Reconnect backoff entry for a closed relay. */
118
+ onRelayClose(closedRelay: RelayClient): void;
119
+ /** Attach the pair_request auto-listener to a live relay. */
120
+ installAutoListener(relay: RelayClient): () => void;
121
+ /** Repaint the footer status line from current state. */
122
+ refreshFooter(ctx?: {
123
+ ui?: {
124
+ setStatus?: unknown;
125
+ setTitle?: unknown;
126
+ };
127
+ } | null): void;
128
+ /** Tear down an owner's live channel + report the revocation. */
129
+ revokeActiveOwnerRuntime(canonicalOwnerPubkey: string): void;
130
+ /** Attach the cross-PC bridge once relay + leader are both up. */
131
+ attachBridgeIfReady(): void;
132
+ /** Broadcast relay connectivity state to RPC clients. */
133
+ emitRelayState(force?: boolean): void;
134
+ /** Re-query the broker for the authoritative local peer count. */
135
+ refreshSessionPeerCount(peer: MeshNode, ctx?: Pick<ExtensionContext, "ui"> | null): void;
136
+ /** Queue an inbound mesh message for agent delivery. */
137
+ deliverMeshMessageToAgent(env: Envelope): void;
138
+ /** Reads peers.json and refreshes the global-pairings cache + footer. */
139
+ refreshPairingsCache(): void;
140
+ /** Tear down a connected owner's channel (revoke / disconnect). */
141
+ detachPeerChannel(appPeerId: string): void;
142
+ /** RPC control channel verbs (`relay:toggle` / `relay:on` / `relay:off`). */
143
+ handleControl(cmd: string): Promise<void>;
144
+ /** Relay connectivity label for status output. */
145
+ relayStatus(): "connected" | "reconnecting" | "disconnected";
146
+ /** Test-visible state: `idle` | `started` | derived `paired`. */
147
+ getState(): "idle" | "started" | "paired";
148
+ /** Hidden dev-only e2e UI harness (`/unbien test <scenario>`). */
149
+ runTestScenario(scenario: string): string;
150
+ /** ctx.ui.notify that never throws (stale-ctx safe). */
151
+ safeNotify(message: string, level?: "info" | "warning" | "error", preferred?: {
152
+ ui?: unknown;
153
+ } | null): void;
154
+ /** Rename this agent (mesh + relay room) — `/unbien rename`. */
155
+ renameAgent(newName: string): Promise<void>;
156
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=deps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deps.js","sourceRoot":"","sources":["../../src/commands/deps.ts"],"names":[],"mappings":""}
@@ -0,0 +1,4 @@
1
+ /** Record that `sessionId` was born from the app fork request `req`. Best-effort. */
2
+ export declare function writeForkLink(sessionDir: string, sessionId: string, req: string): void;
3
+ /** Read + delete the fork request id for `sessionId`, or undefined when none. */
4
+ export declare function takeForkLink(sessionDir: string, sessionId: string): string | undefined;
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Fork-born linkage: a tiny marker persisted by `_cmdFork`'s `withSession`
3
+ * (running in the NEW forked session) and consumed by that session's FIRST
4
+ * `session_sync_end`, which echoes the app's originating fork request id as
5
+ * `forked_from_req` so the app can auto-navigate to the new session.
6
+ *
7
+ * Why a FILE and not a module variable: `ctx.fork` replaces the AgentSession,
8
+ * and the pi TUI host re-evaluates this extension module fresh for the new
9
+ * session (jiti moduleCache:false), so a module-level stash set in withSession
10
+ * would be lost before the new session handles the app's session_sync. The
11
+ * marker is keyed by the new session id and lives in the session dir, so it
12
+ * survives the re-eval (and a process restart) and is unambiguous.
13
+ *
14
+ * Consumed ONCE (take = read + unlink): the app navigates on the first sync;
15
+ * leaving it would re-fire the jump on every reconnect.
16
+ */
17
+ import { existsSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
18
+ import { join } from "node:path";
19
+ function _linkPath(sessionDir, sessionId) {
20
+ return join(sessionDir, `.unbien-fork-${sessionId}.json`);
21
+ }
22
+ /** Record that `sessionId` was born from the app fork request `req`. Best-effort. */
23
+ export function writeForkLink(sessionDir, sessionId, req) {
24
+ try {
25
+ writeFileSync(_linkPath(sessionDir, sessionId), JSON.stringify({ req, ts: Date.now() }));
26
+ }
27
+ catch {
28
+ /* best-effort — a missing link just means no auto-nav */
29
+ }
30
+ }
31
+ /** Read + delete the fork request id for `sessionId`, or undefined when none. */
32
+ export function takeForkLink(sessionDir, sessionId) {
33
+ const path = _linkPath(sessionDir, sessionId);
34
+ try {
35
+ if (!existsSync(path))
36
+ return undefined;
37
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
38
+ unlinkSync(path);
39
+ return typeof parsed.req === "string" ? parsed.req : undefined;
40
+ }
41
+ catch {
42
+ return undefined;
43
+ }
44
+ }
45
+ //# sourceMappingURL=fork_link.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fork_link.js","sourceRoot":"","sources":["../../src/commands/fork_link.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAC7E,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAEhC,SAAS,SAAS,CAAC,UAAkB,EAAE,SAAiB;IACtD,OAAO,IAAI,CAAC,UAAU,EAAE,gBAAgB,SAAS,OAAO,CAAC,CAAA;AAC3D,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,aAAa,CAC3B,UAAkB,EAClB,SAAiB,EACjB,GAAW;IAEX,IAAI,CAAC;QACH,aAAa,CACX,SAAS,CAAC,UAAU,EAAE,SAAS,CAAC,EAChC,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CACxC,CAAA;IACH,CAAC;IAAC,MAAM,CAAC;QACP,yDAAyD;IAC3D,CAAC;AACH,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,YAAY,CAC1B,UAAkB,EAClB,SAAiB;IAEjB,MAAM,IAAI,GAAG,SAAS,CAAC,UAAU,EAAE,SAAS,CAAC,CAAA;IAC7C,IAAI,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,OAAO,SAAS,CAAA;QACvC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAsB,CAAA;QAC1E,UAAU,CAAC,IAAI,CAAC,CAAA;QAChB,OAAO,OAAO,MAAM,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAA;IAChE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * `/unbien` housekeeping commands: service install/uninstall, the
3
+ * agent-network skill deploy, extension-dir/skill-path resolution, and the
4
+ * `unbien claude` CLI launcher.
5
+ *
6
+ * None of these touch index.ts module state, so they take no CommandDeps —
7
+ * everything they use is imported from pi-independent modules. Carved out
8
+ * of index.ts (phase 1 of the index.ts carve-up).
9
+ *
10
+ * NOTE: this file lives one directory deeper than index.ts used to, so the
11
+ * import.meta.url-based path math (extension dir, packaged skill, mesh
12
+ * server) climbs one extra level — behavior is otherwise identical.
13
+ */
14
+ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
15
+ /**
16
+ * `linkCli` controls whether we symlink `un-bien` into `~/.local/bin/`. The
17
+ * slash-command path passes `true` (user is inside Pi's TUI — they installed
18
+ * via `pi install npm:un-bien` and need us to expose the CLI for them). The
19
+ * standalone-CLI path passes `false` because the user is already running our
20
+ * binary from PATH (they did `npm install -g un-bien`), so re-linking would
21
+ * point their `un-bien` at the Pi-extension copy and diverge on upgrades.
22
+ */
23
+ /** Returns true on success, false when install failed (so the standalone CLI
24
+ * can exit non-zero — e.g. the Cockpit / CI detect failure by exit code).
25
+ * We do NOT process.exit here: this also runs inside the Pi TUI, where exiting
26
+ * would kill the session. */
27
+ export declare function _cmdInstall(ctx: Pick<ExtensionContext, "ui">, opts?: {
28
+ linkCli?: boolean;
29
+ }): boolean;
30
+ export declare function _cmdUninstall(ctx: Pick<ExtensionContext, "ui">, opts?: {
31
+ linkCli?: boolean;
32
+ }): void;
33
+ export declare function _deployAgentNetworkSkill(): void;
34
+ export declare function _cmdClaudeCli(args: string[]): Promise<void>;