@nority/bridge-sdk 0.1.0 → 0.2.1
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 +124 -10
- package/dist/agent.d.ts +1 -1
- package/dist/agent.d.ts.map +1 -1
- package/dist/bridge.d.ts +12 -22
- package/dist/bridge.d.ts.map +1 -1
- package/dist/bridge.js +97 -263
- package/dist/bridge.js.map +1 -1
- package/dist/connection-types.d.ts +20 -0
- package/dist/connection-types.d.ts.map +1 -0
- package/dist/connection-types.js +2 -0
- package/dist/connection-types.js.map +1 -0
- package/dist/connection.d.ts +36 -0
- package/dist/connection.d.ts.map +1 -0
- package/dist/connection.js +174 -0
- package/dist/connection.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/pairing/client.d.ts +44 -0
- package/dist/pairing/client.d.ts.map +1 -0
- package/dist/pairing/client.js +241 -0
- package/dist/pairing/client.js.map +1 -0
- package/dist/pairing/index.d.ts +3 -0
- package/dist/pairing/index.d.ts.map +1 -0
- package/dist/pairing/index.js +2 -0
- package/dist/pairing/index.js.map +1 -0
- package/dist/pairing/types.d.ts +20 -0
- package/dist/pairing/types.d.ts.map +1 -0
- package/dist/pairing/types.js +2 -0
- package/dist/pairing/types.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -68,18 +68,92 @@ const bridge = new NorityBridge({
|
|
|
68
68
|
backendUrl: "wss://api.nority.ai/v1/bridge",
|
|
69
69
|
dataDir: "./bridge-data",
|
|
70
70
|
agent: new MyAdapter(),
|
|
71
|
-
|
|
71
|
+
onPairingInfo: (info) => {
|
|
72
72
|
console.log(`Pair: ${info.deeplinkUrl} (expires ${info.expiresAt})`);
|
|
73
73
|
},
|
|
74
74
|
});
|
|
75
75
|
|
|
76
76
|
await bridge.start();
|
|
77
|
+
// Bridge is now connected and handling conversations
|
|
77
78
|
```
|
|
78
79
|
|
|
79
|
-
On first run, the SDK generates an Ed25519 identity, enters the pairing flow, and calls `
|
|
80
|
+
On first run, the SDK generates an Ed25519 identity, enters the pairing flow, and calls `onPairingInfo` with a QR/deeplink URL. `start()` blocks until the user approves in the Nority app and the bridge connects.
|
|
80
81
|
|
|
81
82
|
On subsequent runs, the SDK loads the stored credential, mints a fresh JWT via challenge-response, reconnects, replays missed events, and resumes normal operation.
|
|
82
83
|
|
|
84
|
+
## Layered API
|
|
85
|
+
|
|
86
|
+
The SDK exposes three levels of abstraction. Most developers only need `NorityBridge`.
|
|
87
|
+
|
|
88
|
+
### `NorityBridge` — managed (recommended)
|
|
89
|
+
|
|
90
|
+
Handles everything: pairing, credential persistence, auth, transport, conversations. `start()` blocks until fully connected regardless of whether pairing was needed.
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
import { NorityBridge } from "@nority/bridge-sdk";
|
|
94
|
+
|
|
95
|
+
const bridge = new NorityBridge({
|
|
96
|
+
backendUrl: "wss://api.nority.ai/v1/bridge",
|
|
97
|
+
dataDir: "./bridge-data",
|
|
98
|
+
agent: myAdapter,
|
|
99
|
+
onPairingInfo: (info) => console.log(`Scan: ${info.deeplinkUrl}`),
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
await bridge.start(); // blocks until connected
|
|
103
|
+
await bridge.stop(); // graceful shutdown
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### `PairingClient` — standalone pairing
|
|
107
|
+
|
|
108
|
+
Use this when you want to control the pairing ceremony yourself — embed the QR in your own UI, store the credential in your own database, or skip pairing entirely with pre-provisioned credentials.
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
import { PairingClient } from "@nority/bridge-sdk";
|
|
112
|
+
|
|
113
|
+
const pairing = new PairingClient({
|
|
114
|
+
backendUrl: "wss://api.nority.ai/v1/bridge",
|
|
115
|
+
dataDir: "./bridge-data",
|
|
116
|
+
onPairingInfo: (info) => renderQRCode(info.deeplinkUrl),
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
const result = await pairing.pair(); // blocks until user approves
|
|
120
|
+
// result.credential — durable bridge credential
|
|
121
|
+
// result.identity — Ed25519 keypair
|
|
122
|
+
// result.initialToken — first JWT (pass as seedJwt to BridgeConnection)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
**Methods:**
|
|
126
|
+
- `pair()` — performs the pairing ceremony. Resolves when the user approves. Single-flight (throws if called while already pairing).
|
|
127
|
+
- `cancel()` — cancels in-progress pairing. Rejects the pending `pair()` promise. Idempotent.
|
|
128
|
+
- `regenerateToken()` — closes the WebSocket, opens a new one, sends a fresh `pair_request`. Returns updated `PairingInfo`.
|
|
129
|
+
- `getPairingInfo()` — returns current pairing info or `null`.
|
|
130
|
+
|
|
131
|
+
### `BridgeConnection` — standalone connection
|
|
132
|
+
|
|
133
|
+
Use this when you already have a credential (pre-provisioned from a dashboard, stored in your own secret manager, or obtained via `PairingClient`).
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
import { BridgeConnection } from "@nority/bridge-sdk";
|
|
137
|
+
|
|
138
|
+
const connection = new BridgeConnection({
|
|
139
|
+
backendUrl: "wss://api.nority.ai/v1/bridge",
|
|
140
|
+
dataDir: "./bridge-data",
|
|
141
|
+
credential: process.env.BRIDGE_CREDENTIAL,
|
|
142
|
+
privateKeyPem: fs.readFileSync("./bridge-key.pem", "utf-8"),
|
|
143
|
+
agent: myAdapter,
|
|
144
|
+
seedJwt: cachedToken, // optional — skip first challenge-response
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
await connection.connect(); // blocks until connected
|
|
148
|
+
await connection.disconnect(); // graceful shutdown
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**Methods:**
|
|
152
|
+
- `connect()` — authenticates via challenge-response, opens authenticated WebSocket, sends replay request. Resolves when connected.
|
|
153
|
+
- `disconnect()` — closes connection, stops reconnection, aborts active agent invocations. Idempotent.
|
|
154
|
+
- `getState()` — returns current state (`idle | connecting | connected | reconnecting | blocked | failed | stopped`).
|
|
155
|
+
- `createProactiveTurnFrame(conversationId, request)` — creates an `assistant_turn_start` frame for bridge-initiated turns.
|
|
156
|
+
|
|
83
157
|
## Key concepts
|
|
84
158
|
|
|
85
159
|
| Concept | Description |
|
|
@@ -141,16 +215,56 @@ idle → pairing → connecting → connected
|
|
|
141
215
|
|
|
142
216
|
## Configuration
|
|
143
217
|
|
|
218
|
+
### `BridgeConfig` (for `NorityBridge`)
|
|
219
|
+
|
|
144
220
|
```ts
|
|
145
221
|
interface BridgeConfig {
|
|
146
|
-
backendUrl: string;
|
|
147
|
-
dataDir: string;
|
|
148
|
-
agent: AgentAdapter;
|
|
149
|
-
|
|
150
|
-
logger?: BridgeLogger;
|
|
151
|
-
webSocketFactory?: WebSocketFactory;
|
|
152
|
-
|
|
153
|
-
|
|
222
|
+
backendUrl: string; // Nority backend WebSocket URL
|
|
223
|
+
dataDir: string; // Directory for durable state (identity, keys)
|
|
224
|
+
agent: AgentAdapter; // Your adapter implementation
|
|
225
|
+
onPairingInfo?: (pairing: PairingInfo) => void; // Called when pairing token is ready
|
|
226
|
+
logger?: BridgeLogger; // Structured logging
|
|
227
|
+
webSocketFactory?: WebSocketFactory; // Custom WebSocket creation
|
|
228
|
+
fetchImplementation?: typeof fetch; // Custom fetch (for auth challenge-response)
|
|
229
|
+
clock?: () => Date; // Custom clock (for testing)
|
|
230
|
+
pairingTokenFactory?: () => string; // Custom pairing token generator
|
|
231
|
+
backoffRandom?: () => number; // Custom RNG for reconnect backoff jitter
|
|
232
|
+
heartbeatIntervalMs?: number; // Default: 15000
|
|
233
|
+
idleTimeoutMs?: number; // Default: 45000
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### `PairingClientConfig`
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
interface PairingClientConfig {
|
|
241
|
+
backendUrl: string;
|
|
242
|
+
dataDir: string;
|
|
243
|
+
onPairingInfo?: (info: PairingInfo) => void;
|
|
244
|
+
logger?: BridgeLogger;
|
|
245
|
+
webSocketFactory?: WebSocketFactory;
|
|
246
|
+
clock?: () => Date;
|
|
247
|
+
pairingTokenFactory?: () => string;
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### `BridgeConnectionConfig`
|
|
252
|
+
|
|
253
|
+
```ts
|
|
254
|
+
interface BridgeConnectionConfig {
|
|
255
|
+
backendUrl: string;
|
|
256
|
+
dataDir: string;
|
|
257
|
+
credential: string;
|
|
258
|
+
privateKeyPem: string;
|
|
259
|
+
agent: AgentAdapter;
|
|
260
|
+
logger?: BridgeLogger;
|
|
261
|
+
seedJwt?: AccessTokenRecord;
|
|
262
|
+
fetchImplementation?: typeof fetch;
|
|
263
|
+
webSocketFactory?: WebSocketFactory;
|
|
264
|
+
clock?: () => Date;
|
|
265
|
+
backoffRandom?: () => number;
|
|
266
|
+
heartbeatIntervalMs?: number;
|
|
267
|
+
idleTimeoutMs?: number;
|
|
154
268
|
}
|
|
155
269
|
```
|
|
156
270
|
|
package/dist/agent.d.ts
CHANGED
|
@@ -88,7 +88,7 @@ export interface BridgeConfig {
|
|
|
88
88
|
backendUrl: string;
|
|
89
89
|
dataDir: string;
|
|
90
90
|
agent: AgentAdapter;
|
|
91
|
-
|
|
91
|
+
onPairingInfo?: (pairing: PairingInfo) => void;
|
|
92
92
|
logger?: BridgeLogger;
|
|
93
93
|
fetchImplementation?: typeof fetch;
|
|
94
94
|
webSocketFactory?: WebSocketFactory;
|
package/dist/agent.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}
|
package/dist/bridge.d.ts
CHANGED
|
@@ -1,27 +1,25 @@
|
|
|
1
1
|
import type { BridgeConfig, PairingInfo } from "./agent.js";
|
|
2
2
|
import type { ProactiveTurnRequest } from "./runtime/proactive.js";
|
|
3
|
-
import {
|
|
3
|
+
import type { BridgeFrame } from "./protocol/frames.js";
|
|
4
4
|
/**
|
|
5
5
|
* Bridge lifecycle states.
|
|
6
6
|
*/
|
|
7
7
|
export type BridgeState = "idle" | "pairing" | "connecting" | "connected" | "reconnecting" | "blocked" | "failed" | "stopped";
|
|
8
8
|
/**
|
|
9
|
-
* NorityBridge is the
|
|
10
|
-
*
|
|
9
|
+
* NorityBridge is the managed entry point for bridge implementations.
|
|
10
|
+
* Composes PairingClient + BridgeConnection: if no stored credential,
|
|
11
|
+
* runs pairing first; otherwise connects directly.
|
|
12
|
+
*
|
|
13
|
+
* start() blocks until the bridge is fully connected — regardless of
|
|
14
|
+
* whether pairing was needed. The onPairingInfo callback fires during
|
|
15
|
+
* start() if pairing is required, enabling the caller to display a QR code.
|
|
11
16
|
*/
|
|
12
17
|
export declare class NorityBridge {
|
|
13
18
|
private readonly config;
|
|
14
|
-
private readonly logger;
|
|
15
19
|
private readonly stateStore;
|
|
16
|
-
private readonly accessTokenManager;
|
|
17
|
-
private readonly conversationRuntime;
|
|
18
20
|
private state;
|
|
19
|
-
private
|
|
20
|
-
private
|
|
21
|
-
private pairingTimer;
|
|
22
|
-
private pairingSocket;
|
|
23
|
-
private transport;
|
|
24
|
-
private readonly pendingFrameTasks;
|
|
21
|
+
private pairingClient;
|
|
22
|
+
private connection;
|
|
25
23
|
constructor(config: BridgeConfig);
|
|
26
24
|
getState(): BridgeState;
|
|
27
25
|
getPairingInfo(): PairingInfo | null;
|
|
@@ -29,16 +27,8 @@ export declare class NorityBridge {
|
|
|
29
27
|
start(): Promise<void>;
|
|
30
28
|
stop(): Promise<void>;
|
|
31
29
|
regeneratePairingToken(): Promise<PairingInfo>;
|
|
32
|
-
private
|
|
33
|
-
private
|
|
34
|
-
private handlePairingMessage;
|
|
35
|
-
private ensureAuthenticatedTransport;
|
|
36
|
-
private handleTransportStateChange;
|
|
37
|
-
private getDurableState;
|
|
38
|
-
private clearPairingTimer;
|
|
30
|
+
private connectWithCredential;
|
|
31
|
+
private syncConnectionState;
|
|
39
32
|
private setState;
|
|
40
|
-
private handleTransportFrame;
|
|
41
|
-
private trackFrameTask;
|
|
42
|
-
private sendTransportFrame;
|
|
43
33
|
}
|
|
44
34
|
//# sourceMappingURL=bridge.d.ts.map
|
package/dist/bridge.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAM5D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD;;GAEG;AACH,MAAM,MAAM,WAAW,GACnB,MAAM,GACN,SAAS,GACT,YAAY,GACZ,WAAW,GACX,cAAc,GACd,SAAS,GACT,QAAQ,GACR,SAAS,CAAC;AAEd;;;;;;;;GAQG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;IACtC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAuB;IAClD,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,aAAa,CAA8B;IACnD,OAAO,CAAC,UAAU,CAAiC;gBAEvC,MAAM,EAAE,YAAY;IAMhC,QAAQ,IAAI,WAAW;IAIvB,cAAc,IAAI,WAAW,GAAG,IAAI;IAIpC,wBAAwB,CACtB,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,oBAAoB,GAC5B,WAAW,CAAC,sBAAsB,CAAC;IAShC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAwCtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAcrB,sBAAsB,IAAI,OAAO,CAAC,WAAW,CAAC;YAStC,qBAAqB;IA4BnC,OAAO,CAAC,mBAAmB;IAW3B,OAAO,CAAC,QAAQ;CAGjB"}
|
package/dist/bridge.js
CHANGED
|
@@ -1,311 +1,145 @@
|
|
|
1
1
|
import { validateConfig } from "./config.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { PairingClient } from "./pairing/client.js";
|
|
3
|
+
import { BridgeConnection } from "./connection.js";
|
|
4
4
|
import { FileBridgeStateStore } from "./storage/state-store.js";
|
|
5
|
-
import { ConversationRuntime } from "./conversation/runtime.js";
|
|
6
|
-
import { AuthenticatedWebSocketTransport, } from "./transport/websocket.js";
|
|
7
|
-
import { createFrame } from "./protocol/frames.js";
|
|
8
|
-
import { getEd25519PublicKeyBase64 } from "./crypto/identity.js";
|
|
9
|
-
const noopLogger = {
|
|
10
|
-
debug: () => { },
|
|
11
|
-
info: () => { },
|
|
12
|
-
warn: () => { },
|
|
13
|
-
error: () => { },
|
|
14
|
-
};
|
|
15
5
|
/**
|
|
16
|
-
* NorityBridge is the
|
|
17
|
-
*
|
|
6
|
+
* NorityBridge is the managed entry point for bridge implementations.
|
|
7
|
+
* Composes PairingClient + BridgeConnection: if no stored credential,
|
|
8
|
+
* runs pairing first; otherwise connects directly.
|
|
9
|
+
*
|
|
10
|
+
* start() blocks until the bridge is fully connected — regardless of
|
|
11
|
+
* whether pairing was needed. The onPairingInfo callback fires during
|
|
12
|
+
* start() if pairing is required, enabling the caller to display a QR code.
|
|
18
13
|
*/
|
|
19
14
|
export class NorityBridge {
|
|
20
15
|
config;
|
|
21
|
-
logger;
|
|
22
16
|
stateStore;
|
|
23
|
-
accessTokenManager;
|
|
24
|
-
conversationRuntime;
|
|
25
17
|
state = "idle";
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
pairingTimer = null;
|
|
29
|
-
pairingSocket = null;
|
|
30
|
-
transport = null;
|
|
31
|
-
pendingFrameTasks = new Set();
|
|
18
|
+
pairingClient = null;
|
|
19
|
+
connection = null;
|
|
32
20
|
constructor(config) {
|
|
33
21
|
validateConfig(config);
|
|
34
22
|
this.config = config;
|
|
35
|
-
this.logger = config.logger ?? noopLogger;
|
|
36
23
|
this.stateStore = new FileBridgeStateStore(config.dataDir);
|
|
37
|
-
this.accessTokenManager = new BridgeAccessTokenManager({
|
|
38
|
-
backendUrl: config.backendUrl,
|
|
39
|
-
fetchImplementation: config.fetchImplementation,
|
|
40
|
-
clock: config.clock,
|
|
41
|
-
});
|
|
42
|
-
this.conversationRuntime = new ConversationRuntime({
|
|
43
|
-
dataDir: config.dataDir,
|
|
44
|
-
agent: config.agent,
|
|
45
|
-
logger: this.logger,
|
|
46
|
-
clock: config.clock,
|
|
47
|
-
});
|
|
48
24
|
}
|
|
49
25
|
getState() {
|
|
50
26
|
return this.state;
|
|
51
27
|
}
|
|
52
28
|
getPairingInfo() {
|
|
53
|
-
return this.
|
|
29
|
+
return this.pairingClient?.getPairingInfo() ?? null;
|
|
54
30
|
}
|
|
55
31
|
createProactiveTurnFrame(conversationId, request) {
|
|
56
|
-
|
|
32
|
+
if (this.connection === null) {
|
|
33
|
+
throw new Error("NorityBridge.createProactiveTurnFrame: connection must exist");
|
|
34
|
+
}
|
|
35
|
+
return this.connection.createProactiveTurnFrame(conversationId, request);
|
|
57
36
|
}
|
|
58
37
|
async start() {
|
|
59
38
|
if (this.state !== "idle" && this.state !== "stopped") {
|
|
60
39
|
throw new Error(`NorityBridge.start: cannot start bridge in state ${this.state}`);
|
|
61
40
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
41
|
+
const storedState = await this.stateStore.loadOrCreateIdentity();
|
|
42
|
+
if (storedState.bridgeCredential) {
|
|
43
|
+
await this.connectWithCredential(storedState);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
65
46
|
this.setState("pairing");
|
|
66
|
-
|
|
67
|
-
|
|
47
|
+
this.pairingClient = new PairingClient({
|
|
48
|
+
backendUrl: this.config.backendUrl,
|
|
49
|
+
dataDir: this.config.dataDir,
|
|
50
|
+
onPairingInfo: this.config.onPairingInfo,
|
|
51
|
+
logger: this.config.logger,
|
|
52
|
+
webSocketFactory: this.config.webSocketFactory,
|
|
53
|
+
clock: this.config.clock,
|
|
54
|
+
pairingTokenFactory: this.config.pairingTokenFactory,
|
|
55
|
+
});
|
|
56
|
+
const result = await this.pairingClient.pair();
|
|
57
|
+
this.pairingClient = null;
|
|
58
|
+
await this.stateStore.save({
|
|
59
|
+
agentId: result.agentId,
|
|
60
|
+
bridgeCredential: result.credential,
|
|
61
|
+
privateKeyPem: result.identity.privateKeyPem,
|
|
62
|
+
publicKeyPem: result.identity.publicKeyPem,
|
|
63
|
+
});
|
|
64
|
+
await this.connectWithCredential({
|
|
65
|
+
agentId: result.agentId,
|
|
66
|
+
bridgeCredential: result.credential,
|
|
67
|
+
privateKeyPem: result.identity.privateKeyPem,
|
|
68
|
+
publicKeyPem: result.identity.publicKeyPem,
|
|
69
|
+
}, result.initialToken);
|
|
68
70
|
}
|
|
69
|
-
await this.ensureAuthenticatedTransport(false);
|
|
70
71
|
}
|
|
71
72
|
async stop() {
|
|
72
|
-
this.
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if (pairingSocket !== null) {
|
|
76
|
-
pairingSocket.close(1000, "pairing stopped");
|
|
73
|
+
if (this.pairingClient !== null) {
|
|
74
|
+
this.pairingClient.cancel();
|
|
75
|
+
this.pairingClient = null;
|
|
77
76
|
}
|
|
78
|
-
this.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
this.transport = null;
|
|
77
|
+
if (this.connection !== null) {
|
|
78
|
+
await this.connection.disconnect();
|
|
79
|
+
this.connection = null;
|
|
82
80
|
}
|
|
83
|
-
await Promise.allSettled(Array.from(this.pendingFrameTasks));
|
|
84
81
|
this.setState("stopped");
|
|
85
82
|
}
|
|
86
83
|
async regeneratePairingToken() {
|
|
87
|
-
if (this.state !== "pairing") {
|
|
84
|
+
if (this.state !== "pairing" || this.pairingClient === null) {
|
|
88
85
|
throw new Error(`NorityBridge.regeneratePairingToken: bridge must be in pairing state, got ${this.state}`);
|
|
89
86
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
this.pairingInfo = {
|
|
112
|
-
token,
|
|
113
|
-
deeplinkUrl: createDeeplinkUrl(token),
|
|
114
|
-
expiresAt: createPairingExpiry({
|
|
115
|
-
clock: this.config.clock,
|
|
116
|
-
tokenTtlSeconds: PAIRING_TOKEN_TTL_SECONDS,
|
|
117
|
-
}),
|
|
118
|
-
};
|
|
119
|
-
this.config.onPairingToken?.({ ...this.pairingInfo });
|
|
120
|
-
this.pairingTimer = setTimeout(() => {
|
|
121
|
-
void this.restartPairingFlow(state);
|
|
122
|
-
}, PAIRING_TOKEN_TTL_SECONDS * 1000);
|
|
123
|
-
const webSocketFactory = this.config.webSocketFactory;
|
|
124
|
-
if (webSocketFactory === undefined) {
|
|
125
|
-
throw new Error("NorityBridge.restartPairingFlow: webSocketFactory is required for pairing");
|
|
126
|
-
}
|
|
127
|
-
const socket = webSocketFactory.create(this.config.backendUrl, {});
|
|
128
|
-
this.pairingSocket = socket;
|
|
129
|
-
await waitForOpen(socket);
|
|
130
|
-
socket.addEventListener("message", (event) => {
|
|
131
|
-
void this.handlePairingMessage(event.data);
|
|
87
|
+
return this.pairingClient.regenerateToken();
|
|
88
|
+
}
|
|
89
|
+
async connectWithCredential(storedState, seedJwt) {
|
|
90
|
+
if (!storedState.bridgeCredential) {
|
|
91
|
+
throw new Error("NorityBridge: bridgeCredential is required");
|
|
92
|
+
}
|
|
93
|
+
this.connection = new BridgeConnection({
|
|
94
|
+
backendUrl: this.config.backendUrl,
|
|
95
|
+
dataDir: this.config.dataDir,
|
|
96
|
+
credential: storedState.bridgeCredential,
|
|
97
|
+
privateKeyPem: storedState.privateKeyPem,
|
|
98
|
+
agent: this.config.agent,
|
|
99
|
+
seedJwt,
|
|
100
|
+
logger: this.config.logger,
|
|
101
|
+
fetchImplementation: this.config.fetchImplementation,
|
|
102
|
+
webSocketFactory: this.config.webSocketFactory,
|
|
103
|
+
clock: this.config.clock,
|
|
104
|
+
backoffRandom: this.config.backoffRandom,
|
|
105
|
+
heartbeatIntervalMs: this.config.heartbeatIntervalMs,
|
|
106
|
+
idleTimeoutMs: this.config.idleTimeoutMs,
|
|
132
107
|
});
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
bridge_public_key: getEd25519PublicKeyBase64(state.publicKeyPem),
|
|
136
|
-
});
|
|
137
|
-
socket.send(JSON.stringify(pairRequest));
|
|
108
|
+
await this.connection.connect();
|
|
109
|
+
this.syncConnectionState();
|
|
138
110
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
try {
|
|
142
|
-
frame = JSON.parse(rawData);
|
|
143
|
-
}
|
|
144
|
-
catch (error) {
|
|
145
|
-
this.logger.warn("NorityBridge.handlePairingMessage: invalid JSON", error);
|
|
111
|
+
syncConnectionState() {
|
|
112
|
+
if (this.connection === null) {
|
|
146
113
|
return;
|
|
147
114
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
if (!payload.agent_id ||
|
|
153
|
-
!payload.bridge_credential ||
|
|
154
|
-
!payload.access_jwt ||
|
|
155
|
-
!payload.expires_at) {
|
|
156
|
-
throw new Error("NorityBridge.handlePairingMessage: paired payload missing required fields");
|
|
157
|
-
}
|
|
158
|
-
const currentState = this.getDurableState();
|
|
159
|
-
const nextState = {
|
|
160
|
-
...currentState,
|
|
161
|
-
agentId: payload.agent_id,
|
|
162
|
-
bridgeCredential: payload.bridge_credential,
|
|
163
|
-
};
|
|
164
|
-
await this.stateStore.save(nextState);
|
|
165
|
-
this.durableState = nextState;
|
|
166
|
-
this.accessTokenManager.seed({
|
|
167
|
-
accessJwt: payload.access_jwt,
|
|
168
|
-
agentId: payload.agent_id,
|
|
169
|
-
expiresAt: payload.expires_at,
|
|
170
|
-
});
|
|
171
|
-
this.clearPairingTimer();
|
|
172
|
-
const pairingSocket = this.pairingSocket;
|
|
173
|
-
this.pairingSocket = null;
|
|
174
|
-
if (pairingSocket !== null) {
|
|
175
|
-
pairingSocket.close(1000, "pairing completed");
|
|
176
|
-
}
|
|
177
|
-
await this.ensureAuthenticatedTransport(false);
|
|
178
|
-
}
|
|
179
|
-
async ensureAuthenticatedTransport(forceRefresh) {
|
|
180
|
-
const currentState = this.getDurableState();
|
|
181
|
-
if (!currentState.bridgeCredential) {
|
|
182
|
-
throw new Error("NorityBridge.ensureAuthenticatedTransport: bridgeCredential is required");
|
|
183
|
-
}
|
|
184
|
-
if (this.transport === null) {
|
|
185
|
-
this.transport = new AuthenticatedWebSocketTransport({
|
|
186
|
-
backendUrl: this.config.backendUrl,
|
|
187
|
-
webSocketFactory: this.config.webSocketFactory,
|
|
188
|
-
heartbeatIntervalMs: this.config.heartbeatIntervalMs,
|
|
189
|
-
idleTimeoutMs: this.config.idleTimeoutMs,
|
|
190
|
-
random: this.config.backoffRandom,
|
|
191
|
-
clock: this.config.clock,
|
|
192
|
-
logger: this.logger,
|
|
193
|
-
onFrame: async (frame) => {
|
|
194
|
-
await this.trackFrameTask(this.handleTransportFrame(frame));
|
|
195
|
-
},
|
|
196
|
-
onStateChange: (transportState) => {
|
|
197
|
-
this.handleTransportStateChange(transportState);
|
|
198
|
-
},
|
|
199
|
-
getAccessToken: async (refreshRequested = false) => {
|
|
200
|
-
const token = await this.accessTokenManager.getAccessToken({
|
|
201
|
-
credential: currentState.bridgeCredential ?? "",
|
|
202
|
-
privateKeyPem: currentState.privateKeyPem,
|
|
203
|
-
forceRefresh: refreshRequested,
|
|
204
|
-
});
|
|
205
|
-
return token.accessJwt;
|
|
206
|
-
},
|
|
207
|
-
});
|
|
208
|
-
}
|
|
209
|
-
await this.transport.connect(forceRefresh);
|
|
210
|
-
}
|
|
211
|
-
handleTransportStateChange(transportState) {
|
|
212
|
-
switch (transportState) {
|
|
213
|
-
case "connecting":
|
|
214
|
-
this.setState("connecting");
|
|
215
|
-
break;
|
|
216
|
-
case "connected":
|
|
217
|
-
this.setState("connected");
|
|
218
|
-
if (this.transport !== null) {
|
|
219
|
-
const replayRequest = this.conversationRuntime.createReplayRequestFrame();
|
|
220
|
-
if (replayRequest !== null) {
|
|
221
|
-
this.transport.send(replayRequest);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
break;
|
|
225
|
-
case "reconnecting":
|
|
226
|
-
this.setState("reconnecting");
|
|
227
|
-
break;
|
|
228
|
-
case "blocked":
|
|
229
|
-
this.setState("blocked");
|
|
230
|
-
break;
|
|
231
|
-
case "failed":
|
|
232
|
-
this.setState("failed");
|
|
233
|
-
break;
|
|
234
|
-
case "stopped":
|
|
235
|
-
if (this.state !== "stopped") {
|
|
236
|
-
this.setState("stopped");
|
|
237
|
-
}
|
|
238
|
-
break;
|
|
239
|
-
case "idle":
|
|
240
|
-
break;
|
|
241
|
-
default:
|
|
242
|
-
throw new Error(`NorityBridge.handleTransportStateChange: unsupported transport state ${transportState}`);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
getDurableState() {
|
|
246
|
-
if (this.durableState === null) {
|
|
247
|
-
throw new Error("NorityBridge: durable bridge state has not been loaded");
|
|
248
|
-
}
|
|
249
|
-
return this.durableState;
|
|
250
|
-
}
|
|
251
|
-
clearPairingTimer() {
|
|
252
|
-
if (this.pairingTimer !== null) {
|
|
253
|
-
clearTimeout(this.pairingTimer);
|
|
254
|
-
this.pairingTimer = null;
|
|
115
|
+
const connState = this.connection.getState();
|
|
116
|
+
const mapped = mapConnectionState(connState);
|
|
117
|
+
if (mapped !== null) {
|
|
118
|
+
this.setState(mapped);
|
|
255
119
|
}
|
|
256
120
|
}
|
|
257
121
|
setState(nextState) {
|
|
258
122
|
this.state = nextState;
|
|
259
123
|
}
|
|
260
|
-
async handleTransportFrame(frame) {
|
|
261
|
-
if (this.transport === null) {
|
|
262
|
-
throw new Error("NorityBridge.handleTransportFrame: transport must exist while handling frames");
|
|
263
|
-
}
|
|
264
|
-
await this.conversationRuntime.handleFrame(frame, (outboundFrame) => {
|
|
265
|
-
this.sendTransportFrame(outboundFrame);
|
|
266
|
-
});
|
|
267
|
-
}
|
|
268
|
-
async trackFrameTask(task) {
|
|
269
|
-
this.pendingFrameTasks.add(task);
|
|
270
|
-
try {
|
|
271
|
-
await task;
|
|
272
|
-
}
|
|
273
|
-
finally {
|
|
274
|
-
this.pendingFrameTasks.delete(task);
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
sendTransportFrame(outboundFrame) {
|
|
278
|
-
if (this.transport === null) {
|
|
279
|
-
throw new Error("NorityBridge.sendTransportFrame: transport must exist while sending outbound frames");
|
|
280
|
-
}
|
|
281
|
-
this.transport.send(outboundFrame);
|
|
282
|
-
}
|
|
283
124
|
}
|
|
284
|
-
function
|
|
285
|
-
|
|
286
|
-
|
|
125
|
+
function mapConnectionState(state) {
|
|
126
|
+
switch (state) {
|
|
127
|
+
case "connecting":
|
|
128
|
+
return "connecting";
|
|
129
|
+
case "connected":
|
|
130
|
+
return "connected";
|
|
131
|
+
case "reconnecting":
|
|
132
|
+
return "reconnecting";
|
|
133
|
+
case "blocked":
|
|
134
|
+
return "blocked";
|
|
135
|
+
case "failed":
|
|
136
|
+
return "failed";
|
|
137
|
+
case "stopped":
|
|
138
|
+
return "stopped";
|
|
139
|
+
case "idle":
|
|
140
|
+
return null;
|
|
141
|
+
default:
|
|
142
|
+
return null;
|
|
287
143
|
}
|
|
288
|
-
return new Promise((resolve, reject) => {
|
|
289
|
-
const handleOpen = () => {
|
|
290
|
-
cleanup();
|
|
291
|
-
resolve();
|
|
292
|
-
};
|
|
293
|
-
const handleError = () => {
|
|
294
|
-
cleanup();
|
|
295
|
-
reject(new Error("NorityBridge.waitForOpen: websocket error during pairing"));
|
|
296
|
-
};
|
|
297
|
-
const handleClose = () => {
|
|
298
|
-
cleanup();
|
|
299
|
-
reject(new Error("NorityBridge.waitForOpen: websocket closed before pairing socket opened"));
|
|
300
|
-
};
|
|
301
|
-
const cleanup = () => {
|
|
302
|
-
socket.removeEventListener("open", handleOpen);
|
|
303
|
-
socket.removeEventListener("error", handleError);
|
|
304
|
-
socket.removeEventListener("close", handleClose);
|
|
305
|
-
};
|
|
306
|
-
socket.addEventListener("open", handleOpen);
|
|
307
|
-
socket.addEventListener("error", handleError);
|
|
308
|
-
socket.addEventListener("close", handleClose);
|
|
309
|
-
});
|
|
310
144
|
}
|
|
311
145
|
//# sourceMappingURL=bridge.js.map
|