@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 +74 -74
- package/dist/config.d.ts +13 -0
- package/dist/config.js.map +1 -1
- package/dist/index.js +36 -2
- package/dist/index.js.map +1 -1
- package/dist/pairing/crypto.d.ts +4 -0
- package/dist/pairing/crypto.js +10 -0
- package/dist/pairing/crypto.js.map +1 -1
- package/dist/pairing/storage.d.ts +51 -26
- package/dist/pairing/storage.js +346 -136
- package/dist/pairing/storage.js.map +1 -1
- package/package.json +1 -1
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/
|
|
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">
|
|
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 [`
|
|
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
|
-
|
|
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
|
|
107
|
-
the rest are
|
|
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 [`
|
|
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
|
|
138
|
-
|
|
|
139
|
-
| **Compact context** | Runs `ctx.compact()` — same as `/compact` in the TUI.
|
|
140
|
-
| **New session**
|
|
141
|
-
| **Model**
|
|
142
|
-
| **Thinking**
|
|
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 [`
|
|
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
|
|
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
|
|
214
|
-
| Returning user, auto-start disabled
|
|
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
|
|
402
|
-
|
|
|
403
|
-
| `/remote-pi`
|
|
404
|
-
| `/remote-pi peers`
|
|
405
|
-
| `/remote-pi rename <new>` | Rename this agent in the current session
|
|
406
|
-
| `/remote-pi stop`
|
|
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
|
|
419
|
-
|
|
|
420
|
-
| `/remote-pi`
|
|
421
|
-
| `/remote-pi setup`
|
|
422
|
-
| `/remote-pi status`
|
|
423
|
-
| `/remote-pi stop`
|
|
424
|
-
| `/remote-pi pair`
|
|
425
|
-
| `/remote-pi devices`
|
|
426
|
-
| `/remote-pi revoke <shortid>`
|
|
427
|
-
| `/remote-pi set-relay <url>`
|
|
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>`
|
|
430
|
-
| `/remote-pi config`
|
|
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
|
|
435
|
-
|
|
|
436
|
-
| `/remote-pi create <cwd> [--name X]`
|
|
437
|
-
| `/remote-pi remove <id>`
|
|
438
|
-
| `/remote-pi daemons`
|
|
439
|
-
| `/remote-pi daemon start`
|
|
440
|
-
| `/remote-pi daemon stop`
|
|
441
|
-
| `/remote-pi daemon restart`
|
|
442
|
-
| `/remote-pi daemon status`
|
|
443
|
-
| `/remote-pi daemon send <id> "<text>"`
|
|
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`
|
|
446
|
-
| `/remote-pi cron run <jobId>`
|
|
447
|
-
| `/remote-pi cron enable\|disable <jobId>`
|
|
448
|
-
| `/remote-pi cron remove <jobId>`
|
|
449
|
-
| `/remote-pi cron log [<jobId>] [--tail N]`
|
|
450
|
-
| `/remote-pi install`
|
|
451
|
-
| `/remote-pi uninstall`
|
|
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
|
|
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
|
|
577
|
-
| macOS
|
|
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
|
|
602
|
-
|
|
|
603
|
-
| `<cwd>/.pi/remote-pi/config.json`
|
|
604
|
-
| `~/.pi/remote/config.json`
|
|
605
|
-
| `~/.pi/remote/peers.json`
|
|
606
|
-
| `~/.pi/remote/sessions/<name>/`
|
|
607
|
-
| `~/.pi/remote/skills/agent-network/SKILL.md` | Per-user
|
|
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
|
|
614
|
-
|
|
|
615
|
-
| `REMOTE_PI_DIR`
|
|
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
|
|
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
|
|
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;
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C,6EAA6E;AAC7E,6EAA6E;AAC7E,gBAAgB;AAChB,MAAM,SAAS,GAAG,GAAW,EAAE,CAAC,gBAAgB,EAAE,CAAC;AACnD,MAAM,UAAU,GAAG,GAAW,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,EAAE,cAAc,CAAC,CAAC;
|
|
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
|
-
|
|
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
|
*
|