@geohar/un-bien 0.7.0 → 0.8.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/README.md CHANGED
@@ -4,10 +4,10 @@
4
4
  > below still carries upstream branding and is being rebranded to un-bien.
5
5
 
6
6
  <p align="center">
7
- <img src="https://raw.githubusercontent.com/jacobaraujo7/remote_pi/main/branding/logo-full.svg" width="160" alt="Remote Pi logo" />
7
+ <img src="https://raw.githubusercontent.com/georgeharker/un-bien/main/app/icons/un-bien-macos-1024.png" width="160" alt="un-bien logo" />
8
8
  </p>
9
9
 
10
- <h1 align="center">Remote Pi</h1>
10
+ <h1 align="center">un-bien</h1>
11
11
 
12
12
  > Extend the [Pi coding agent](https://github.com/earendil-works/pi) with two
13
13
  > superpowers: agents that talk to each other on the same machine, and a mobile
@@ -22,7 +22,7 @@ first time it asks a couple of questions and you are done.
22
22
 
23
23
  For wire format, identity model, ACK protocol, cross-PC routing, mesh
24
24
  membership, and the trust model (what the relay sees and doesn't see),
25
- read [`PROTOCOL.md`](../PROTOCOL.md) at the repo root. It is the canonical
25
+ read [`rpc-envelope`](../docs/rpc-envelope.md) at the repo root. It is the canonical
26
26
  document — this README only covers user-facing setup.
27
27
 
28
28
  ---
@@ -85,7 +85,7 @@ both:
85
85
 
86
86
  Several Pi instances running side-by-side in different terminals can discover
87
87
  each other and exchange messages. Each instance is a peer in a named
88
- *session*. The LLM uses:
88
+ _session_. The LLM uses:
89
89
 
90
90
  - `list_peers` — discover current peer routing addresses
91
91
  - `agent_send` — unicast waits for the broker delivery ACK; broadcast is
@@ -103,8 +103,8 @@ addresses over the relay; local-only use stays on UDS when relay access is off.
103
103
  Useful for splitting work across roles (`backend`, `frontend`, `tests`,
104
104
  `orchestrator`, …) and letting them coordinate.
105
105
 
106
- The first agent to enter a session becomes the *leader* (hosts the broker);
107
- the rest are *followers*. If the leader exits, a follower automatically takes
106
+ The first agent to enter a session becomes the _leader_ (hosts the broker);
107
+ the rest are _followers_. If the leader exits, a follower automatically takes
108
108
  over — the failover is invisible to the LLMs.
109
109
 
110
110
  ### 2) Mobile app (over the relay)
@@ -118,7 +118,7 @@ Communication uses WebSocket over TLS to the relay. Fields such as `ct` are
118
118
  wire containers, not a systemwide end-to-end confidentiality guarantee: current
119
119
  Pi-forward, cross-PC, app, and control envelopes visible to the relay are not
120
120
  fully opaque or E2E encrypted. A relay operator can see routed plaintext
121
- protocol content and metadata; see [`PROTOCOL.md`](../PROTOCOL.md) for the exact
121
+ protocol content and metadata; see [`rpc-envelope`](../docs/rpc-envelope.md) for the exact
122
122
  trust boundaries.
123
123
 
124
124
  **Get the app** — all current download options (Google Play, App Store, and
@@ -134,17 +134,17 @@ Beyond the chat, the app surfaces a small set of typed actions you can run
134
134
  on the paired Pi session. Tap the ⚙ button next to the message input (visible
135
135
  when the input is empty) to open the Quick Actions sheet:
136
136
 
137
- | Action | What it does |
138
- | --- | --- |
139
- | **Compact context** | Runs `ctx.compact()` — same as `/compact` in the TUI. |
140
- | **New session** | Runs `ctx.newSession()` — equivalent to `/new`, asks for confirmation first. |
141
- | **Model** | Opens a model picker fed by your authenticated providers (same source the TUI uses) and switches via `pi.setModel(model)`. |
142
- | **Thinking** | Segmented control with the 6 SDK levels (`off` · `minimal` · `low` · `medium` · `high` · `xhigh`). Changes via `pi.setThinkingLevel(level)`. |
137
+ | Action | What it does |
138
+ | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
139
+ | **Compact context** | Runs `ctx.compact()` — same as `/compact` in the TUI. |
140
+ | **New session** | Runs `ctx.newSession()` — equivalent to `/new`, asks for confirmation first. |
141
+ | **Model** | Opens a model picker fed by your authenticated providers (same source the TUI uses) and switches via `pi.setModel(model)`. |
142
+ | **Thinking** | Segmented control with the 6 SDK levels (`off` · `minimal` · `low` · `medium` · `high` · `xhigh`). Changes via `pi.setThinkingLevel(level)`. |
143
143
 
144
144
  Each action gets a structured `action_ok` / `action_error` reply so the app
145
145
  can show a SnackBar on failure. Visible side-effects (chat output, model
146
146
  change broadcasts, compaction notice) still flow through the normal chat
147
- channels. The wire schema is documented in [`PROTOCOL.md`](../PROTOCOL.md)
147
+ channels. The wire schema is documented in [`rpc-envelope`](../docs/rpc-envelope.md)
148
148
  under "App actions".
149
149
 
150
150
  It is **not** a generic slash-command picker. The Pi SDK does not expose
@@ -207,11 +207,11 @@ The bare command is the everyday entry point:
207
207
 
208
208
  Behavior depends on whether there's a local config for this directory:
209
209
 
210
- | State | What happens |
211
- | --- | --- |
210
+ | State | What happens |
211
+ | ------------------------------------------ | ---------------------------------------------------------------------------------------- |
212
212
  | First run (no `.pi/remote-pi/config.json`) | Interactive wizard → saves config → joins agent session → starts relay (if you opted in) |
213
- | Returning user, auto-start enabled | Joins agent session + starts relay automatically, then prints status |
214
- | Returning user, auto-start disabled | Prints status only; join/relay must be run manually |
213
+ | Returning user, auto-start enabled | Joins agent session + starts relay automatically, then prints status |
214
+ | Returning user, auto-start disabled | Prints status only; join/relay must be run manually |
215
215
 
216
216
  The wizard asks three questions:
217
217
 
@@ -398,12 +398,12 @@ log at `~/.pi/remote/sessions/<name>/audit.jsonl` for postmortem inspection.
398
398
 
399
399
  Useful commands:
400
400
 
401
- | Command | What it does |
402
- | --- | --- |
403
- | `/remote-pi` | Join the local mesh (and start the relay, if enabled) |
404
- | `/remote-pi peers` | List local + cross-PC mesh peers, grouped by PC |
405
- | `/remote-pi rename <new>` | Rename this agent in the current session |
406
- | `/remote-pi stop` | Leave the local mesh and disconnect the relay |
401
+ | Command | What it does |
402
+ | ------------------------- | ----------------------------------------------------- |
403
+ | `/remote-pi` | Join the local mesh (and start the relay, if enabled) |
404
+ | `/remote-pi peers` | List local + cross-PC mesh peers, grouped by PC |
405
+ | `/remote-pi rename <new>` | Rename this agent in the current session |
406
+ | `/remote-pi stop` | Leave the local mesh and disconnect the relay |
407
407
 
408
408
  Name collisions inside a session get a numeric suffix automatically
409
409
  (`backend`, `backend#2`, `backend#3`). The broker assigns it and returns the
@@ -415,40 +415,40 @@ real name to the peer.
415
415
 
416
416
  ### Local session (one Pi, one terminal)
417
417
 
418
- | Command | Description |
419
- | --- | --- |
420
- | `/remote-pi` | Connect (join local mesh + start relay), or run setup on first use |
421
- | `/remote-pi setup` | Run the setup wizard and update local config |
422
- | `/remote-pi status` | Show local mesh + relay status |
423
- | `/remote-pi stop` | Stop everything for **this** terminal (mesh + relay) |
424
- | `/remote-pi pair` | Show QR code + copy-paste pairing URI for a new mobile device |
425
- | `/remote-pi devices` | List paired mobile devices (online/offline per device) |
426
- | `/remote-pi revoke <shortid>` | Revoke a paired device by its shortid |
427
- | `/remote-pi set-relay <url>` | Persist a new relay URL (http:// or https://) |
418
+ | Command | Description |
419
+ | ---------------------------------------- | ------------------------------------------------------------------------------ |
420
+ | `/remote-pi` | Connect (join local mesh + start relay), or run setup on first use |
421
+ | `/remote-pi setup` | Run the setup wizard and update local config |
422
+ | `/remote-pi status` | Show local mesh + relay status |
423
+ | `/remote-pi stop` | Stop everything for **this** terminal (mesh + relay) |
424
+ | `/remote-pi pair` | Show QR code + copy-paste pairing URI for a new mobile device |
425
+ | `/remote-pi devices` | List paired mobile devices (online/offline per device) |
426
+ | `/remote-pi revoke <shortid>` | Revoke a paired device by its shortid |
427
+ | `/remote-pi set-relay <url>` | Persist a new relay URL (http:// or https://) |
428
428
  | `/remote-pi relay [start\|stop\|status]` | Relay-only control — leaves local mesh membership untouched (no verb = toggle) |
429
- | `/remote-pi relay url <url>` | Same as `set-relay` |
430
- | `/remote-pi config` | Show the effective relay URL and its source (env / config / default) |
429
+ | `/remote-pi relay url <url>` | Same as `set-relay` |
430
+ | `/remote-pi config` | Show the effective relay URL and its source (env / config / default) |
431
431
 
432
432
  ### Daemon fleet (one supervisor, N background Pis — see [Daemon mode](#daemon-mode))
433
433
 
434
- | Command | Description |
435
- | --- | --- |
436
- | `/remote-pi create <cwd> [--name X]` | Register a folder as a daemon |
437
- | `/remote-pi remove <id>` | Unregister a daemon (local config preserved) |
438
- | `/remote-pi daemons` | List registered daemons + state |
439
- | `/remote-pi daemon start` | Start every registered daemon |
440
- | `/remote-pi daemon stop` | Stop every running daemon (`/remote-pi stop` stops only the local terminal) |
441
- | `/remote-pi daemon restart` | Stop + start all daemons |
442
- | `/remote-pi daemon status` | Detailed runtime status (pid, uptime, restart count) |
443
- | `/remote-pi daemon send <id> "<text>"` | Send a prompt to a specific daemon |
434
+ | Command | Description |
435
+ | ---------------------------------------------- | ----------------------------------------------------------------------------- |
436
+ | `/remote-pi create <cwd> [--name X]` | Register a folder as a daemon |
437
+ | `/remote-pi remove <id>` | Unregister a daemon (local config preserved) |
438
+ | `/remote-pi daemons` | List registered daemons + state |
439
+ | `/remote-pi daemon start` | Start every registered daemon |
440
+ | `/remote-pi daemon stop` | Stop every running daemon (`/remote-pi stop` stops only the local terminal) |
441
+ | `/remote-pi daemon restart` | Stop + start all daemons |
442
+ | `/remote-pi daemon status` | Detailed runtime status (pid, uptime, restart count) |
443
+ | `/remote-pi daemon send <id> "<text>"` | Send a prompt to a specific daemon |
444
444
  | `/remote-pi cron add <id> "<expr>" "<prompt>"` | Schedule a recurring prompt (`--tz`, `--wake`, `--no-skip-busy`, `--catchup`) |
445
- | `/remote-pi cron list` | List scheduled jobs (schedule, enabled, next run, last status) |
446
- | `/remote-pi cron run <jobId>` | Fire a job now (ignores its schedule) |
447
- | `/remote-pi cron enable\|disable <jobId>` | Toggle a job on/off |
448
- | `/remote-pi cron remove <jobId>` | Delete a job |
449
- | `/remote-pi cron log [<jobId>] [--tail N]` | Read the fire/skip audit log |
450
- | `/remote-pi install` | Install `pi-supervisord` as a system service |
451
- | `/remote-pi uninstall` | Remove the system service (registry preserved) |
445
+ | `/remote-pi cron list` | List scheduled jobs (schedule, enabled, next run, last status) |
446
+ | `/remote-pi cron run <jobId>` | Fire a job now (ignores its schedule) |
447
+ | `/remote-pi cron enable\|disable <jobId>` | Toggle a job on/off |
448
+ | `/remote-pi cron remove <jobId>` | Delete a job |
449
+ | `/remote-pi cron log [<jobId>] [--tail N]` | Read the fire/skip audit log |
450
+ | `/remote-pi install` | Install `pi-supervisord` as a system service |
451
+ | `/remote-pi uninstall` | Remove the system service (registry preserved) |
452
452
 
453
453
  All commands above work both as Pi slash commands (interactive) and as
454
454
  shell-level `remote-pi <subcommand>` when the package is installed
@@ -460,7 +460,7 @@ globally (`npm install -g remote-pi`).
460
460
  supervisor — e.g. a daily "summarise new PRs". Output flows fire-and-forget to
461
461
  the mesh/app like any prompt; the cron layer only audits the dispatch.
462
462
 
463
- - **Schedule** is a cron expression (croner syntax; an optional 6th *seconds*
463
+ - **Schedule** is a cron expression (croner syntax; an optional 6th _seconds_
464
464
  field is supported), with an optional IANA timezone via `--tz`:
465
465
 
466
466
  ```sh
@@ -571,10 +571,10 @@ registered daemon back. To wipe the registry entirely, `rm
571
571
 
572
572
  ### Where to find logs
573
573
 
574
- | Platform | Command |
575
- | --- | --- |
576
- | Linux | `journalctl --user -u remote-pi-supervisord -f` |
577
- | macOS | `tail -f ~/.pi/remote/supervisord.log` |
574
+ | Platform | Command |
575
+ | -------- | ----------------------------------------------- |
576
+ | Linux | `journalctl --user -u remote-pi-supervisord -f` |
577
+ | macOS | `tail -f ~/.pi/remote/supervisord.log` |
578
578
 
579
579
  Each spawned daemon's stderr is forwarded into the supervisor's log
580
580
  with a `[<cwd>]` prefix, so a single log stream shows every agent.
@@ -598,22 +598,22 @@ with a `[<cwd>]` prefix, so a single log stream shows every agent.
598
598
 
599
599
  ## Configuration files
600
600
 
601
- | Path | Scope | What's in it |
602
- | --- | --- | --- |
603
- | `<cwd>/.pi/remote-pi/config.json` | Per-directory | `agent_name`, `session_name`, `auto_start_relay` |
604
- | `~/.pi/remote/config.json` | Per-user | `relay` URL; optional `defaults.auto_start_relay` (machine-wide fallback for per-directory configs) |
605
- | `~/.pi/remote/peers.json` | Per-machine | Paired mobile devices |
606
- | `~/.pi/remote/sessions/<name>/` | Per-session | Broker socket + `audit.jsonl` |
607
- | `~/.pi/remote/skills/agent-network/SKILL.md` | Per-user | Agent skill the LLM reads |
601
+ | Path | Scope | What's in it |
602
+ | -------------------------------------------- | ------------- | --------------------------------------------------------------------------------------------------- |
603
+ | `<cwd>/.pi/remote-pi/config.json` | Per-directory | `agent_name`, `session_name`, `auto_start_relay` |
604
+ | `~/.pi/remote/config.json` | Per-user | `relay` URL; optional `defaults.auto_start_relay` (machine-wide fallback for per-directory configs) |
605
+ | `~/.pi/remote/peers.json` | Per-machine | Paired mobile devices |
606
+ | `~/.pi/remote/sessions/<name>/` | Per-session | Broker socket + `audit.jsonl` |
607
+ | `~/.pi/remote/skills/agent-network/SKILL.md` | Per-user | Agent skill the LLM reads |
608
608
 
609
609
  Every path above **except the global `config.json`** derives from a single
610
610
  **state root** — by default `~/.pi/remote`. Two environment variables relocate
611
611
  that state root:
612
612
 
613
- | Variable | Behaviour |
614
- | --- | --- |
615
- | `REMOTE_PI_DIR` | Absolute path to the state root. No suffix appended — set this to an XDG-style location like `~/.config/pi/remote-pi`. Takes priority over every other variable. |
616
- | `REMOTE_PI_HOME` | Stand-in for `$HOME`; state lives at `<REMOTE_PI_HOME>/.pi/remote`. Kept for backward compatibility. |
613
+ | Variable | Behaviour |
614
+ | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
615
+ | `REMOTE_PI_DIR` | Absolute path to the state root. No suffix appended — set this to an XDG-style location like `~/.config/pi/remote-pi`. Takes priority over every other variable. |
616
+ | `REMOTE_PI_HOME` | Stand-in for `$HOME`; state lives at `<REMOTE_PI_HOME>/.pi/remote`. Kept for backward compatibility. |
617
617
 
618
618
  When both are set, `REMOTE_PI_DIR` wins. Use these to put remote-pi **state**
619
619
  (sessions, daemon registries, cwd locks, paired identity) on a specific disk,
@@ -622,8 +622,8 @@ inside an XDG directory, or anywhere your system's conventions dictate.
622
622
  The global `config.json` (relay URL + defaults) is resolved separately, so it
623
623
  can sit beside the coding agent's own settings rather than the state tree:
624
624
 
625
- | Variable | Behaviour |
626
- |---|---|
625
+ | Variable | Behaviour |
626
+ | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
627
627
  | `PI_CODING_AGENT_DIR` | The Pi host's settings root (default `~/.pi`). `config.json` lives at `<PI_CODING_AGENT_DIR>/remote/config.json`, so with the default agent dir the path stays exactly `~/.pi/remote/config.json`. Takes priority for config. |
628
628
 
629
629
  When `PI_CODING_AGENT_DIR` is unset, `config.json` falls back to the state root
@@ -638,8 +638,8 @@ as a fallback for every per-directory config that doesn't set the field:
638
638
  {
639
639
  "relay": "https://relay.yourdomain.tld",
640
640
  "defaults": {
641
- "auto_start_relay": true
642
- }
641
+ "auto_start_relay": true,
642
+ },
643
643
  }
644
644
  ```
645
645
 
@@ -662,7 +662,7 @@ REMOTE_PI_RELAY=https://staging.example.tld pi
662
662
  ## Troubleshooting
663
663
 
664
664
  **Footer says `🟡 relay waiting for pairing` even though I paired a device.**
665
- The icon reflects whether *any* device has been paired on this machine, not
665
+ The icon reflects whether _any_ device has been paired on this machine, not
666
666
  whether one is connected right now. If you really have a paired device in
667
667
  `/remote-pi devices`, restart Pi — the cache may be stale (fixed in current
668
668
  release; report a bug if it recurs).
package/dist/config.d.ts CHANGED
@@ -22,6 +22,19 @@ export type UnBienConfig = {
22
22
  defaults?: {
23
23
  auto_start_relay?: boolean;
24
24
  };
25
+ /**
26
+ * Machine-identity storage. `storage` selects the PRIMARY backend for this
27
+ * Pi's long-term Ed25519 seed — `"keychain"` (OS-secured, the default) or
28
+ * `"file"` (a 0600 seed file, the SSH-private-key model: cat-able, portable,
29
+ * works headless). `path` overrides the file-backend location (default
30
+ * `~/.pi/un-bien/identity.json`). The unselected backend is still READ (to
31
+ * recover an existing identity) but never written; the resolver mints only on
32
+ * a genuine first run. See `pairing/storage.ts`. Absent ⇒ keychain default.
33
+ */
34
+ identity?: {
35
+ storage?: "keychain" | "file";
36
+ path?: string;
37
+ };
25
38
  };
26
39
  export declare function loadConfig(): UnBienConfig;
27
40
  export declare function saveConfig(patch: Partial<UnBienConfig>): void;
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -32,7 +32,7 @@
32
32
  import { createHash, randomUUID } from "node:crypto";
33
33
  import { SettingsManager, convertToPng } from "@earendil-works/pi-coding-agent";
34
34
  import { buildQRUri, qrSession, renderQRAscii, clampPairTtlMs, TOKEN_TTL_MS, } from "./pairing/qr.js";
35
- import { addPeer, getOrCreateEd25519Keypair, KeyringUnavailableError, PairedIdentityMissingError, listPeers, removePeer, snapshotOwnerPubkeys, conditionalRemovePeer, } from "./pairing/storage.js";
35
+ import { addPeer, getOrCreateEd25519Keypair, describeIdentity, KeyringUnavailableError, PairedIdentityMissingError, listPeers, removePeer, snapshotOwnerPubkeys, conditionalRemovePeer, } from "./pairing/storage.js";
36
36
  import { MeshClient } from "./mesh/client.js";
37
37
  import { canonicalizeEd25519PublicKey, decodeEd25519PublicKey, publicKeyFingerprint, } from "./mesh/encoding.js";
38
38
  import { SelfRevoke } from "./mesh/self_revoke.js";
@@ -3042,6 +3042,8 @@ const extension = (pi) => {
3042
3042
  "relay status",
3043
3043
  "relay url",
3044
3044
  "config",
3045
+ "identity",
3046
+ "identity show",
3045
3047
  "test", // hidden e2e UI harness (dev-only)
3046
3048
  "peers", // plan/25 Wave D — local + cross-PC inventory
3047
3049
  "create",
@@ -3101,6 +3103,9 @@ const extension = (pi) => {
3101
3103
  else if (sub === "config") {
3102
3104
  _cmdConfig(ctx);
3103
3105
  }
3106
+ else if (sub === "identity" || sub.startsWith("identity ")) {
3107
+ await _cmdIdentity(ctx);
3108
+ }
3104
3109
  else if (sub === "test" || sub.startsWith("test ")) {
3105
3110
  // Hidden dev-only e2e UI harness: broadcast canned frames to paired apps.
3106
3111
  _safeNotify(`[un-bien test] ${_runTestScenario(sub.slice("test".length).trim())}`, "info");
@@ -3217,6 +3222,20 @@ const extension = (pi) => {
3217
3222
  _cmdConfig(ctx);
3218
3223
  },
3219
3224
  });
3225
+ pi.registerCommand("unbien identity", {
3226
+ description: "Show this machine's identity: active EPK (public), backend, and source",
3227
+ handler: async (_, ctx) => {
3228
+ _lastCtx = ctx;
3229
+ await _cmdIdentity(ctx);
3230
+ },
3231
+ });
3232
+ pi.registerCommand("unbien identity show", {
3233
+ description: "Show this machine's identity (EPK/backend/source) — alias of `identity`",
3234
+ handler: async (_, ctx) => {
3235
+ _lastCtx = ctx;
3236
+ await _cmdIdentity(ctx);
3237
+ },
3238
+ });
3220
3239
  pi.registerCommand("unbien relay", {
3221
3240
  description: "Relay control: start | stop | status | url <http(s) url> (no arg toggles)",
3222
3241
  handler: async (args, ctx) => {
@@ -3582,7 +3601,7 @@ async function _cmdStart(ctx) {
3582
3601
  ctx.ui.notify("[un-bien] Could not read this machine's identity: the system " +
3583
3602
  "keychain is locked or access was denied. Unlock it (open the app / " +
3584
3603
  "log in) and run /unbien again. Your pairing is NOT lost. " +
3585
- "(Set UNBIEN_ALLOW_FILE_IDENTITY=1 only for headless hosts.)", "error");
3604
+ '(For a headless host, set "identity": { "storage": "file" } in un-bien.json.)', "error");
3586
3605
  return;
3587
3606
  }
3588
3607
  if (err instanceof PairedIdentityMissingError) {
@@ -4067,6 +4086,21 @@ function _cmdConfig(ctx) {
4067
4086
  : "";
4068
4087
  ctx.ui.notify(`[un-bien]\n Relay URL: ${url ?? "(none)"}\n Source: ${source} — ${origin}${live}`, "info");
4069
4088
  }
4089
+ /**
4090
+ * `/unbien identity` — report NON-SECRET identity state (active EPK, backend,
4091
+ * resolved source). The private seed is NEVER shown: command output is
4092
+ * LLM-visible, so extraction stays a manual `cat`/keychain op. Read-only
4093
+ * (never mints).
4094
+ */
4095
+ async function _cmdIdentity(ctx) {
4096
+ const info = await describeIdentity();
4097
+ const backendLine = info.backend === "file" ? `file (${info.filePath})` : "keychain";
4098
+ const epkLine = info.epk ?? "(none yet — minted on first use)";
4099
+ const sourceLine = info.detail
4100
+ ? `${info.source} — ${info.detail}`
4101
+ : info.source;
4102
+ ctx.ui.notify(`[un-bien] identity\n Backend: ${backendLine}\n EPK (public): ${epkLine}\n Source: ${sourceLine}`, info.source === "error" ? "error" : "info");
4103
+ }
4070
4104
  /**
4071
4105
  * `/unbien relay [start|stop|status|url <url>]` — issue #119.
4072
4106
  *