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.
Files changed (129) hide show
  1. package/README.md +68 -39
  2. package/package.json +2 -1
  3. package/packages/a2a-adapter/package.json +1 -1
  4. package/packages/agent-policy/package.json +1 -1
  5. package/packages/architecture-generator/package.json +1 -1
  6. package/packages/auth/package.json +1 -1
  7. package/packages/autonomous-refactor/package.json +1 -1
  8. package/packages/autonomous-runtime/package.json +1 -1
  9. package/packages/cache-strategy/package.json +1 -1
  10. package/packages/capability-registry/package.json +1 -1
  11. package/packages/checkpoint/package.json +1 -1
  12. package/packages/cli-plugin-sdk/package.json +1 -1
  13. package/packages/clipboard/package.json +1 -1
  14. package/packages/clipboard-plugin/package.json +1 -1
  15. package/packages/code-generation/package.json +1 -1
  16. package/packages/code-review/package.json +1 -1
  17. package/packages/codex-adapter/package.json +1 -1
  18. package/packages/codex-watcher/package.json +1 -1
  19. package/packages/config-capture/package.json +1 -1
  20. package/packages/connection-runtime/package.json +1 -1
  21. package/packages/context-compiler/package.json +1 -1
  22. package/packages/context-discovery/package.json +1 -1
  23. package/packages/context-manager/package.json +1 -1
  24. package/packages/contracts/package.json +1 -1
  25. package/packages/cookie-sanitizer/package.json +1 -1
  26. package/packages/cost-optimizer/package.json +1 -1
  27. package/packages/dependency-analyzer/package.json +1 -1
  28. package/packages/django-plugin/package.json +1 -1
  29. package/packages/doc-generator/package.json +1 -1
  30. package/packages/durable-review/package.json +1 -1
  31. package/packages/e2e-tester/package.json +1 -1
  32. package/packages/evaluation-engine/package.json +1 -1
  33. package/packages/evaluation-runner/package.json +1 -1
  34. package/packages/event-bus/package.json +1 -1
  35. package/packages/event-sink/package.json +1 -1
  36. package/packages/event-store/package.json +1 -1
  37. package/packages/experience-replay/package.json +1 -1
  38. package/packages/feedback-collector/package.json +1 -1
  39. package/packages/file-copy-helper/package.json +1 -1
  40. package/packages/framework-detector/package.json +1 -1
  41. package/packages/framework-plugin-sdk/package.json +1 -1
  42. package/packages/frappe-plugin/package.json +1 -1
  43. package/packages/generic-web-plugin/package.json +1 -1
  44. package/packages/health-monitor/package.json +1 -1
  45. package/packages/intent-analyzer/package.json +1 -1
  46. package/packages/jev-judge/package.json +1 -1
  47. package/packages/knowledge-graph/package.json +1 -1
  48. package/packages/knowledge-retrieval/package.json +1 -1
  49. package/packages/laravel-plugin/package.json +1 -1
  50. package/packages/learning-engine/package.json +1 -1
  51. package/packages/mcp-adapter/package.json +1 -1
  52. package/packages/memory-engine/package.json +1 -1
  53. package/packages/milestone-manager/package.json +1 -1
  54. package/packages/model-registry/package.json +1 -1
  55. package/packages/nextjs-plugin/package.json +1 -1
  56. package/packages/notification/package.json +1 -1
  57. package/packages/observability/package.json +1 -1
  58. package/packages/okf-indexer/package.json +1 -1
  59. package/packages/paperclip-integration/package.json +1 -1
  60. package/packages/performance-optimizer/package.json +1 -1
  61. package/packages/permission-mode/package.json +1 -1
  62. package/packages/privilege-broker/package.json +1 -1
  63. package/packages/project-analyzer/package.json +1 -1
  64. package/packages/project-bootstrap/package.json +1 -1
  65. package/packages/projection-engine/package.json +1 -1
  66. package/packages/prompt-compiler/package.json +1 -1
  67. package/packages/prompt-versioning/package.json +1 -1
  68. package/packages/provider-adapter-sdk/package.json +1 -1
  69. package/packages/provider-router/package.json +1 -1
  70. package/packages/provider-selector/package.json +1 -1
  71. package/packages/providers/package.json +1 -1
  72. package/packages/qdrant-skills/package.json +1 -1
  73. package/packages/quota-manager/package.json +1 -1
  74. package/packages/rate-limiter/package.json +1 -1
  75. package/packages/react-vite-plugin/package.json +1 -1
  76. package/packages/relay/dist/client.d.ts +56 -35
  77. package/packages/relay/dist/client.js +278 -71
  78. package/packages/relay/dist/index.d.ts +12 -5
  79. package/packages/relay/dist/index.js +12 -4
  80. package/packages/relay/dist/server.d.ts +58 -20
  81. package/packages/relay/dist/server.js +344 -49
  82. package/packages/relay/dist/types.d.ts +137 -1
  83. package/packages/relay/dist/types.js +21 -2
  84. package/packages/relay/dist/ws-server.d.ts +48 -0
  85. package/packages/relay/dist/ws-server.js +130 -0
  86. package/packages/relay/package.json +1 -1
  87. package/packages/relay/src/client.ts +375 -81
  88. package/packages/relay/src/index.ts +42 -5
  89. package/packages/relay/src/server.ts +452 -54
  90. package/packages/relay/src/types.ts +174 -1
  91. package/packages/relay/src/ws-server.ts +165 -0
  92. package/packages/release-manager/package.json +1 -1
  93. package/packages/requirement-compiler/package.json +1 -1
  94. package/packages/runtime/package.json +1 -1
  95. package/packages/scheduler/package.json +1 -1
  96. package/packages/scheduler-adapter/package.json +1 -1
  97. package/packages/session/package.json +1 -1
  98. package/packages/session-api/package.json +1 -1
  99. package/packages/session-export/package.json +1 -1
  100. package/packages/shared-context/package.json +1 -1
  101. package/packages/skill-mcp-client/package.json +1 -1
  102. package/packages/skill-registry/package.json +1 -1
  103. package/packages/skills/package.json +1 -1
  104. package/packages/sprint-planner/package.json +1 -1
  105. package/packages/subscription-engine/package.json +1 -1
  106. package/packages/task-compiler/package.json +1 -1
  107. package/packages/telegram/package.json +1 -1
  108. package/packages/tencentdb-memory/package.json +1 -1
  109. package/packages/tencentdb-sync/package.json +1 -1
  110. package/packages/test-data-generator/package.json +1 -1
  111. package/packages/test-generator/package.json +1 -1
  112. package/packages/todo-bd-sync/package.json +1 -1
  113. package/packages/token-estimation/package.json +1 -1
  114. package/packages/token-optimizer/package.json +1 -1
  115. package/packages/trajectory/package.json +1 -1
  116. package/packages/tui/package.json +1 -1
  117. package/packages/types/package.json +1 -1
  118. package/packages/voice-api/package.json +1 -1
  119. package/packages/webmcp-tester/package.json +1 -1
  120. package/packages/wiki/package.json +1 -1
  121. package/packages/workflow-events/package.json +1 -1
  122. package/packages/workspace-scanner/package.json +1 -1
  123. package/packages/worktree/package.json +1 -1
  124. package/packages/write-review/package.json +1 -1
  125. package/scripts/relay-client.ts +184 -0
  126. package/scripts/sync-callback-queue.ts +57 -0
  127. package/scripts/telegram-callback-processor.ts +16 -0
  128. package/scripts/unified-webhook-server.ts +160 -145
  129. package/scripts/ws-server.ts +81 -0
@@ -1,93 +1,388 @@
1
1
  /**
2
- * Relay Server - Cloud relay server for tunnels
3
- */
4
- /**
5
- * Generate a UUID v4
6
- */
7
- function generateUUID() {
8
- // Use crypto.randomUUID if available
9
- const crypto = globalThis;
10
- if (crypto.crypto?.randomUUID) {
11
- return crypto.crypto.randomUUID();
12
- }
13
- // Fallback
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 Server
2
+ * Relay Server - Two-way streaming WebSocket relay
22
3
  *
23
- * Manages tunnels and relays messages between clients.
4
+ * Implements t3code-style streaming with:
5
+ * - Snapshot + Live event pattern
6
+ * - Typed message protocol
7
+ * - Connection state machine
8
+ * - Stream coalescing
24
9
  */
10
+ import { createServer } from "node:http";
11
+ import { WebSocketServer, WebSocket } from "ws";
12
+ import { randomUUID } from "node:crypto";
13
+ import { PROTOCOL_VERSION, generateMessageId, createTimestamp, } from './types.js';
14
+ // ============================================================================
15
+ // Constants
16
+ // ============================================================================
17
+ const DEFAULT_PORT = 8444;
18
+ const DEFAULT_HOST = "0.0.0.0";
19
+ const MAX_RECENT_CALLBACKS = 100;
20
+ const STREAM_BATCH_SIZE = 50; // Batch events for coalescing
21
+ const STREAM_BATCH_TIMEOUT_MS = 100; // Flush batch after this delay
22
+ function createEventBuffer() {
23
+ return {
24
+ callbacks: [],
25
+ timer: null,
26
+ };
27
+ }
28
+ // ============================================================================
29
+ // Relay Server - Two-way Streaming Implementation
30
+ // ============================================================================
25
31
  export class RelayServer {
32
+ server = null;
33
+ wss = null;
34
+ clients = new Map();
26
35
  tunnels = new Map();
27
36
  options;
28
37
  running = false;
38
+ serverSessionId;
39
+ // Event stream buffering for coalescing
40
+ eventBuffer = createEventBuffer();
41
+ // Recent callbacks for snapshot
42
+ recentCallbacks = [];
29
43
  constructor(options = {}) {
44
+ this.serverSessionId = randomUUID();
30
45
  this.options = {
31
- port: options.port ?? 3001,
32
- host: options.host ?? '0.0.0.0',
46
+ port: options.port ?? DEFAULT_PORT,
47
+ host: options.host ?? DEFAULT_HOST,
48
+ authToken: options.authToken ?? "change-me-in-production",
49
+ serverVersion: options.serverVersion ?? "1.0.0",
33
50
  onTunnelConnect: options.onTunnelConnect ?? (() => { }),
34
51
  onTunnelDisconnect: options.onTunnelDisconnect ?? (() => { }),
35
52
  onMessage: options.onMessage ?? (() => { }),
53
+ onStateChange: options.onStateChange ?? (() => { }),
36
54
  };
37
55
  }
56
+ // ========================================================================
57
+ // Lifecycle
58
+ // ========================================================================
38
59
  /**
39
- * Start the relay server
60
+ * Start the WebSocket server
40
61
  */
41
62
  async start() {
42
63
  if (this.running)
43
64
  return;
44
- this.running = true;
45
- // Note: Actual HTTP/WebSocket server would be implemented here
46
- // For now, this is a stub that can be expanded with actual server code
65
+ this.server = createServer();
66
+ this.wss = new WebSocketServer({ server: this.server });
67
+ this.wss.on("connection", (ws, req) => {
68
+ const clientId = randomUUID();
69
+ const clientIp = req.socket.remoteAddress;
70
+ console.log(`[RelayServer] Client connecting: ${clientId} from ${clientIp}`);
71
+ // Create client session
72
+ const session = {
73
+ id: clientId,
74
+ ws,
75
+ state: { state: 'connecting' },
76
+ subscriptions: new Set(['callback', 'status', 'sync']),
77
+ connectedAt: createTimestamp(),
78
+ lastPing: createTimestamp(),
79
+ pendingSnapshot: true,
80
+ };
81
+ this.clients.set(clientId, session);
82
+ this.updateState(session, { state: 'connecting' });
83
+ // Handle incoming messages
84
+ ws.on("message", (data) => {
85
+ this.handleMessage(clientId, data);
86
+ });
87
+ ws.on("close", () => {
88
+ this.clients.delete(clientId);
89
+ console.log(`[RelayServer] Client disconnected: ${clientId}`);
90
+ });
91
+ ws.on("error", (err) => {
92
+ console.error(`[RelayServer] Client error: ${clientId}`, err.message);
93
+ this.clients.delete(clientId);
94
+ });
95
+ // Send initial connected message
96
+ this.sendMessage(session, {
97
+ version: PROTOCOL_VERSION,
98
+ type: 'connected',
99
+ payload: {
100
+ clientId,
101
+ serverTime: createTimestamp(),
102
+ },
103
+ timestamp: createTimestamp(),
104
+ });
105
+ // Auto-auth for now (can be changed to require explicit auth)
106
+ this.updateState(session, { state: 'authenticating' });
107
+ this.handleAuth(clientId, this.options.authToken);
108
+ });
109
+ return new Promise((resolve, reject) => {
110
+ this.server.listen(this.options.port, this.options.host, () => {
111
+ this.running = true;
112
+ console.log(`[RelayServer] Server started on ${this.options.host}:${this.options.port}`);
113
+ resolve();
114
+ });
115
+ this.server.on("error", (err) => {
116
+ console.error("[RelayServer] Server error:", err);
117
+ reject(err);
118
+ });
119
+ });
47
120
  }
48
121
  /**
49
122
  * Stop the relay server
50
123
  */
51
124
  async stop() {
52
- this.running = false;
53
- this.tunnels.clear();
125
+ if (!this.running)
126
+ return;
127
+ // Close all client connections
128
+ for (const session of this.clients.values()) {
129
+ session.ws.close();
130
+ }
131
+ this.clients.clear();
132
+ // Close event buffer timer
133
+ if (this.eventBuffer.timer) {
134
+ clearTimeout(this.eventBuffer.timer);
135
+ }
136
+ return new Promise((resolve) => {
137
+ this.server.close(() => {
138
+ this.running = false;
139
+ console.log("[RelayServer] Server stopped");
140
+ resolve();
141
+ });
142
+ });
143
+ }
144
+ // ========================================================================
145
+ // State Management
146
+ // ========================================================================
147
+ updateState(session, newState) {
148
+ const previous = session.state;
149
+ session.state = newState;
150
+ this.options.onStateChange({
151
+ previous,
152
+ current: newState,
153
+ timestamp: createTimestamp(),
154
+ });
155
+ }
156
+ // ========================================================================
157
+ // Message Handling
158
+ // ========================================================================
159
+ handleMessage(clientId, data) {
160
+ const session = this.clients.get(clientId);
161
+ if (!session)
162
+ return;
163
+ try {
164
+ const msg = JSON.parse(data.toString());
165
+ switch (msg.type) {
166
+ case 'auth':
167
+ this.handleAuth(clientId, msg.payload.token);
168
+ break;
169
+ case 'ping':
170
+ this.handlePing(session);
171
+ break;
172
+ case 'subscribe':
173
+ this.handleSubscribe(session, msg);
174
+ break;
175
+ default:
176
+ const _msg = msg;
177
+ console.warn(`[RelayServer] Unknown message type: ${_msg?.type}`);
178
+ }
179
+ }
180
+ catch (err) {
181
+ console.error("[RelayServer] Invalid message:", err);
182
+ this.sendStatus(session, 'error', 'INVALID_MESSAGE', 'Failed to parse message');
183
+ }
184
+ }
185
+ handleAuth(clientId, token) {
186
+ const session = this.clients.get(clientId);
187
+ if (!session)
188
+ return;
189
+ const success = token === this.options.authToken;
190
+ const response = {
191
+ version: PROTOCOL_VERSION,
192
+ type: 'auth',
193
+ payload: {
194
+ success,
195
+ sessionId: success ? this.serverSessionId : undefined,
196
+ error: success ? undefined : 'Invalid token',
197
+ },
198
+ timestamp: createTimestamp(),
199
+ messageId: generateMessageId(),
200
+ };
201
+ this.sendMessage(session, response);
202
+ if (success) {
203
+ this.updateState(session, { state: 'connected', sessionId: this.serverSessionId });
204
+ console.log(`[RelayServer] Client authenticated: ${clientId}`);
205
+ // Send snapshot + synchronized (t3code pattern)
206
+ this.sendSnapshot(session);
207
+ }
208
+ else {
209
+ this.updateState(session, { state: 'error', error: 'Authentication failed' });
210
+ session.ws.close();
211
+ }
212
+ }
213
+ handlePing(session) {
214
+ session.lastPing = createTimestamp();
215
+ this.sendMessage(session, {
216
+ version: PROTOCOL_VERSION,
217
+ type: 'pong',
218
+ payload: {},
219
+ timestamp: createTimestamp(),
220
+ });
221
+ }
222
+ handleSubscribe(session, msg) {
223
+ session.subscriptions = new Set(msg.payload.events);
224
+ console.log(`[RelayServer] Client ${session.id} subscribed to: ${Array.from(session.subscriptions).join(', ')}`);
225
+ }
226
+ // ========================================================================
227
+ // Snapshot + Live Streaming (t3code pattern)
228
+ // ========================================================================
229
+ /**
230
+ * Send initial snapshot followed by synchronized marker
231
+ * This is the t3code pattern: snapshot -> synchronized -> live events
232
+ */
233
+ async sendSnapshot(session) {
234
+ // Build snapshot message
235
+ const snapshot = {
236
+ version: PROTOCOL_VERSION,
237
+ type: 'snapshot',
238
+ payload: {
239
+ sessionId: this.serverSessionId,
240
+ serverVersion: this.options.serverVersion,
241
+ connectedClients: this.clients.size,
242
+ recentCallbacks: this.recentCallbacks.slice(-20), // Last 20 callbacks
243
+ },
244
+ timestamp: createTimestamp(),
245
+ messageId: generateMessageId(),
246
+ };
247
+ // Send snapshot
248
+ this.sendMessage(session, snapshot);
249
+ // Send synchronized marker (t3code pattern: indicates snapshot complete)
250
+ const synchronized = {
251
+ version: PROTOCOL_VERSION,
252
+ type: 'synchronized',
253
+ payload: {},
254
+ timestamp: createTimestamp(),
255
+ messageId: generateMessageId(),
256
+ };
257
+ this.sendMessage(session, synchronized);
258
+ session.pendingSnapshot = false;
259
+ console.log(`[RelayServer] Snapshot sent to ${session.id}`);
54
260
  }
55
261
  /**
56
- * Get all active tunnels
262
+ * Flush event buffer and send batched callbacks
57
263
  */
58
- getTunnels() {
59
- return Array.from(this.tunnels.values());
264
+ flushEventBuffer() {
265
+ if (this.eventBuffer.callbacks.length === 0)
266
+ return;
267
+ const callbacks = this.eventBuffer.callbacks;
268
+ this.eventBuffer.callbacks = [];
269
+ this.eventBuffer.timer = null;
270
+ // Send each callback as a separate live event
271
+ for (const callback of callbacks) {
272
+ this.broadcastCallback(callback, false); // false = don't add to recent
273
+ }
60
274
  }
275
+ // ========================================================================
276
+ // Broadcasting
277
+ // ========================================================================
61
278
  /**
62
- * Get tunnel by ID
279
+ * Broadcast callback to all connected clients
280
+ * Uses stream batching for high-frequency events
63
281
  */
64
- getTunnel(id) {
65
- return this.tunnels.get(id);
282
+ broadcastCallback(callback, batch = true) {
283
+ // Add to recent callbacks (circular buffer)
284
+ this.recentCallbacks.push(callback);
285
+ if (this.recentCallbacks.length > MAX_RECENT_CALLBACKS) {
286
+ this.recentCallbacks.shift();
287
+ }
288
+ const message = {
289
+ version: PROTOCOL_VERSION,
290
+ type: 'callback',
291
+ payload: callback,
292
+ timestamp: createTimestamp(),
293
+ messageId: generateMessageId(),
294
+ };
295
+ if (batch) {
296
+ // Add to buffer for coalescing
297
+ this.eventBuffer.callbacks.push(callback);
298
+ if (!this.eventBuffer.timer) {
299
+ this.eventBuffer.timer = setTimeout(() => {
300
+ this.flushEventBuffer();
301
+ }, STREAM_BATCH_TIMEOUT_MS);
302
+ }
303
+ }
304
+ else {
305
+ // Send immediately
306
+ this.broadcast(message);
307
+ }
66
308
  }
67
309
  /**
68
- * Create a tunnel
310
+ * Send status to all clients
69
311
  */
70
- createTunnel(localPort) {
71
- const tunnel = {
72
- id: generateUUID(),
73
- url: `https://tunnel-${Date.now()}.relay.example.com`,
74
- localPort,
75
- status: 'connecting',
76
- createdAt: new Date().toISOString(),
312
+ broadcastStatus(kind, code, message) {
313
+ const msg = {
314
+ version: PROTOCOL_VERSION,
315
+ type: 'status',
316
+ payload: { kind, code, message },
317
+ timestamp: createTimestamp(),
318
+ messageId: generateMessageId(),
77
319
  };
78
- this.tunnels.set(tunnel.id, tunnel);
79
- return tunnel;
320
+ this.broadcast(msg);
80
321
  }
81
322
  /**
82
- * Remove a tunnel
323
+ * Broadcast to all subscribed clients
83
324
  */
84
- removeTunnel(id) {
85
- return this.tunnels.delete(id);
325
+ broadcast(message) {
326
+ let count = 0;
327
+ for (const session of this.clients.values()) {
328
+ if (this.isConnected(session)) {
329
+ this.sendMessage(session, message);
330
+ count++;
331
+ }
332
+ }
333
+ console.log(`[RelayServer] Broadcast ${message.type} to ${count} client(s)`);
86
334
  }
87
335
  /**
88
- * Check if server is running
336
+ * Check if client is connected and authenticated
89
337
  */
338
+ isConnected(session) {
339
+ return session.ws.readyState === WebSocket.OPEN &&
340
+ session.state.state === 'connected';
341
+ }
342
+ /**
343
+ * Send a typed message to a client
344
+ */
345
+ sendMessage(session, message) {
346
+ if (session.ws.readyState === WebSocket.OPEN) {
347
+ session.ws.send(JSON.stringify(message));
348
+ }
349
+ }
350
+ /**
351
+ * Send status to specific client
352
+ */
353
+ sendStatus(session, kind, code, message) {
354
+ const msg = {
355
+ version: PROTOCOL_VERSION,
356
+ type: 'status',
357
+ payload: { kind, code, message },
358
+ timestamp: createTimestamp(),
359
+ messageId: generateMessageId(),
360
+ };
361
+ this.sendMessage(session, msg);
362
+ }
363
+ // ========================================================================
364
+ // Public API
365
+ // ========================================================================
90
366
  isRunning() {
91
367
  return this.running;
92
368
  }
369
+ getClientCount() {
370
+ return this.clients.size;
371
+ }
372
+ getClients() {
373
+ return Array.from(this.clients.values()).map(s => ({
374
+ id: s.id,
375
+ state: s.state,
376
+ connectedAt: s.connectedAt,
377
+ }));
378
+ }
379
+ getRecentCallbacks(limit = 20) {
380
+ return this.recentCallbacks.slice(-limit);
381
+ }
382
+ }
383
+ // ============================================================================
384
+ // Export for use in webhook server
385
+ // ============================================================================
386
+ export function createRelayServer(options) {
387
+ return new RelayServer(options);
93
388
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Relay Types
2
+ * Relay Types - Two-way Streaming Communication
3
3
  */
4
4
  export interface RelayConfig {
5
5
  /** Relay server URL */
@@ -27,6 +27,142 @@ export interface RelayTunnel {
27
27
  /** Error message if any */
28
28
  error?: string;
29
29
  }
30
+ export type ConnectionState = {
31
+ state: 'disconnected';
32
+ } | {
33
+ state: 'connecting';
34
+ } | {
35
+ state: 'authenticating';
36
+ } | {
37
+ state: 'connected';
38
+ sessionId: string;
39
+ } | {
40
+ state: 'reconnecting';
41
+ attempt: number;
42
+ } | {
43
+ state: 'error';
44
+ error: string;
45
+ };
46
+ export interface ConnectionStateChange {
47
+ previous: ConnectionState;
48
+ current: ConnectionState;
49
+ timestamp: string;
50
+ }
51
+ /**
52
+ * Protocol version for typed streaming
53
+ */
54
+ export declare const PROTOCOL_VERSION = 1;
55
+ /**
56
+ * Generate a unique message ID
57
+ */
58
+ export declare function generateMessageId(): string;
59
+ /**
60
+ * Create a timestamp
61
+ */
62
+ export declare function createTimestamp(): string;
63
+ /**
64
+ * Base message interface
65
+ */
66
+ export interface BaseMessage<T extends string, P = unknown> {
67
+ version: typeof PROTOCOL_VERSION;
68
+ type: T;
69
+ payload: P;
70
+ timestamp: string;
71
+ messageId?: string;
72
+ }
73
+ /**
74
+ * Server -> Client: Initial state snapshot sent on connect
75
+ */
76
+ export interface SnapshotMessage extends BaseMessage<'snapshot'> {
77
+ payload: {
78
+ sessionId: string;
79
+ serverVersion: string;
80
+ connectedClients: number;
81
+ recentCallbacks: CallbackPayload[];
82
+ };
83
+ }
84
+ /**
85
+ * Server -> Client: Synchronization marker (indicates all initial state sent)
86
+ */
87
+ export interface SynchronizedMessage extends BaseMessage<'synchronized'> {
88
+ }
89
+ /**
90
+ * Callback types from Telegram/LINE
91
+ */
92
+ export interface CallbackPayload {
93
+ id: string;
94
+ type: 'telegram_callback' | 'line_callback';
95
+ action: string;
96
+ targetId: string;
97
+ userId: number;
98
+ username?: string;
99
+ messageId?: number;
100
+ timestamp: string;
101
+ }
102
+ /**
103
+ * Server -> Client: Live callback event (after synchronized)
104
+ */
105
+ export interface CallbackMessage extends BaseMessage<'callback'> {
106
+ payload: CallbackPayload;
107
+ }
108
+ /**
109
+ * Server -> Client: Status update
110
+ */
111
+ export type StatusKind = 'info' | 'warning' | 'error';
112
+ export interface StatusMessage extends BaseMessage<'status'> {
113
+ payload: {
114
+ kind: StatusKind;
115
+ code: string;
116
+ message: string;
117
+ };
118
+ }
119
+ /**
120
+ * Heartbeat messages
121
+ */
122
+ export interface PingMessage extends BaseMessage<'ping'> {
123
+ }
124
+ export interface PongMessage extends BaseMessage<'pong'> {
125
+ }
126
+ /**
127
+ * Authentication messages
128
+ */
129
+ export interface AuthRequestMessage extends BaseMessage<'auth'> {
130
+ payload: {
131
+ token: string;
132
+ };
133
+ }
134
+ export interface AuthResponseMessage extends BaseMessage<'auth'> {
135
+ payload: {
136
+ success: boolean;
137
+ sessionId?: string;
138
+ error?: string;
139
+ };
140
+ }
141
+ /**
142
+ * Client -> Server: Subscribe to specific event types
143
+ */
144
+ export interface SubscribeMessage extends BaseMessage<'subscribe'> {
145
+ payload: {
146
+ events: ('callback' | 'status' | 'sync')[];
147
+ };
148
+ }
149
+ /**
150
+ * Connection acknowledged by server
151
+ */
152
+ export interface ConnectedMessage extends BaseMessage<'connected'> {
153
+ payload: {
154
+ clientId: string;
155
+ serverTime: string;
156
+ };
157
+ }
158
+ /**
159
+ * Union type of all server -> client messages
160
+ */
161
+ export type ServerMessage = SnapshotMessage | SynchronizedMessage | CallbackMessage | StatusMessage | PongMessage | ConnectedMessage | AuthResponseMessage;
162
+ /**
163
+ * Union type of all client -> server messages
164
+ */
165
+ export type ClientMessage = AuthRequestMessage | PingMessage | SubscribeMessage;
30
166
  export interface RelayMessage {
31
167
  type: 'connect' | 'disconnect' | 'data' | 'ping' | 'pong' | 'error';
32
168
  tunnelId?: string;
@@ -1,4 +1,23 @@
1
1
  /**
2
- * Relay Types
2
+ * Relay Types - Two-way Streaming Communication
3
3
  */
4
- export {};
4
+ import { randomUUID } from "node:crypto";
5
+ // ============================================================================
6
+ // Typed Streaming Protocol
7
+ // ============================================================================
8
+ /**
9
+ * Protocol version for typed streaming
10
+ */
11
+ export const PROTOCOL_VERSION = 1;
12
+ /**
13
+ * Generate a unique message ID
14
+ */
15
+ export function generateMessageId() {
16
+ return randomUUID();
17
+ }
18
+ /**
19
+ * Create a timestamp
20
+ */
21
+ export function createTimestamp() {
22
+ return new Date().toISOString();
23
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * WebSocket Relay Server - Pushes callbacks to connected clients in real-time
3
+ *
4
+ * Architecture:
5
+ * Telegram/LINE → Webhook → This Server → WebSocket → Local Client
6
+ *
7
+ * No file queue needed - real-time streaming!
8
+ */
9
+ export interface RelayMessage {
10
+ type: "callback" | "ping" | "pong" | "auth";
11
+ payload: unknown;
12
+ }
13
+ export interface CallbackPayload {
14
+ type: "telegram_callback" | "line_callback";
15
+ action: string;
16
+ targetId: string;
17
+ userId: number;
18
+ username?: string;
19
+ messageId?: number;
20
+ timestamp: string;
21
+ }
22
+ /**
23
+ * WebSocket Relay Server
24
+ */
25
+ export declare class WsRelayServer {
26
+ private server;
27
+ private wss;
28
+ private clients;
29
+ private port;
30
+ private running;
31
+ constructor(port?: number);
32
+ /**
33
+ * Start the WebSocket server
34
+ */
35
+ start(): Promise<void>;
36
+ private handleMessage;
37
+ /**
38
+ * Broadcast callback to all connected clients
39
+ */
40
+ broadcastCallback(callback: CallbackPayload): void;
41
+ /**
42
+ * Stop the server
43
+ */
44
+ stop(): Promise<void>;
45
+ isRunning(): boolean;
46
+ getClientCount(): number;
47
+ }
48
+ export declare function getRelayServer(): WsRelayServer;