@geohar/un-bien 0.7.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/LICENSE +21 -0
- package/README.md +726 -0
- package/dist/actions/handlers.d.ts +77 -0
- package/dist/actions/handlers.js +26 -0
- package/dist/actions/handlers.js.map +1 -0
- package/dist/actions/registry.d.ts +39 -0
- package/dist/actions/registry.js +62 -0
- package/dist/actions/registry.js.map +1 -0
- package/dist/bin/supervisord.d.ts +2 -0
- package/dist/bin/supervisord.js +86 -0
- package/dist/bin/supervisord.js.map +1 -0
- package/dist/config.d.ts +84 -0
- package/dist/config.js +115 -0
- package/dist/config.js.map +1 -0
- package/dist/daemon/client.d.ts +20 -0
- package/dist/daemon/client.js +131 -0
- package/dist/daemon/client.js.map +1 -0
- package/dist/daemon/control_protocol.d.ts +176 -0
- package/dist/daemon/control_protocol.js +63 -0
- package/dist/daemon/control_protocol.js.map +1 -0
- package/dist/daemon/cron_log.d.ts +45 -0
- package/dist/daemon/cron_log.js +71 -0
- package/dist/daemon/cron_log.js.map +1 -0
- package/dist/daemon/cron_registry.d.ts +80 -0
- package/dist/daemon/cron_registry.js +198 -0
- package/dist/daemon/cron_registry.js.map +1 -0
- package/dist/daemon/id.d.ts +24 -0
- package/dist/daemon/id.js +36 -0
- package/dist/daemon/id.js.map +1 -0
- package/dist/daemon/install.d.ts +170 -0
- package/dist/daemon/install.js +603 -0
- package/dist/daemon/install.js.map +1 -0
- package/dist/daemon/registry.d.ts +63 -0
- package/dist/daemon/registry.js +153 -0
- package/dist/daemon/registry.js.map +1 -0
- package/dist/daemon/rpc_child.d.ts +194 -0
- package/dist/daemon/rpc_child.js +419 -0
- package/dist/daemon/rpc_child.js.map +1 -0
- package/dist/daemon/supervisor.d.ts +93 -0
- package/dist/daemon/supervisor.js +666 -0
- package/dist/daemon/supervisor.js.map +1 -0
- package/dist/extension_ui_bridge.d.ts +26 -0
- package/dist/extension_ui_bridge.js +341 -0
- package/dist/extension_ui_bridge.js.map +1 -0
- package/dist/index.d.ts +197 -0
- package/dist/index.js +5750 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/mesh_result.d.ts +10 -0
- package/dist/mcp/mesh_result.js +42 -0
- package/dist/mcp/mesh_result.js.map +1 -0
- package/dist/mcp/mesh_server.d.ts +16 -0
- package/dist/mcp/mesh_server.js +373 -0
- package/dist/mcp/mesh_server.js.map +1 -0
- package/dist/mesh/canonical.d.ts +30 -0
- package/dist/mesh/canonical.js +63 -0
- package/dist/mesh/canonical.js.map +1 -0
- package/dist/mesh/client.d.ts +18 -0
- package/dist/mesh/client.js +127 -0
- package/dist/mesh/client.js.map +1 -0
- package/dist/mesh/encoding.d.ts +38 -0
- package/dist/mesh/encoding.js +217 -0
- package/dist/mesh/encoding.js.map +1 -0
- package/dist/mesh/self_revoke.d.ts +73 -0
- package/dist/mesh/self_revoke.js +407 -0
- package/dist/mesh/self_revoke.js.map +1 -0
- package/dist/mesh/siblings.d.ts +49 -0
- package/dist/mesh/siblings.js +159 -0
- package/dist/mesh/siblings.js.map +1 -0
- package/dist/mesh/types.d.ts +34 -0
- package/dist/mesh/types.js +11 -0
- package/dist/mesh/types.js.map +1 -0
- package/dist/mesh/verify.d.ts +17 -0
- package/dist/mesh/verify.js +86 -0
- package/dist/mesh/verify.js.map +1 -0
- package/dist/pairing/crypto.d.ts +8 -0
- package/dist/pairing/crypto.js +22 -0
- package/dist/pairing/crypto.js.map +1 -0
- package/dist/pairing/qr.d.ts +59 -0
- package/dist/pairing/qr.js +127 -0
- package/dist/pairing/qr.js.map +1 -0
- package/dist/pairing/storage.d.ts +120 -0
- package/dist/pairing/storage.js +614 -0
- package/dist/pairing/storage.js.map +1 -0
- package/dist/panel_bridge.d.ts +15 -0
- package/dist/panel_bridge.js +409 -0
- package/dist/panel_bridge.js.map +1 -0
- package/dist/paths.d.ts +39 -0
- package/dist/paths.js +51 -0
- package/dist/paths.js.map +1 -0
- package/dist/protocol/codec.d.ts +7 -0
- package/dist/protocol/codec.js +48 -0
- package/dist/protocol/codec.js.map +1 -0
- package/dist/protocol/types.d.ts +404 -0
- package/dist/protocol/types.js +2 -0
- package/dist/protocol/types.js.map +1 -0
- package/dist/rooms.d.ts +42 -0
- package/dist/rooms.js +78 -0
- package/dist/rooms.js.map +1 -0
- package/dist/session/bridge.d.ts +37 -0
- package/dist/session/bridge.js +134 -0
- package/dist/session/bridge.js.map +1 -0
- package/dist/session/broker.d.ts +189 -0
- package/dist/session/broker.js +527 -0
- package/dist/session/broker.js.map +1 -0
- package/dist/session/broker_remote.d.ts +74 -0
- package/dist/session/broker_remote.js +581 -0
- package/dist/session/broker_remote.js.map +1 -0
- package/dist/session/cwd_lock.d.ts +33 -0
- package/dist/session/cwd_lock.js +127 -0
- package/dist/session/cwd_lock.js.map +1 -0
- package/dist/session/debug_log.d.ts +3 -0
- package/dist/session/debug_log.js +32 -0
- package/dist/session/debug_log.js.map +1 -0
- package/dist/session/envelope.d.ts +33 -0
- package/dist/session/envelope.js +117 -0
- package/dist/session/envelope.js.map +1 -0
- package/dist/session/global_config.d.ts +33 -0
- package/dist/session/global_config.js +73 -0
- package/dist/session/global_config.js.map +1 -0
- package/dist/session/ipc.d.ts +27 -0
- package/dist/session/ipc.js +22 -0
- package/dist/session/ipc.js.map +1 -0
- package/dist/session/leader_election.d.ts +32 -0
- package/dist/session/leader_election.js +108 -0
- package/dist/session/leader_election.js.map +1 -0
- package/dist/session/local_config.d.ts +67 -0
- package/dist/session/local_config.js +193 -0
- package/dist/session/local_config.js.map +1 -0
- package/dist/session/mesh_node.d.ts +116 -0
- package/dist/session/mesh_node.js +596 -0
- package/dist/session/mesh_node.js.map +1 -0
- package/dist/session/peer.d.ts +139 -0
- package/dist/session/peer.js +474 -0
- package/dist/session/peer.js.map +1 -0
- package/dist/session/peer_inventory.d.ts +13 -0
- package/dist/session/peer_inventory.js +48 -0
- package/dist/session/peer_inventory.js.map +1 -0
- package/dist/session/peer_limits.d.ts +9 -0
- package/dist/session/peer_limits.js +24 -0
- package/dist/session/peer_limits.js.map +1 -0
- package/dist/session/rpc_envelope.d.ts +108 -0
- package/dist/session/rpc_envelope.js +221 -0
- package/dist/session/rpc_envelope.js.map +1 -0
- package/dist/session/rpc_inbound.d.ts +55 -0
- package/dist/session/rpc_inbound.js +136 -0
- package/dist/session/rpc_inbound.js.map +1 -0
- package/dist/session/setup_wizard.d.ts +39 -0
- package/dist/session/setup_wizard.js +63 -0
- package/dist/session/setup_wizard.js.map +1 -0
- package/dist/session/tools.d.ts +24 -0
- package/dist/session/tools.js +272 -0
- package/dist/session/tools.js.map +1 -0
- package/dist/session/wizard.d.ts +13 -0
- package/dist/session/wizard.js +20 -0
- package/dist/session/wizard.js.map +1 -0
- package/dist/transport/peer_channel.d.ts +52 -0
- package/dist/transport/peer_channel.js +145 -0
- package/dist/transport/peer_channel.js.map +1 -0
- package/dist/transport/pi_forward_client.d.ts +29 -0
- package/dist/transport/pi_forward_client.js +68 -0
- package/dist/transport/pi_forward_client.js.map +1 -0
- package/dist/transport/relay_client.d.ts +96 -0
- package/dist/transport/relay_client.js +210 -0
- package/dist/transport/relay_client.js.map +1 -0
- package/dist/ui/footer.d.ts +32 -0
- package/dist/ui/footer.js +34 -0
- package/dist/ui/footer.js.map +1 -0
- package/docs/daemon.md +289 -0
- package/package.json +92 -0
- package/service-templates/launchd.plist.template +35 -0
- package/service-templates/systemd.service.template +19 -0
- package/service-templates/task-launcher.vbs.template +10 -0
- package/service-templates/task-scheduler.xml.template +38 -0
- package/skills/agent-network/SKILL.md +310 -0
|
@@ -0,0 +1,614 @@
|
|
|
1
|
+
import { writeFileSync } from "node:fs";
|
|
2
|
+
import { mkdir, readFile, writeFile, chmod, unlink } from "node:fs/promises";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { generateEd25519Keypair } from "./crypto.js";
|
|
5
|
+
import { canonicalizeEd25519PublicKey } from "../mesh/encoding.js";
|
|
6
|
+
import { unbienStateHome } from "../paths.js";
|
|
7
|
+
/**
|
|
8
|
+
* Pi-secret storage (plan/27 Wave E1).
|
|
9
|
+
*
|
|
10
|
+
* The Ed25519 long-term identity of this Pi lives in the platform keyring
|
|
11
|
+
* via `@napi-rs/keyring` (Keychain on macOS, libsecret on Linux desktop,
|
|
12
|
+
* Credential Manager on Windows — DPAPI-backed). When the keyring is
|
|
13
|
+
* unavailable (headless Linux without a D-Bus session, Docker containers,
|
|
14
|
+
* VPS without GNOME Keyring/KWallet running) we fall back to a
|
|
15
|
+
* file-backed store at `~/.pi/un-bien/identity.json` with `0o600`
|
|
16
|
+
* permissions and the parent dir at `0o700`.
|
|
17
|
+
*
|
|
18
|
+
* **Migration**: previous builds used `keytar` against service
|
|
19
|
+
* `dev.unbien.mac`. This module reads from the old service if the new
|
|
20
|
+
* service is empty, copies the entry to the new service `dev.unbien.pi`,
|
|
21
|
+
* and deletes the old one. Both keytar and `@napi-rs/keyring` address the
|
|
22
|
+
* same OS-level credential store on every supported platform, so the read
|
|
23
|
+
* succeeds without keeping the deprecated `keytar` dependency.
|
|
24
|
+
*/
|
|
25
|
+
const NEW_SERVICE = "dev.unbien.pi"; // platform-neutral
|
|
26
|
+
const OLD_SERVICE = "dev.unbien.mac"; // legacy keytar service (pre-2026-05-25)
|
|
27
|
+
const ACCOUNT = "longterm-ed25519";
|
|
28
|
+
/**
|
|
29
|
+
* The keyring read can THROW transiently rather than permanently — most
|
|
30
|
+
* notably a macOS Keychain that's still locked right after login/wake (the
|
|
31
|
+
* machine sat idle for days). Treating that throw as "backend unavailable"
|
|
32
|
+
* and minting a fresh identity silently orphans the paired key (the
|
|
33
|
+
* "lost pairing after a week idle" failure). So we retry the read a few times
|
|
34
|
+
* before ever concluding the keyring is truly unavailable. Overridable for
|
|
35
|
+
* tests via `_setKeyringRetryForTest`. */
|
|
36
|
+
let _keyringReadAttempts = 3;
|
|
37
|
+
let _keyringRetryDelayMs = 300;
|
|
38
|
+
/** Raised when the keyring is unreadable on a platform where it's a core OS
|
|
39
|
+
* service (macOS Keychain, Windows Credential Manager) AND no prior file
|
|
40
|
+
* identity exists. We refuse to generate a NEW identity here because that
|
|
41
|
+
* would break existing pairing — the caller surfaces this so the user can
|
|
42
|
+
* unlock the keychain and retry instead of silently re-pairing. */
|
|
43
|
+
export class KeyringUnavailableError extends Error {
|
|
44
|
+
constructor(cause) {
|
|
45
|
+
super("Platform keyring is unreadable and no file-backed identity exists. " +
|
|
46
|
+
"Refusing to generate a NEW identity (that would break existing " +
|
|
47
|
+
"pairing). Unlock your keychain / start your secret service and retry. " +
|
|
48
|
+
"Set UNBIEN_ALLOW_FILE_IDENTITY=1 to force a file-backed identity. " +
|
|
49
|
+
`Cause: ${String(cause)}`);
|
|
50
|
+
this.name = "KeyringUnavailableError";
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/** Raised when no identity can be resolved (keyring unreadable, no identity
|
|
54
|
+
* file) BUT `peers.json` already lists paired devices. Minting a fresh key
|
|
55
|
+
* here would make SelfRevoke wipe those pairings — see issues #95 / #69. */
|
|
56
|
+
export class PairedIdentityMissingError extends Error {
|
|
57
|
+
constructor(pairedCount, cause) {
|
|
58
|
+
super(`No identity could be read, but ${pairedCount} device(s) are already ` +
|
|
59
|
+
"paired. Refusing to generate a NEW identity — that would revoke every " +
|
|
60
|
+
"paired device. This usually means this process cannot reach the same " +
|
|
61
|
+
"keyring as the session that paired (e.g. a systemd --user daemon vs. " +
|
|
62
|
+
"your desktop session). Fix the service's keyring access, or pin the " +
|
|
63
|
+
"identity by copying the paired keypair to ~/.pi/un-bien/identity.json " +
|
|
64
|
+
"(0600), which both contexts read first. " +
|
|
65
|
+
`Cause: ${String(cause)}`);
|
|
66
|
+
this.name = "PairedIdentityMissingError";
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const PI_DIR = unbienStateHome();
|
|
70
|
+
const IDENTITY_FILE = join(PI_DIR, "identity.json");
|
|
71
|
+
const PEERS_PATH = join(PI_DIR, "peers.json");
|
|
72
|
+
/**
|
|
73
|
+
* Per-operation timeout for native keyring calls (gnome-keyring / libsecret
|
|
74
|
+
* via @napi-rs/keyring). A healthy secret service settles in milliseconds; 3s
|
|
75
|
+
* is generous. The point is NOT speed — it is converting a HANG into a thrown
|
|
76
|
+
* error. The native getPassword()/setPassword() can block indefinitely when
|
|
77
|
+
* gnome-keyring waits on a GUI authorization prompt that cannot be shown in
|
|
78
|
+
* a headless / tmux / non-interactive context. A hang never settles, so
|
|
79
|
+
* without this guard the promise never rejects and the retry + file-fallback
|
|
80
|
+
* logic in getOrCreateEd25519Keypair() is unreachable — freezing the entire
|
|
81
|
+
* /unbien pair bootstrap. Raising a real error here lets that fallback
|
|
82
|
+
* chain run as designed.
|
|
83
|
+
*/
|
|
84
|
+
const KEYRING_OP_TIMEOUT_MS = 3_000;
|
|
85
|
+
function _withTimeout(p, op, ms = KEYRING_OP_TIMEOUT_MS) {
|
|
86
|
+
return Promise.race([
|
|
87
|
+
p,
|
|
88
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error(`keyring ${op} timed out after ${ms}ms`)), ms)),
|
|
89
|
+
]);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Lazily loaded `@napi-rs/keyring` binding — issue #113.
|
|
93
|
+
*
|
|
94
|
+
* A STATIC `import { AsyncEntry } from "@napi-rs/keyring"` is evaluated when
|
|
95
|
+
* the extension module is loaded, so a native binding that cannot be resolved
|
|
96
|
+
* takes the WHOLE extension down at load time ("Failed to load extension …:
|
|
97
|
+
* Cannot find native binding"). That happens on a Bun-compiled `pi`: the
|
|
98
|
+
* loader's first branch (`require("./keyring.<triple>.node")`) is fine, but its
|
|
99
|
+
* fallback (`require("@napi-rs/keyring-<triple>")`, a bare package whose `main`
|
|
100
|
+
* IS the .node file) resolves under Node and not under Bun — and the message it
|
|
101
|
+
* prints then blames npm's optional-dependency bug, sending users off to delete
|
|
102
|
+
* node_modules and losing their other pi packages.
|
|
103
|
+
*
|
|
104
|
+
* Loading on first use turns that fatal load error into an ordinary backend
|
|
105
|
+
* failure, which the existing retry + file-identity fallback already handles.
|
|
106
|
+
*/
|
|
107
|
+
let _asyncEntryCtor = null;
|
|
108
|
+
let _nativeBindingError = null;
|
|
109
|
+
async function _loadAsyncEntry() {
|
|
110
|
+
if (_asyncEntryCtor)
|
|
111
|
+
return _asyncEntryCtor;
|
|
112
|
+
if (_nativeBindingError)
|
|
113
|
+
throw _nativeBindingError;
|
|
114
|
+
try {
|
|
115
|
+
const mod = await import("@napi-rs/keyring");
|
|
116
|
+
_asyncEntryCtor = mod.AsyncEntry;
|
|
117
|
+
return _asyncEntryCtor;
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
_nativeBindingError = err;
|
|
121
|
+
throw err;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Did the native binding fail to LOAD (as opposed to an operation failing)?
|
|
126
|
+
*
|
|
127
|
+
* A load failure is deterministic and platform-wide: no retry, no unlock, no
|
|
128
|
+
* amount of waiting brings the keyring back in this process. So it must NOT be
|
|
129
|
+
* treated like a transiently locked Keychain — on macOS/Windows that would
|
|
130
|
+
* throw `KeyringUnavailableError` and leave the user with no working path at
|
|
131
|
+
* all. The file-identity fallback (with its loud warning) is the only usable
|
|
132
|
+
* route here, exactly as on headless Linux.
|
|
133
|
+
*/
|
|
134
|
+
function _nativeBindingUnavailable() {
|
|
135
|
+
return _nativeBindingError !== null;
|
|
136
|
+
}
|
|
137
|
+
/** Test-only: force (or clear with `null`) a memoized binding-load failure, so
|
|
138
|
+
* the Bun/no-native-binding branch is reachable without a Bun host. */
|
|
139
|
+
export function _setNativeBindingErrorForTest(err) {
|
|
140
|
+
_asyncEntryCtor = null;
|
|
141
|
+
_nativeBindingError = err;
|
|
142
|
+
}
|
|
143
|
+
class NapiKeyringBackend {
|
|
144
|
+
async read(service, account) {
|
|
145
|
+
const AsyncEntry = await _loadAsyncEntry();
|
|
146
|
+
const entry = new AsyncEntry(service, account);
|
|
147
|
+
return _withTimeout(entry.getPassword(), `read(${service})`); // undefined on no-entry
|
|
148
|
+
}
|
|
149
|
+
async write(service, account, value) {
|
|
150
|
+
const AsyncEntry = await _loadAsyncEntry();
|
|
151
|
+
const entry = new AsyncEntry(service, account);
|
|
152
|
+
await _withTimeout(entry.setPassword(value), `write(${service})`);
|
|
153
|
+
}
|
|
154
|
+
async delete(service, account) {
|
|
155
|
+
let entry;
|
|
156
|
+
try {
|
|
157
|
+
const AsyncEntry = await _loadAsyncEntry();
|
|
158
|
+
entry = new AsyncEntry(service, account);
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
try {
|
|
164
|
+
return await _withTimeout(entry.deleteCredential(), `delete(${service})`);
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
let _backend = null;
|
|
172
|
+
function _getBackend() {
|
|
173
|
+
if (!_backend)
|
|
174
|
+
_backend = new NapiKeyringBackend();
|
|
175
|
+
return _backend;
|
|
176
|
+
}
|
|
177
|
+
/** Test-only: swap (or clear with `null`) the keyring backend. */
|
|
178
|
+
export function _setKeyStoreBackendForTest(backend) {
|
|
179
|
+
_backend = backend;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Is the platform keyring a CORE OS service we should expect to be present?
|
|
183
|
+
* macOS (Keychain) and Windows (Credential Manager) always have one, so a read
|
|
184
|
+
* that throws there is transient/locked, NOT "headless" — we must not mint a
|
|
185
|
+
* new identity. On Linux/other the secret service may be genuinely absent
|
|
186
|
+
* (headless, no D-Bus), so the documented file fallback applies. Overridable
|
|
187
|
+
* for tests via `_setKeyringExpectedForTest`. */
|
|
188
|
+
let _keyringExpectedOverride = null;
|
|
189
|
+
function _keyringExpectedAvailable() {
|
|
190
|
+
if (_keyringExpectedOverride !== null)
|
|
191
|
+
return _keyringExpectedOverride;
|
|
192
|
+
// Binding never loaded (Bun-built pi, issue #113) → there is no keyring on
|
|
193
|
+
// this platform *for this process*, whatever the OS normally offers.
|
|
194
|
+
if (_nativeBindingUnavailable())
|
|
195
|
+
return false;
|
|
196
|
+
return process.platform === "darwin" || process.platform === "win32";
|
|
197
|
+
}
|
|
198
|
+
/** Test-only: force `_keyringExpectedAvailable()` (so a darwin test host can
|
|
199
|
+
* exercise the Linux/headless branch and vice-versa). `null` restores the
|
|
200
|
+
* real platform check. */
|
|
201
|
+
export function _setKeyringExpectedForTest(value) {
|
|
202
|
+
_keyringExpectedOverride = value;
|
|
203
|
+
}
|
|
204
|
+
/** Test-only: shrink retry attempts/delay so the persistent-failure path is
|
|
205
|
+
* fast. `null`/omitted restores defaults. */
|
|
206
|
+
export function _setKeyringRetryForTest(attempts, delayMs) {
|
|
207
|
+
_keyringReadAttempts = attempts ?? 3;
|
|
208
|
+
_keyringRetryDelayMs = delayMs ?? 300;
|
|
209
|
+
}
|
|
210
|
+
function _sleep(ms) {
|
|
211
|
+
return ms > 0 ? new Promise((r) => setTimeout(r, ms)) : Promise.resolve();
|
|
212
|
+
}
|
|
213
|
+
function _serialize(kp) {
|
|
214
|
+
const payload = {
|
|
215
|
+
pk: Buffer.from(kp.publicKey).toString("base64"),
|
|
216
|
+
sk: Buffer.from(kp.secretKey).toString("base64"),
|
|
217
|
+
};
|
|
218
|
+
return JSON.stringify(payload);
|
|
219
|
+
}
|
|
220
|
+
function _deserialize(stored) {
|
|
221
|
+
const parsed = JSON.parse(stored);
|
|
222
|
+
return {
|
|
223
|
+
publicKey: Buffer.from(parsed.pk, "base64"),
|
|
224
|
+
secretKey: Buffer.from(parsed.sk, "base64"),
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
// ── File fallback (headless Linux) ──────────────────────────────────────────
|
|
228
|
+
async function _readKeypairFromFile() {
|
|
229
|
+
try {
|
|
230
|
+
const raw = await readFile(IDENTITY_FILE, "utf8");
|
|
231
|
+
return _deserialize(raw);
|
|
232
|
+
}
|
|
233
|
+
catch {
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
async function _writeKeypairToFile(kp) {
|
|
238
|
+
await mkdir(PI_DIR, { recursive: true, mode: 0o700 });
|
|
239
|
+
// Best-effort tighten of the dir in case it pre-existed with looser
|
|
240
|
+
// permissions (mkdir's mode is only applied to NEW dirs).
|
|
241
|
+
try {
|
|
242
|
+
await chmod(PI_DIR, 0o700);
|
|
243
|
+
}
|
|
244
|
+
catch {
|
|
245
|
+
/* not fatal */
|
|
246
|
+
}
|
|
247
|
+
await writeFile(IDENTITY_FILE, _serialize(kp), { mode: 0o600 });
|
|
248
|
+
try {
|
|
249
|
+
await chmod(IDENTITY_FILE, 0o600);
|
|
250
|
+
}
|
|
251
|
+
catch {
|
|
252
|
+
/* not fatal */
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
// ── Public API ──────────────────────────────────────────────────────────────
|
|
256
|
+
/**
|
|
257
|
+
* Returns the Pi-secret Ed25519 keypair, generating + persisting one on
|
|
258
|
+
* first call. Resolution order:
|
|
259
|
+
* 1. Existing file `~/.pi/un-bien/identity.json`, if present — it WINS over
|
|
260
|
+
* the keyring. A file identity is only ever written by the headless/
|
|
261
|
+
* degraded fallback (step 4) or an explicit `UNBIEN_ALLOW_FILE_IDENTITY`
|
|
262
|
+
* opt-in, so its mere presence means this machine established its identity
|
|
263
|
+
* as a file and the mobile device paired against THAT pubkey. If the
|
|
264
|
+
* platform keyring later becomes readable (D-Bus/libsecret installed, a
|
|
265
|
+
* desktop session, or a stale/other entry from another install), reading
|
|
266
|
+
* it first would mask the file identity — returning a DIFFERENT key, or
|
|
267
|
+
* (when the keyring is empty) minting a fresh one and persisting it —
|
|
268
|
+
* silently breaking the existing pairing. So when both exist, file wins.
|
|
269
|
+
* 2. New keyring service `dev.unbien.pi` (read retried — a transiently
|
|
270
|
+
* locked Keychain throws; we don't treat that as "no key")
|
|
271
|
+
* 3. Old keyring service `dev.unbien.mac` (migrate → step 2, delete old)
|
|
272
|
+
* 4. Generate a fresh keypair, BUT only when it's safe to: either both
|
|
273
|
+
* keyring reads succeeded and returned nothing (genuine first run), or
|
|
274
|
+
* the keyring is genuinely unavailable on a platform without a core one
|
|
275
|
+
* (headless Linux → a file identity is minted here). On macOS/Windows a
|
|
276
|
+
* persistent read failure with no file identity throws
|
|
277
|
+
* `KeyringUnavailableError` instead of minting a new key — generating
|
|
278
|
+
* there silently breaks existing pairing (the "lost pairing after idle"
|
|
279
|
+
* bug). `UNBIEN_ALLOW_FILE_IDENTITY=1` opts back into a file identity
|
|
280
|
+
* for headless macOS/Windows hosts.
|
|
281
|
+
*
|
|
282
|
+
* Idempotent: subsequent calls return the same identity. The migration
|
|
283
|
+
* runs at most once per machine (the old entry is deleted after copy).
|
|
284
|
+
*/
|
|
285
|
+
export async function getOrCreateEd25519Keypair() {
|
|
286
|
+
const backend = _getBackend();
|
|
287
|
+
// ── Path 0: an existing file-backed identity wins ──────────────────────
|
|
288
|
+
// `~/.pi/un-bien/identity.json` is only ever written by the headless/degraded
|
|
289
|
+
// fallback below (or an operator who set UNBIEN_ALLOW_FILE_IDENTITY=1) —
|
|
290
|
+
// never on a keyring-backed install. So its presence means THIS machine
|
|
291
|
+
// paired against the file key, and the keyring (readable or not, matching or
|
|
292
|
+
// not) must not be allowed to mask it. Short-circuit before touching the
|
|
293
|
+
// keyring so a keyring that later comes online can't return a different key,
|
|
294
|
+
// nor mint a fresh one over the file identity. No file → normal keyring
|
|
295
|
+
// resolution below; a headless first run still reaches Path B and mints one.
|
|
296
|
+
const existingFile = await _readKeypairFromFile();
|
|
297
|
+
if (existingFile)
|
|
298
|
+
return existingFile;
|
|
299
|
+
// ── Path A: keyring (retried) ──────────────────────────────────────────
|
|
300
|
+
// A throw here means the keyring op FAILED — but on macOS/Windows that is
|
|
301
|
+
// almost always a transiently locked Keychain (idle/just-woke machine), not
|
|
302
|
+
// a missing backend. `read` returns `undefined` for "no such entry" (the
|
|
303
|
+
// genuine first-run signal). So we retry on throw, and only a throw that
|
|
304
|
+
// SURVIVES every attempt drops us to Path B.
|
|
305
|
+
let keyringError;
|
|
306
|
+
for (let attempt = 0; attempt < _keyringReadAttempts; attempt++) {
|
|
307
|
+
try {
|
|
308
|
+
const existing = await backend.read(NEW_SERVICE, ACCOUNT);
|
|
309
|
+
if (existing)
|
|
310
|
+
return _deserialize(existing);
|
|
311
|
+
const legacy = await backend.read(OLD_SERVICE, ACCOUNT);
|
|
312
|
+
if (legacy) {
|
|
313
|
+
const kp = _deserialize(legacy);
|
|
314
|
+
await backend.write(NEW_SERVICE, ACCOUNT, legacy);
|
|
315
|
+
await backend.delete(OLD_SERVICE, ACCOUNT);
|
|
316
|
+
// Silent migration: writing the chat surface would be premature
|
|
317
|
+
// (Pi SDK isn't bound yet at this point in boot) and console
|
|
318
|
+
// output bleeds outside the TUI. The presence of an entry under
|
|
319
|
+
// NEW_SERVICE is itself the audit signal — re-running migration
|
|
320
|
+
// is idempotent and harmless.
|
|
321
|
+
return kp;
|
|
322
|
+
}
|
|
323
|
+
// Both reads SUCCEEDED and returned nothing → genuine first run on a
|
|
324
|
+
// working keyring. Generate and save to the new service.
|
|
325
|
+
const fresh = generateEd25519Keypair();
|
|
326
|
+
await backend.write(NEW_SERVICE, ACCOUNT, _serialize(fresh));
|
|
327
|
+
return fresh;
|
|
328
|
+
}
|
|
329
|
+
catch (err) {
|
|
330
|
+
keyringError = err;
|
|
331
|
+
if (attempt < _keyringReadAttempts - 1) {
|
|
332
|
+
// Linear backoff — a locked Keychain usually frees within seconds.
|
|
333
|
+
await _sleep(_keyringRetryDelayMs * (attempt + 1));
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
// ── Path B: keyring threw on every attempt ─────────────────────────────
|
|
338
|
+
// Path 0 already returned any pre-existing file identity; this defensive
|
|
339
|
+
// re-check catches a file written concurrently by another Pi process during
|
|
340
|
+
// the keyring-retry window (still: use it, never regenerate).
|
|
341
|
+
const fromFile = await _readKeypairFromFile();
|
|
342
|
+
if (fromFile)
|
|
343
|
+
return fromFile;
|
|
344
|
+
// No file identity AND the keyring is unreadable. CRITICAL FORK:
|
|
345
|
+
//
|
|
346
|
+
// - On a platform without a guaranteed keyring (headless Linux, no D-Bus),
|
|
347
|
+
// minting a file-backed identity is the documented, correct first-run
|
|
348
|
+
// behavior.
|
|
349
|
+
// - On macOS/Windows the keyring is a core OS service, so a persistent read
|
|
350
|
+
// failure means it's LOCKED/denied — NOT that we're a fresh install.
|
|
351
|
+
// Generating a new key here is exactly what silently broke pairing after
|
|
352
|
+
// a week idle, and the new key then masks the real Keychain identity via
|
|
353
|
+
// the file. So we FAIL LOUD instead, unless the operator explicitly
|
|
354
|
+
// opts into a file identity.
|
|
355
|
+
const forceFile = process.env.UNBIEN_ALLOW_FILE_IDENTITY === "1";
|
|
356
|
+
if (_keyringExpectedAvailable() && !forceFile) {
|
|
357
|
+
throw new KeyringUnavailableError(keyringError);
|
|
358
|
+
}
|
|
359
|
+
// Issues #95 / #69 — minting a NEW identity when devices are already paired
|
|
360
|
+
// is destructive, on every platform.
|
|
361
|
+
//
|
|
362
|
+
// A daemon started by `systemd --user` resolves a different secret-service
|
|
363
|
+
// store than the desktop session that ran the pairing, so the keyring read
|
|
364
|
+
// fails, this path mints a fresh Ed25519 key, and the SelfRevoke poller then
|
|
365
|
+
// finds that key absent from the relay-side owner envelope, concludes it was
|
|
366
|
+
// revoked, and wipes `peers.json` — the phone silently goes offline a few
|
|
367
|
+
// seconds after every `daemon start`. `peers.json` being non-empty is proof
|
|
368
|
+
// that a working identity existed, so "no key found" here is a broken
|
|
369
|
+
// environment, not a first run. Fail loud and keep the pairing intact; the
|
|
370
|
+
// operator can pin the identity to a file (the confirmed workaround) or fix
|
|
371
|
+
// the service's keyring access.
|
|
372
|
+
if (!forceFile) {
|
|
373
|
+
const paired = await listPeers();
|
|
374
|
+
if (paired.length > 0) {
|
|
375
|
+
throw new PairedIdentityMissingError(paired.length, keyringError);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
console.warn(_nativeBindingUnavailable()
|
|
379
|
+
? // Issue #113: the @napi-rs/keyring native binding could not be loaded at
|
|
380
|
+
// all (typically a Bun-compiled pi). Name it explicitly — the error the
|
|
381
|
+
// loader prints blames npm's optional-dependency bug and sends people off
|
|
382
|
+
// to delete node_modules, which takes their other pi packages with it.
|
|
383
|
+
"[un-bien] @napi-rs/keyring native binding could not be loaded in this " +
|
|
384
|
+
`runtime; using file-backed identity at ${IDENTITY_FILE} (0600) instead. ` +
|
|
385
|
+
"This is expected on a Bun-built pi. Paired devices keyed to a previous " +
|
|
386
|
+
`keyring identity must be re-paired. ${String(keyringError)}`
|
|
387
|
+
: "[un-bien] keyring unavailable; using file-backed identity at " +
|
|
388
|
+
`${IDENTITY_FILE}. ${String(keyringError)}`);
|
|
389
|
+
const fresh = generateEd25519Keypair();
|
|
390
|
+
await _writeKeypairToFile(fresh);
|
|
391
|
+
return fresh;
|
|
392
|
+
}
|
|
393
|
+
export async function listPeers() {
|
|
394
|
+
try {
|
|
395
|
+
const raw = await readFile(PEERS_PATH, "utf8");
|
|
396
|
+
const parsed = JSON.parse(raw);
|
|
397
|
+
return Array.isArray(parsed.peers) ? parsed.peers : [];
|
|
398
|
+
}
|
|
399
|
+
catch {
|
|
400
|
+
return [];
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Authoritative container read for SelfRevoke's token path. Public readers
|
|
405
|
+
* intentionally remain best-effort; only a missing file is proof of emptiness
|
|
406
|
+
* here. Valid array elements are returned verbatim for corruption isolation.
|
|
407
|
+
*/
|
|
408
|
+
async function _readPeerContainerStrict() {
|
|
409
|
+
let raw;
|
|
410
|
+
try {
|
|
411
|
+
raw = await readFile(PEERS_PATH, "utf8");
|
|
412
|
+
}
|
|
413
|
+
catch (error) {
|
|
414
|
+
if (typeof error === "object" &&
|
|
415
|
+
error !== null &&
|
|
416
|
+
"code" in error &&
|
|
417
|
+
error.code === "ENOENT") {
|
|
418
|
+
return [];
|
|
419
|
+
}
|
|
420
|
+
throw error;
|
|
421
|
+
}
|
|
422
|
+
const parsed = JSON.parse(raw);
|
|
423
|
+
if (!Array.isArray(parsed.peers)) {
|
|
424
|
+
throw new Error("Invalid peers.json container");
|
|
425
|
+
}
|
|
426
|
+
return parsed.peers;
|
|
427
|
+
}
|
|
428
|
+
let _peerMutationQueue = Promise.resolve();
|
|
429
|
+
const _ownerSlotTokens = new Map();
|
|
430
|
+
const _ownerStorageTokenBrand = Symbol("owner-storage-token");
|
|
431
|
+
function _ownerSlotKey(rawOwnerPubkey) {
|
|
432
|
+
if (typeof rawOwnerPubkey !== "string") {
|
|
433
|
+
// Invalid non-string records remain in snapshots, but SelfRevoke skips
|
|
434
|
+
// them before conditional removal; quarantine-key collisions cannot
|
|
435
|
+
// authorize a removal.
|
|
436
|
+
return `raw:quarantine:${typeof rawOwnerPubkey}`;
|
|
437
|
+
}
|
|
438
|
+
try {
|
|
439
|
+
return `owner:${canonicalizeEd25519PublicKey(rawOwnerPubkey, "Owner record")}`;
|
|
440
|
+
}
|
|
441
|
+
catch {
|
|
442
|
+
return `raw:string:${rawOwnerPubkey}`;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
function _tokenForSlot(slot) {
|
|
446
|
+
const existing = _ownerSlotTokens.get(slot);
|
|
447
|
+
if (existing)
|
|
448
|
+
return existing;
|
|
449
|
+
const token = Object.freeze({
|
|
450
|
+
[_ownerStorageTokenBrand]: true,
|
|
451
|
+
});
|
|
452
|
+
_ownerSlotTokens.set(slot, token);
|
|
453
|
+
return token;
|
|
454
|
+
}
|
|
455
|
+
function _invalidateOwnerSlot(rawOwnerPubkey) {
|
|
456
|
+
const slot = _ownerSlotKey(rawOwnerPubkey);
|
|
457
|
+
const token = Object.freeze({
|
|
458
|
+
[_ownerStorageTokenBrand]: true,
|
|
459
|
+
});
|
|
460
|
+
_ownerSlotTokens.set(slot, token);
|
|
461
|
+
return token;
|
|
462
|
+
}
|
|
463
|
+
function _serializePeerMutation(mutation) {
|
|
464
|
+
const result = _peerMutationQueue.then(mutation, mutation);
|
|
465
|
+
_peerMutationQueue = result.then(() => undefined, () => undefined);
|
|
466
|
+
return result;
|
|
467
|
+
}
|
|
468
|
+
export function addPeer(record) {
|
|
469
|
+
return _serializePeerMutation(async () => {
|
|
470
|
+
const peers = (await listPeers());
|
|
471
|
+
const idx = peers.findIndex((peer) => !!peer &&
|
|
472
|
+
typeof peer === "object" &&
|
|
473
|
+
peer.remote_epk === record.remote_epk);
|
|
474
|
+
if (idx >= 0) {
|
|
475
|
+
peers[idx] = record; // idempotent re-pair
|
|
476
|
+
}
|
|
477
|
+
else {
|
|
478
|
+
peers.push(record);
|
|
479
|
+
}
|
|
480
|
+
await mkdir(dirname(PEERS_PATH), { recursive: true });
|
|
481
|
+
await writeFile(PEERS_PATH, JSON.stringify({ peers }, null, 2));
|
|
482
|
+
// A successful re-pair is a new storage provenance event even when the
|
|
483
|
+
// record bytes happen to be identical.
|
|
484
|
+
_invalidateOwnerSlot(record.remote_epk);
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* Returns the set of distinct `remote_epk` values in peers.json.
|
|
489
|
+
*
|
|
490
|
+
* In the current pairing model (plan/23 + plan/24), each `remote_epk` is the
|
|
491
|
+
* Owner's Ed25519 pubkey — and we treat each as a distinct Owner the Pi has
|
|
492
|
+
* been paired with. Used by the mesh self-revoke poller (plan/24 Wave 3) to
|
|
493
|
+
* know which Owners' mesh blobs to fetch.
|
|
494
|
+
*/
|
|
495
|
+
export async function listOwnerPubkeys() {
|
|
496
|
+
const peers = (await listPeers());
|
|
497
|
+
const seen = new Set();
|
|
498
|
+
for (const peer of peers) {
|
|
499
|
+
if (!peer || typeof peer !== "object") {
|
|
500
|
+
seen.add(peer);
|
|
501
|
+
continue;
|
|
502
|
+
}
|
|
503
|
+
seen.add(peer.remote_epk);
|
|
504
|
+
}
|
|
505
|
+
return [...seen];
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* Atomically snapshots raw Owner handles and their canonical-slot provenance.
|
|
509
|
+
* The token is deliberately process-local and opaque to callers.
|
|
510
|
+
*/
|
|
511
|
+
export function snapshotOwnerPubkeys() {
|
|
512
|
+
return _serializePeerMutation(async () => {
|
|
513
|
+
const peers = await _readPeerContainerStrict();
|
|
514
|
+
const rawOwners = new Set();
|
|
515
|
+
for (const peer of peers) {
|
|
516
|
+
if (!peer || typeof peer !== "object") {
|
|
517
|
+
rawOwners.add(peer);
|
|
518
|
+
}
|
|
519
|
+
else {
|
|
520
|
+
rawOwners.add(peer.remote_epk);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
return [...rawOwners].map((rawOwnerPubkey) => ({
|
|
524
|
+
rawOwnerPubkey,
|
|
525
|
+
token: _tokenForSlot(_ownerSlotKey(rawOwnerPubkey)),
|
|
526
|
+
}));
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* Removes one exact raw handle only when its snapshot provenance still owns
|
|
531
|
+
* the target canonical Owner slot. The final authority/token checks and sync
|
|
532
|
+
* write share the existing serialized mutation lane.
|
|
533
|
+
*/
|
|
534
|
+
export function conditionalRemovePeer(remoteEpk, expectedToken, canCommit) {
|
|
535
|
+
return _serializePeerMutation(async () => {
|
|
536
|
+
const slot = _ownerSlotKey(remoteEpk);
|
|
537
|
+
// Provenance belongs to the canonical Owner slot, not the exact raw
|
|
538
|
+
// spelling. A stale slot must therefore win over an absent old spelling.
|
|
539
|
+
if (_tokenForSlot(slot) !== expectedToken)
|
|
540
|
+
return { outcome: "stale" };
|
|
541
|
+
const peers = await _readPeerContainerStrict();
|
|
542
|
+
const filtered = peers.filter((peer) => !peer ||
|
|
543
|
+
typeof peer !== "object" ||
|
|
544
|
+
peer.remote_epk !== remoteEpk);
|
|
545
|
+
if (filtered.length === peers.length)
|
|
546
|
+
return { outcome: "not_found" };
|
|
547
|
+
await mkdir(dirname(PEERS_PATH), { recursive: true });
|
|
548
|
+
// No await may intervene between the final token/authority checks and
|
|
549
|
+
// synchronous write, preserving the lane's fail-closed commit boundary.
|
|
550
|
+
if (_tokenForSlot(slot) !== expectedToken)
|
|
551
|
+
return { outcome: "stale" };
|
|
552
|
+
if (canCommit) {
|
|
553
|
+
let authorized = false;
|
|
554
|
+
try {
|
|
555
|
+
authorized = canCommit();
|
|
556
|
+
}
|
|
557
|
+
catch {
|
|
558
|
+
return { outcome: "no_authority" };
|
|
559
|
+
}
|
|
560
|
+
if (!authorized)
|
|
561
|
+
return { outcome: "no_authority" };
|
|
562
|
+
}
|
|
563
|
+
writeFileSync(PEERS_PATH, JSON.stringify({ peers: filtered }, null, 2));
|
|
564
|
+
return { outcome: "removed", nextToken: _invalidateOwnerSlot(remoteEpk) };
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
export function removePeer(remoteEpk, canCommit) {
|
|
568
|
+
return _serializePeerMutation(async () => {
|
|
569
|
+
const peers = (await listPeers());
|
|
570
|
+
const filtered = peers.filter((peer) => !peer ||
|
|
571
|
+
typeof peer !== "object" ||
|
|
572
|
+
peer.remote_epk !== remoteEpk);
|
|
573
|
+
if (filtered.length === peers.length)
|
|
574
|
+
return false;
|
|
575
|
+
await mkdir(dirname(PEERS_PATH), { recursive: true });
|
|
576
|
+
const serialized = JSON.stringify({ peers: filtered }, null, 2);
|
|
577
|
+
if (canCommit) {
|
|
578
|
+
// Guarded SelfRevoke commits must be atomic with their final authority
|
|
579
|
+
// check at the JavaScript level: fail closed on false/throw, then perform
|
|
580
|
+
// the tiny JSON rewrite synchronously with no interruptible await between.
|
|
581
|
+
let authorized = false;
|
|
582
|
+
try {
|
|
583
|
+
authorized = canCommit();
|
|
584
|
+
}
|
|
585
|
+
catch {
|
|
586
|
+
return false;
|
|
587
|
+
}
|
|
588
|
+
if (!authorized)
|
|
589
|
+
return false;
|
|
590
|
+
writeFileSync(PEERS_PATH, serialized);
|
|
591
|
+
}
|
|
592
|
+
else {
|
|
593
|
+
// Manual removals keep the established asynchronous storage behavior.
|
|
594
|
+
await writeFile(PEERS_PATH, serialized);
|
|
595
|
+
}
|
|
596
|
+
const removed = true;
|
|
597
|
+
if (removed)
|
|
598
|
+
_invalidateOwnerSlot(remoteEpk);
|
|
599
|
+
return removed;
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
// ── Test-only helpers ────────────────────────────────────────────────────────
|
|
603
|
+
/** Test-only: expose the identity-file path so tests can clean it. */
|
|
604
|
+
export const _IDENTITY_FILE_FOR_TEST = IDENTITY_FILE;
|
|
605
|
+
/** Test-only: expose unlink for cleanup. */
|
|
606
|
+
export const _unlinkIdentityFileForTest = async () => {
|
|
607
|
+
try {
|
|
608
|
+
await unlink(IDENTITY_FILE);
|
|
609
|
+
}
|
|
610
|
+
catch {
|
|
611
|
+
/* fine if missing */
|
|
612
|
+
}
|
|
613
|
+
};
|
|
614
|
+
//# sourceMappingURL=storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/pairing/storage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import type { ServerMessage } from "./protocol/types.js";
|
|
3
|
+
export interface PanelBridge {
|
|
4
|
+
/** Current panels as `panel_update` frames, for `session_sync` to replay to a
|
|
5
|
+
* late-joining peer (mirrors the ui bridge's `pendingRequests`). */
|
|
6
|
+
pendingPanels(): ServerMessage[];
|
|
7
|
+
/** Drop subscriptions, timers, and state. */
|
|
8
|
+
dispose(): void;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Subscribe the plan + subagents buses and broadcast `panel_update` frames.
|
|
12
|
+
* Returns `null` when the SDK exposes no usable events bus (defensive — modern
|
|
13
|
+
* Pi always has one); callers stay null-safe.
|
|
14
|
+
*/
|
|
15
|
+
export declare function createPanelBridge(pi: ExtensionAPI, broadcast: (msg: ServerMessage) => void): PanelBridge | null;
|