pi-harness-runtime 1.1.375 → 1.1.377
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 +68 -39
- package/package.json +2 -1
- package/packages/a2a-adapter/package.json +1 -1
- package/packages/agent-policy/package.json +1 -1
- package/packages/architecture-generator/package.json +1 -1
- package/packages/auth/package.json +1 -1
- package/packages/autonomous-refactor/package.json +1 -1
- package/packages/autonomous-runtime/package.json +1 -1
- package/packages/cache-strategy/package.json +1 -1
- package/packages/capability-registry/package.json +1 -1
- package/packages/checkpoint/package.json +1 -1
- package/packages/cli-plugin-sdk/package.json +1 -1
- package/packages/clipboard/package.json +1 -1
- package/packages/clipboard-plugin/package.json +1 -1
- package/packages/code-generation/package.json +1 -1
- package/packages/code-review/package.json +1 -1
- package/packages/codex-adapter/package.json +1 -1
- package/packages/codex-watcher/package.json +1 -1
- package/packages/config-capture/package.json +1 -1
- package/packages/connection-runtime/package.json +1 -1
- package/packages/context-compiler/package.json +1 -1
- package/packages/context-discovery/package.json +1 -1
- package/packages/context-manager/package.json +1 -1
- package/packages/contracts/package.json +1 -1
- package/packages/cookie-sanitizer/package.json +1 -1
- package/packages/cost-optimizer/package.json +1 -1
- package/packages/dependency-analyzer/package.json +1 -1
- package/packages/django-plugin/package.json +1 -1
- package/packages/doc-generator/package.json +1 -1
- package/packages/durable-review/package.json +1 -1
- package/packages/e2e-tester/package.json +1 -1
- package/packages/evaluation-engine/package.json +1 -1
- package/packages/evaluation-runner/package.json +1 -1
- package/packages/event-bus/package.json +1 -1
- package/packages/event-sink/package.json +1 -1
- package/packages/event-store/package.json +1 -1
- package/packages/experience-replay/package.json +1 -1
- package/packages/feedback-collector/package.json +1 -1
- package/packages/file-copy-helper/package.json +1 -1
- package/packages/framework-detector/package.json +1 -1
- package/packages/framework-plugin-sdk/package.json +1 -1
- package/packages/frappe-plugin/package.json +1 -1
- package/packages/generic-web-plugin/package.json +1 -1
- package/packages/health-monitor/package.json +1 -1
- package/packages/intent-analyzer/package.json +1 -1
- package/packages/jev-judge/package.json +1 -1
- package/packages/knowledge-graph/package.json +1 -1
- package/packages/knowledge-retrieval/package.json +1 -1
- package/packages/laravel-plugin/package.json +1 -1
- package/packages/learning-engine/package.json +1 -1
- package/packages/mcp-adapter/package.json +1 -1
- package/packages/memory-engine/package.json +1 -1
- package/packages/milestone-manager/package.json +1 -1
- package/packages/model-registry/package.json +1 -1
- package/packages/nextjs-plugin/package.json +1 -1
- package/packages/notification/package.json +1 -1
- package/packages/observability/package.json +1 -1
- package/packages/okf-indexer/package.json +1 -1
- package/packages/paperclip-integration/package.json +1 -1
- package/packages/performance-optimizer/package.json +1 -1
- package/packages/permission-mode/package.json +1 -1
- package/packages/privilege-broker/package.json +1 -1
- package/packages/project-analyzer/package.json +1 -1
- package/packages/project-bootstrap/package.json +1 -1
- package/packages/projection-engine/package.json +1 -1
- package/packages/prompt-compiler/package.json +1 -1
- package/packages/prompt-versioning/package.json +1 -1
- package/packages/provider-adapter-sdk/package.json +1 -1
- package/packages/provider-router/package.json +1 -1
- package/packages/provider-selector/package.json +1 -1
- package/packages/providers/package.json +1 -1
- package/packages/qdrant-skills/package.json +1 -1
- package/packages/quota-manager/package.json +1 -1
- package/packages/rate-limiter/package.json +1 -1
- package/packages/react-vite-plugin/package.json +1 -1
- package/packages/relay/dist/client.d.ts +56 -35
- package/packages/relay/dist/client.js +278 -71
- package/packages/relay/dist/index.d.ts +12 -5
- package/packages/relay/dist/index.js +12 -4
- package/packages/relay/dist/server.d.ts +58 -20
- package/packages/relay/dist/server.js +344 -49
- package/packages/relay/dist/types.d.ts +137 -1
- package/packages/relay/dist/types.js +21 -2
- package/packages/relay/dist/ws-server.d.ts +48 -0
- package/packages/relay/dist/ws-server.js +130 -0
- package/packages/relay/package.json +1 -1
- package/packages/relay/src/client.ts +375 -81
- package/packages/relay/src/index.ts +42 -5
- package/packages/relay/src/server.ts +452 -54
- package/packages/relay/src/types.ts +174 -1
- package/packages/relay/src/ws-server.ts +165 -0
- package/packages/release-manager/package.json +1 -1
- package/packages/requirement-compiler/package.json +1 -1
- package/packages/runtime/package.json +1 -1
- package/packages/scheduler/package.json +1 -1
- package/packages/scheduler-adapter/package.json +1 -1
- package/packages/session/package.json +1 -1
- package/packages/session-api/package.json +1 -1
- package/packages/session-export/package.json +1 -1
- package/packages/shared-context/package.json +1 -1
- package/packages/skill-mcp-client/package.json +1 -1
- package/packages/skill-registry/package.json +1 -1
- package/packages/skills/package.json +1 -1
- package/packages/sprint-planner/package.json +1 -1
- package/packages/subscription-engine/package.json +1 -1
- package/packages/task-compiler/package.json +1 -1
- package/packages/telegram/package.json +1 -1
- package/packages/tencentdb-memory/package.json +1 -1
- package/packages/tencentdb-sync/package.json +1 -1
- package/packages/test-data-generator/package.json +1 -1
- package/packages/test-generator/package.json +1 -1
- package/packages/todo-bd-sync/package.json +1 -1
- package/packages/token-estimation/package.json +1 -1
- package/packages/token-optimizer/package.json +1 -1
- package/packages/trajectory/package.json +1 -1
- package/packages/tui/package.json +1 -1
- package/packages/types/package.json +1 -1
- package/packages/voice-api/package.json +1 -1
- package/packages/webmcp-tester/package.json +1 -1
- package/packages/wiki/package.json +1 -1
- package/packages/workflow-events/package.json +1 -1
- package/packages/workspace-scanner/package.json +1 -1
- package/packages/worktree/package.json +1 -1
- package/packages/write-review/package.json +1 -1
- package/scripts/relay-client.ts +184 -0
- package/scripts/sync-callback-queue.ts +57 -0
- package/scripts/telegram-callback-processor.ts +16 -0
- package/scripts/unified-webhook-server.ts +160 -145
- package/scripts/ws-server.ts +81 -0
|
@@ -1,95 +1,302 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Relay Client -
|
|
3
|
-
*/
|
|
4
|
-
/**
|
|
5
|
-
* Generate a UUID v4
|
|
6
|
-
*/
|
|
7
|
-
function generateUUID() {
|
|
8
|
-
// Use crypto.randomUUID if available (Node.js 14.17+)
|
|
9
|
-
const crypto = globalThis;
|
|
10
|
-
if (crypto.crypto?.randomUUID) {
|
|
11
|
-
return crypto.crypto.randomUUID();
|
|
12
|
-
}
|
|
13
|
-
// Fallback: simple UUID generation
|
|
14
|
-
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
|
15
|
-
const r = (Math.random() * 16) | 0;
|
|
16
|
-
const v = c === 'x' ? r : (r & 0x3) | 0x8;
|
|
17
|
-
return v.toString(16);
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Relay Client
|
|
2
|
+
* Relay Client - Two-way streaming WebSocket client
|
|
22
3
|
*
|
|
23
|
-
*
|
|
4
|
+
* Implements t3code-style streaming with:
|
|
5
|
+
* - Connection state machine
|
|
6
|
+
* - Snapshot + live event handling
|
|
7
|
+
* - Typed message protocol
|
|
8
|
+
* - Automatic reconnection with backoff
|
|
24
9
|
*/
|
|
10
|
+
import WebSocket from "ws";
|
|
11
|
+
import { PROTOCOL_VERSION, createTimestamp, } from './types.js';
|
|
12
|
+
// ============================================================================
|
|
13
|
+
// Constants
|
|
14
|
+
// ============================================================================
|
|
15
|
+
const DEFAULT_RECONNECT_DELAY_MS = 1000;
|
|
16
|
+
const MAX_RECONNECT_DELAY_MS = 30000;
|
|
17
|
+
const HEARTBEAT_INTERVAL_MS = 30000;
|
|
18
|
+
const CONNECT_TIMEOUT_MS = 10000;
|
|
19
|
+
// ============================================================================
|
|
20
|
+
// Relay Client - Two-way Streaming Implementation
|
|
21
|
+
// ============================================================================
|
|
25
22
|
export class RelayClient {
|
|
23
|
+
ws = null;
|
|
26
24
|
config;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
callbacks;
|
|
26
|
+
// State machine
|
|
27
|
+
state = { state: 'disconnected' };
|
|
28
|
+
sessionId = null;
|
|
29
|
+
clientId = null;
|
|
30
|
+
// Reconnection
|
|
31
|
+
reconnectAttempt = 0;
|
|
32
|
+
reconnectDelay = DEFAULT_RECONNECT_DELAY_MS;
|
|
33
|
+
reconnectTimer = null;
|
|
34
|
+
connectTimeoutTimer = null;
|
|
35
|
+
// Heartbeat
|
|
36
|
+
heartbeatTimer = null;
|
|
37
|
+
// Snapshot tracking (t3code pattern)
|
|
38
|
+
snapshotReceived = false;
|
|
39
|
+
synchronizedReceived = false;
|
|
40
|
+
constructor(config, callbacks = {}) {
|
|
31
41
|
this.config = config;
|
|
32
|
-
this.
|
|
42
|
+
this.callbacks = callbacks;
|
|
43
|
+
}
|
|
44
|
+
// ========================================================================
|
|
45
|
+
// Connection State Machine
|
|
46
|
+
// ========================================================================
|
|
47
|
+
setState(newState) {
|
|
48
|
+
const previous = this.state;
|
|
49
|
+
this.state = newState;
|
|
50
|
+
this.callbacks.onStateChange?.({
|
|
51
|
+
previous,
|
|
52
|
+
current: newState,
|
|
53
|
+
timestamp: createTimestamp(),
|
|
54
|
+
});
|
|
33
55
|
}
|
|
56
|
+
isConnected() {
|
|
57
|
+
return this.ws?.readyState === WebSocket.OPEN &&
|
|
58
|
+
this.state.state === 'connected';
|
|
59
|
+
}
|
|
60
|
+
// ========================================================================
|
|
61
|
+
// Lifecycle
|
|
62
|
+
// ========================================================================
|
|
34
63
|
/**
|
|
35
|
-
* Connect to relay server
|
|
64
|
+
* Connect to relay server with automatic authentication
|
|
36
65
|
*/
|
|
37
66
|
async connect() {
|
|
38
|
-
if (this.
|
|
67
|
+
if (this.isConnected())
|
|
39
68
|
return;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
this.
|
|
48
|
-
|
|
49
|
-
|
|
69
|
+
// Clear any existing connection
|
|
70
|
+
this.cleanup();
|
|
71
|
+
this.setState({ state: 'connecting' });
|
|
72
|
+
const url = this.config.serverUrl;
|
|
73
|
+
console.log(`[RelayClient] Connecting to ${url}...`);
|
|
74
|
+
return new Promise((resolve, reject) => {
|
|
75
|
+
// Set connect timeout
|
|
76
|
+
this.connectTimeoutTimer = setTimeout(() => {
|
|
77
|
+
if (this.state.state === 'connecting') {
|
|
78
|
+
this.ws?.close();
|
|
79
|
+
reject(new Error('Connection timeout'));
|
|
80
|
+
}
|
|
81
|
+
}, CONNECT_TIMEOUT_MS);
|
|
82
|
+
this.ws = new WebSocket(url);
|
|
83
|
+
this.ws.on("open", () => {
|
|
84
|
+
console.log("[RelayClient] WebSocket opened");
|
|
85
|
+
this.setState({ state: 'authenticating' });
|
|
86
|
+
// Send authentication
|
|
87
|
+
this.send({
|
|
88
|
+
version: PROTOCOL_VERSION,
|
|
89
|
+
type: 'auth',
|
|
90
|
+
payload: { token: this.config.token || '' },
|
|
91
|
+
timestamp: createTimestamp(),
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
this.ws.on("message", (data) => {
|
|
95
|
+
this.handleMessage(data.toString());
|
|
96
|
+
});
|
|
97
|
+
this.ws.on("close", () => {
|
|
98
|
+
console.log("[RelayClient] WebSocket closed");
|
|
99
|
+
this.handleDisconnect();
|
|
100
|
+
});
|
|
101
|
+
this.ws.on("error", (err) => {
|
|
102
|
+
console.error("[RelayClient] WebSocket error:", err.message);
|
|
103
|
+
this.callbacks.onError?.(err);
|
|
104
|
+
if (this.state.state === 'connecting' || this.state.state === 'authenticating') {
|
|
105
|
+
clearTimeout(this.connectTimeoutTimer);
|
|
106
|
+
reject(err);
|
|
107
|
+
}
|
|
108
|
+
this.handleDisconnect();
|
|
109
|
+
});
|
|
110
|
+
});
|
|
50
111
|
}
|
|
51
112
|
/**
|
|
52
113
|
* Disconnect from relay server
|
|
53
114
|
*/
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
this.
|
|
57
|
-
this.
|
|
115
|
+
disconnect() {
|
|
116
|
+
console.log("[RelayClient] Disconnecting...");
|
|
117
|
+
this.cleanup();
|
|
118
|
+
this.setState({ state: 'disconnected' });
|
|
119
|
+
this.callbacks.onDisconnected?.();
|
|
58
120
|
}
|
|
59
121
|
/**
|
|
60
|
-
*
|
|
122
|
+
* Send a message through the WebSocket
|
|
61
123
|
*/
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
localPort,
|
|
67
|
-
status: this.connected ? 'connected' : 'disconnected',
|
|
68
|
-
createdAt: new Date().toISOString(),
|
|
69
|
-
};
|
|
70
|
-
this.tunnel = tunnel;
|
|
71
|
-
this.options.onTunnel?.(tunnel);
|
|
72
|
-
return tunnel;
|
|
124
|
+
send(message) {
|
|
125
|
+
if (this.isConnected()) {
|
|
126
|
+
this.ws.send(JSON.stringify(message));
|
|
127
|
+
}
|
|
73
128
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
129
|
+
// ========================================================================
|
|
130
|
+
// Message Handling
|
|
131
|
+
// ========================================================================
|
|
132
|
+
handleMessage(data) {
|
|
133
|
+
try {
|
|
134
|
+
const msg = JSON.parse(data);
|
|
135
|
+
switch (msg.type) {
|
|
136
|
+
case 'connected':
|
|
137
|
+
this.handleConnected(msg);
|
|
138
|
+
break;
|
|
139
|
+
case 'auth':
|
|
140
|
+
this.handleAuth(msg);
|
|
141
|
+
break;
|
|
142
|
+
case 'snapshot':
|
|
143
|
+
this.handleSnapshot(msg);
|
|
144
|
+
break;
|
|
145
|
+
case 'synchronized':
|
|
146
|
+
this.handleSynchronized(msg);
|
|
147
|
+
break;
|
|
148
|
+
case 'callback':
|
|
149
|
+
this.handleCallback(msg);
|
|
150
|
+
break;
|
|
151
|
+
case 'status':
|
|
152
|
+
this.handleStatus(msg);
|
|
153
|
+
break;
|
|
154
|
+
case 'pong':
|
|
155
|
+
// Heartbeat response - connection is alive
|
|
156
|
+
break;
|
|
157
|
+
default: {
|
|
158
|
+
const unknownMsg = msg;
|
|
159
|
+
console.warn(`[RelayClient] Unknown message type: ${unknownMsg.type}`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
catch (err) {
|
|
164
|
+
console.error("[RelayClient] Failed to parse message:", err);
|
|
165
|
+
this.callbacks.onError?.(err);
|
|
80
166
|
}
|
|
81
|
-
// In a real implementation, this would send the message over WebSocket
|
|
82
167
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
isConnected() {
|
|
87
|
-
return this.connected;
|
|
168
|
+
handleConnected(msg) {
|
|
169
|
+
this.clientId = msg.payload.clientId;
|
|
170
|
+
console.log(`[RelayClient] Connected, clientId: ${this.clientId}`);
|
|
88
171
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
172
|
+
handleAuth(msg) {
|
|
173
|
+
if (msg.payload.success) {
|
|
174
|
+
this.sessionId = msg.payload.sessionId || null;
|
|
175
|
+
this.setState({ state: 'connected', sessionId: this.sessionId || '' });
|
|
176
|
+
// Reset reconnection state
|
|
177
|
+
this.reconnectAttempt = 0;
|
|
178
|
+
this.reconnectDelay = DEFAULT_RECONNECT_DELAY_MS;
|
|
179
|
+
// Start heartbeat
|
|
180
|
+
this.startHeartbeat();
|
|
181
|
+
// Notify callback
|
|
182
|
+
this.callbacks.onConnected?.(this.sessionId || '');
|
|
183
|
+
console.log(`[RelayClient] Authenticated, sessionId: ${this.sessionId}`);
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
console.error(`[RelayClient] Auth failed: ${msg.payload.error}`);
|
|
187
|
+
this.callbacks.onError?.(new Error(msg.payload.error || 'Authentication failed'));
|
|
188
|
+
this.disconnect();
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
handleSnapshot(msg) {
|
|
192
|
+
console.log(`[RelayClient] Snapshot received: ${msg.payload.recentCallbacks.length} recent callbacks`);
|
|
193
|
+
this.snapshotReceived = true;
|
|
194
|
+
// Notify callback
|
|
195
|
+
this.callbacks.onSnapshot?.(msg.payload);
|
|
196
|
+
}
|
|
197
|
+
handleSynchronized(_msg) {
|
|
198
|
+
console.log("[RelayClient] Synchronized - live events starting");
|
|
199
|
+
this.synchronizedReceived = true;
|
|
200
|
+
// Notify callback
|
|
201
|
+
this.callbacks.onSynchronized?.();
|
|
202
|
+
}
|
|
203
|
+
handleCallback(msg) {
|
|
204
|
+
// Only process callbacks after synchronized
|
|
205
|
+
if (!this.synchronizedReceived) {
|
|
206
|
+
console.warn("[RelayClient] Ignoring callback before synchronized");
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
this.callbacks.onCallback?.(msg.payload);
|
|
210
|
+
}
|
|
211
|
+
handleStatus(msg) {
|
|
212
|
+
console.log(`[RelayClient] Status [${msg.payload.kind}]: ${msg.payload.code} - ${msg.payload.message}`);
|
|
213
|
+
this.callbacks.onStatus?.(msg.payload);
|
|
214
|
+
}
|
|
215
|
+
// ========================================================================
|
|
216
|
+
// Disconnect & Reconnect
|
|
217
|
+
// ========================================================================
|
|
218
|
+
handleDisconnect() {
|
|
219
|
+
this.stopHeartbeat();
|
|
220
|
+
const wasConnected = this.state.state === 'connected';
|
|
221
|
+
this.setState({ state: 'disconnected' });
|
|
222
|
+
if (wasConnected) {
|
|
223
|
+
this.callbacks.onDisconnected?.();
|
|
224
|
+
this.scheduleReconnect();
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
scheduleReconnect() {
|
|
228
|
+
if (this.reconnectTimer)
|
|
229
|
+
return;
|
|
230
|
+
this.reconnectAttempt++;
|
|
231
|
+
this.setState({ state: 'reconnecting', attempt: this.reconnectAttempt });
|
|
232
|
+
console.log(`[RelayClient] Scheduling reconnect attempt ${this.reconnectAttempt} in ${this.reconnectDelay}ms`);
|
|
233
|
+
this.reconnectTimer = setTimeout(() => {
|
|
234
|
+
this.reconnectTimer = null;
|
|
235
|
+
this.connect().catch((err) => {
|
|
236
|
+
console.error(`[RelayClient] Reconnect failed: ${err.message}`);
|
|
237
|
+
});
|
|
238
|
+
}, this.reconnectDelay);
|
|
239
|
+
// Exponential backoff
|
|
240
|
+
this.reconnectDelay = Math.min(this.reconnectDelay * 2, MAX_RECONNECT_DELAY_MS);
|
|
241
|
+
}
|
|
242
|
+
// ========================================================================
|
|
243
|
+
// Heartbeat
|
|
244
|
+
// ========================================================================
|
|
245
|
+
startHeartbeat() {
|
|
246
|
+
this.stopHeartbeat();
|
|
247
|
+
this.heartbeatTimer = setInterval(() => {
|
|
248
|
+
if (this.isConnected()) {
|
|
249
|
+
this.send({
|
|
250
|
+
version: PROTOCOL_VERSION,
|
|
251
|
+
type: 'ping',
|
|
252
|
+
payload: {},
|
|
253
|
+
timestamp: createTimestamp(),
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}, HEARTBEAT_INTERVAL_MS);
|
|
257
|
+
}
|
|
258
|
+
stopHeartbeat() {
|
|
259
|
+
if (this.heartbeatTimer) {
|
|
260
|
+
clearInterval(this.heartbeatTimer);
|
|
261
|
+
this.heartbeatTimer = null;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
// ========================================================================
|
|
265
|
+
// Cleanup
|
|
266
|
+
// ========================================================================
|
|
267
|
+
cleanup() {
|
|
268
|
+
// Clear timers
|
|
269
|
+
if (this.reconnectTimer) {
|
|
270
|
+
clearTimeout(this.reconnectTimer);
|
|
271
|
+
this.reconnectTimer = null;
|
|
272
|
+
}
|
|
273
|
+
if (this.connectTimeoutTimer) {
|
|
274
|
+
clearTimeout(this.connectTimeoutTimer);
|
|
275
|
+
this.connectTimeoutTimer = null;
|
|
276
|
+
}
|
|
277
|
+
this.stopHeartbeat();
|
|
278
|
+
// Reset snapshot state
|
|
279
|
+
this.snapshotReceived = false;
|
|
280
|
+
this.synchronizedReceived = false;
|
|
281
|
+
// Close WebSocket
|
|
282
|
+
if (this.ws) {
|
|
283
|
+
this.ws.close();
|
|
284
|
+
this.ws = null;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
// ========================================================================
|
|
288
|
+
// Public API
|
|
289
|
+
// ========================================================================
|
|
290
|
+
getState() {
|
|
291
|
+
return this.state;
|
|
292
|
+
}
|
|
293
|
+
isReady() {
|
|
294
|
+
return this.isConnected() && this.synchronizedReceived;
|
|
295
|
+
}
|
|
296
|
+
getSessionId() {
|
|
297
|
+
return this.sessionId;
|
|
298
|
+
}
|
|
299
|
+
getClientId() {
|
|
300
|
+
return this.clientId;
|
|
94
301
|
}
|
|
95
302
|
}
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Cloud Relay -
|
|
2
|
+
* Cloud Relay - Two-way Streaming Communication
|
|
3
3
|
*
|
|
4
|
-
* Enables
|
|
5
|
-
* Based on T3Code's
|
|
4
|
+
* Enables real-time two-way communication with pi-harness without router/port forwarding.
|
|
5
|
+
* Based on T3Code's streaming architecture:
|
|
6
|
+
* - Snapshot + Live event pattern
|
|
7
|
+
* - Typed message protocol
|
|
8
|
+
* - Connection state machine
|
|
9
|
+
* - Stream coalescing
|
|
6
10
|
*/
|
|
7
|
-
export { RelayServer } from './server.js';
|
|
11
|
+
export { RelayServer, createRelayServer } from './server.js';
|
|
12
|
+
export type { RelayServerOptions } from './server.js';
|
|
8
13
|
export { RelayClient } from './client.js';
|
|
9
|
-
export {
|
|
14
|
+
export type { RelayClientCallbacks } from './client.js';
|
|
15
|
+
export { PROTOCOL_VERSION, generateMessageId, createTimestamp, } from './types.js';
|
|
16
|
+
export type { RelayConfig, RelayTunnel, ConnectionState, ConnectionStateChange, BaseMessage, SnapshotMessage, SynchronizedMessage, CallbackPayload, CallbackMessage, StatusKind, StatusMessage, PingMessage, PongMessage, AuthRequestMessage, AuthResponseMessage, SubscribeMessage, ConnectedMessage, ServerMessage, ClientMessage, RelayMessage, } from './types.js';
|
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Cloud Relay -
|
|
2
|
+
* Cloud Relay - Two-way Streaming Communication
|
|
3
3
|
*
|
|
4
|
-
* Enables
|
|
5
|
-
* Based on T3Code's
|
|
4
|
+
* Enables real-time two-way communication with pi-harness without router/port forwarding.
|
|
5
|
+
* Based on T3Code's streaming architecture:
|
|
6
|
+
* - Snapshot + Live event pattern
|
|
7
|
+
* - Typed message protocol
|
|
8
|
+
* - Connection state machine
|
|
9
|
+
* - Stream coalescing
|
|
6
10
|
*/
|
|
7
|
-
|
|
11
|
+
// Server
|
|
12
|
+
export { RelayServer, createRelayServer } from './server.js';
|
|
13
|
+
// Client
|
|
8
14
|
export { RelayClient } from './client.js';
|
|
15
|
+
// Types
|
|
16
|
+
export { PROTOCOL_VERSION, generateMessageId, createTimestamp, } from './types.js';
|
|
@@ -1,58 +1,96 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Relay Server -
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
*
|
|
2
|
+
* Relay Server - Two-way streaming WebSocket relay
|
|
3
|
+
*
|
|
4
|
+
* Implements t3code-style streaming with:
|
|
5
|
+
* - Snapshot + Live event pattern
|
|
6
|
+
* - Typed message protocol
|
|
7
|
+
* - Connection state machine
|
|
8
|
+
* - Stream coalescing
|
|
7
9
|
*/
|
|
10
|
+
import { type RelayTunnel, type RelayMessage, type ConnectionState, type ConnectionStateChange, type CallbackPayload } from './types.js';
|
|
8
11
|
export interface RelayServerOptions {
|
|
9
12
|
/** Port to listen on */
|
|
10
13
|
port?: number;
|
|
11
14
|
/** Host to bind */
|
|
12
15
|
host?: string;
|
|
16
|
+
/** Authentication token */
|
|
17
|
+
authToken?: string;
|
|
18
|
+
/** Server version string */
|
|
19
|
+
serverVersion?: string;
|
|
13
20
|
/** Called when a tunnel connects */
|
|
14
21
|
onTunnelConnect?: (tunnel: RelayTunnel) => void;
|
|
15
22
|
/** Called when a tunnel disconnects */
|
|
16
23
|
onTunnelDisconnect?: (tunnelId: string) => void;
|
|
17
24
|
/** Called when a message is received */
|
|
18
25
|
onMessage?: (tunnelId: string, message: RelayMessage) => void;
|
|
26
|
+
/** Called on state change */
|
|
27
|
+
onStateChange?: (change: ConnectionStateChange) => void;
|
|
19
28
|
}
|
|
20
|
-
/**
|
|
21
|
-
* Relay Server
|
|
22
|
-
*
|
|
23
|
-
* Manages tunnels and relays messages between clients.
|
|
24
|
-
*/
|
|
25
29
|
export declare class RelayServer {
|
|
30
|
+
private server;
|
|
31
|
+
private wss;
|
|
32
|
+
private clients;
|
|
26
33
|
private tunnels;
|
|
27
34
|
private options;
|
|
28
35
|
private running;
|
|
36
|
+
private serverSessionId;
|
|
37
|
+
private eventBuffer;
|
|
38
|
+
private recentCallbacks;
|
|
29
39
|
constructor(options?: RelayServerOptions);
|
|
30
40
|
/**
|
|
31
|
-
* Start the
|
|
41
|
+
* Start the WebSocket server
|
|
32
42
|
*/
|
|
33
43
|
start(): Promise<void>;
|
|
34
44
|
/**
|
|
35
45
|
* Stop the relay server
|
|
36
46
|
*/
|
|
37
47
|
stop(): Promise<void>;
|
|
48
|
+
private updateState;
|
|
49
|
+
private handleMessage;
|
|
50
|
+
private handleAuth;
|
|
51
|
+
private handlePing;
|
|
52
|
+
private handleSubscribe;
|
|
53
|
+
/**
|
|
54
|
+
* Send initial snapshot followed by synchronized marker
|
|
55
|
+
* This is the t3code pattern: snapshot -> synchronized -> live events
|
|
56
|
+
*/
|
|
57
|
+
private sendSnapshot;
|
|
58
|
+
/**
|
|
59
|
+
* Flush event buffer and send batched callbacks
|
|
60
|
+
*/
|
|
61
|
+
private flushEventBuffer;
|
|
62
|
+
/**
|
|
63
|
+
* Broadcast callback to all connected clients
|
|
64
|
+
* Uses stream batching for high-frequency events
|
|
65
|
+
*/
|
|
66
|
+
broadcastCallback(callback: CallbackPayload, batch?: boolean): void;
|
|
38
67
|
/**
|
|
39
|
-
*
|
|
68
|
+
* Send status to all clients
|
|
40
69
|
*/
|
|
41
|
-
|
|
70
|
+
broadcastStatus(kind: 'info' | 'warning' | 'error', code: string, message: string): void;
|
|
42
71
|
/**
|
|
43
|
-
*
|
|
72
|
+
* Broadcast to all subscribed clients
|
|
44
73
|
*/
|
|
45
|
-
|
|
74
|
+
private broadcast;
|
|
46
75
|
/**
|
|
47
|
-
*
|
|
76
|
+
* Check if client is connected and authenticated
|
|
48
77
|
*/
|
|
49
|
-
|
|
78
|
+
private isConnected;
|
|
50
79
|
/**
|
|
51
|
-
*
|
|
80
|
+
* Send a typed message to a client
|
|
52
81
|
*/
|
|
53
|
-
|
|
82
|
+
private sendMessage;
|
|
54
83
|
/**
|
|
55
|
-
*
|
|
84
|
+
* Send status to specific client
|
|
56
85
|
*/
|
|
86
|
+
private sendStatus;
|
|
57
87
|
isRunning(): boolean;
|
|
88
|
+
getClientCount(): number;
|
|
89
|
+
getClients(): Array<{
|
|
90
|
+
id: string;
|
|
91
|
+
state: ConnectionState;
|
|
92
|
+
connectedAt: string;
|
|
93
|
+
}>;
|
|
94
|
+
getRecentCallbacks(limit?: number): CallbackPayload[];
|
|
58
95
|
}
|
|
96
|
+
export declare function createRelayServer(options?: RelayServerOptions): RelayServer;
|