@kraki/tentacle 0.1.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.
Files changed (54) hide show
  1. package/LICENSE +21 -0
  2. package/dist/adapters/base.d.ts +107 -0
  3. package/dist/adapters/base.js +32 -0
  4. package/dist/adapters/base.js.map +1 -0
  5. package/dist/adapters/copilot.d.ts +57 -0
  6. package/dist/adapters/copilot.js +489 -0
  7. package/dist/adapters/copilot.js.map +1 -0
  8. package/dist/adapters/index.d.ts +5 -0
  9. package/dist/adapters/index.js +4 -0
  10. package/dist/adapters/index.js.map +1 -0
  11. package/dist/banner-data.json +1 -0
  12. package/dist/banner.d.ts +7 -0
  13. package/dist/banner.js +187 -0
  14. package/dist/banner.js.map +1 -0
  15. package/dist/checks.d.ts +26 -0
  16. package/dist/checks.js +74 -0
  17. package/dist/checks.js.map +1 -0
  18. package/dist/cli.d.ts +15 -0
  19. package/dist/cli.js +306 -0
  20. package/dist/cli.js.map +1 -0
  21. package/dist/config.d.ts +38 -0
  22. package/dist/config.js +113 -0
  23. package/dist/config.js.map +1 -0
  24. package/dist/daemon-worker.d.ts +21 -0
  25. package/dist/daemon-worker.js +127 -0
  26. package/dist/daemon-worker.js.map +1 -0
  27. package/dist/daemon.d.ts +24 -0
  28. package/dist/daemon.js +163 -0
  29. package/dist/daemon.js.map +1 -0
  30. package/dist/index.d.ts +8 -0
  31. package/dist/index.js +11 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/key-manager.d.ts +28 -0
  34. package/dist/key-manager.js +61 -0
  35. package/dist/key-manager.js.map +1 -0
  36. package/dist/logger.d.ts +8 -0
  37. package/dist/logger.js +30 -0
  38. package/dist/logger.js.map +1 -0
  39. package/dist/pair.d.ts +32 -0
  40. package/dist/pair.js +131 -0
  41. package/dist/pair.js.map +1 -0
  42. package/dist/parse-permission.d.ts +25 -0
  43. package/dist/parse-permission.js +67 -0
  44. package/dist/parse-permission.js.map +1 -0
  45. package/dist/relay-client.d.ts +90 -0
  46. package/dist/relay-client.js +525 -0
  47. package/dist/relay-client.js.map +1 -0
  48. package/dist/session-manager.d.ts +85 -0
  49. package/dist/session-manager.js +218 -0
  50. package/dist/session-manager.js.map +1 -0
  51. package/dist/setup.d.ts +13 -0
  52. package/dist/setup.js +234 -0
  53. package/dist/setup.js.map +1 -0
  54. package/package.json +48 -0
package/dist/logger.js ADDED
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Structured logging for Kraki tentacle.
3
+ *
4
+ * - Development: pretty-prints to stdout
5
+ * - Production: writes to rotating log files under ~/.kraki/logs/
6
+ */
7
+ import pino from 'pino';
8
+ import { mkdirSync } from 'node:fs';
9
+ import { join } from 'node:path';
10
+ import { homedir } from 'node:os';
11
+ const LOG_DIR = join(homedir(), '.kraki', 'logs');
12
+ export function createLogger(name) {
13
+ const level = process.env.LOG_LEVEL ?? 'info';
14
+ const isDev = process.env.NODE_ENV !== 'production';
15
+ if (isDev) {
16
+ return pino({ name, level });
17
+ }
18
+ // Production: rotate log files via pino-roll
19
+ mkdirSync(LOG_DIR, { recursive: true });
20
+ const transport = pino.transport({
21
+ target: 'pino-roll',
22
+ options: {
23
+ file: join(LOG_DIR, `${name}.log`),
24
+ size: '5m',
25
+ limit: { count: 5 },
26
+ },
27
+ });
28
+ return pino({ name, level }, transport);
29
+ }
30
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;AAElD,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,MAAM,CAAC;IAC9C,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,CAAC;IAEpD,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC/B,CAAC;IAED,6CAA6C;IAC7C,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAExC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC;QAC/B,MAAM,EAAE,WAAW;QACnB,OAAO,EAAE;YACP,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE,GAAG,IAAI,MAAM,CAAC;YAClC,IAAI,EAAE,IAAI;YACV,KAAK,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE;SACpB;KACF,CAAC,CAAC;IAEH,OAAO,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,SAAS,CAAC,CAAC;AAC1C,CAAC"}
package/dist/pair.d.ts ADDED
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Pairing command — generates a QR code for mobile app pairing.
3
+ *
4
+ * Requests a one-time pairing token from the head,
5
+ * then displays a QR code in the terminal containing:
6
+ * - relay URL
7
+ * - pairing token (expires in 5 min)
8
+ * - tentacle's public key (for E2E)
9
+ */
10
+ export interface PairingInfo {
11
+ relay: string;
12
+ pairingToken: string;
13
+ publicKey?: string;
14
+ expiresIn: number;
15
+ }
16
+ /**
17
+ * Request a pairing token from the head and return pairing info.
18
+ */
19
+ export declare function requestPairingToken(relayUrl: string, authToken?: string): Promise<PairingInfo>;
20
+ /**
21
+ * Generate the pairing URL that will be encoded in the QR code.
22
+ * Phone camera scans → opens this URL → web app auto-pairs.
23
+ */
24
+ export declare function buildPairingUrl(info: PairingInfo, appBaseUrl?: string): string;
25
+ /**
26
+ * Legacy: build compact JSON payload (for manual transfer).
27
+ */
28
+ export declare function buildPairingPayload(info: PairingInfo): string;
29
+ /**
30
+ * Render a QR code to the terminal. Copy link to clipboard.
31
+ */
32
+ export declare function renderQrToTerminal(url: string): Promise<string>;
package/dist/pair.js ADDED
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Pairing command — generates a QR code for mobile app pairing.
3
+ *
4
+ * Requests a one-time pairing token from the head,
5
+ * then displays a QR code in the terminal containing:
6
+ * - relay URL
7
+ * - pairing token (expires in 5 min)
8
+ * - tentacle's public key (for E2E)
9
+ */
10
+ import { WebSocket } from 'ws';
11
+ import { execSync } from 'node:child_process';
12
+ import { KeyManager } from './key-manager.js';
13
+ /**
14
+ * Request a pairing token from the head and return pairing info.
15
+ */
16
+ export async function requestPairingToken(relayUrl, authToken) {
17
+ return new Promise((resolve, reject) => {
18
+ const ws = new WebSocket(relayUrl);
19
+ const timeout = setTimeout(() => {
20
+ ws.close();
21
+ reject(new Error('Pairing request timed out'));
22
+ }, 10_000);
23
+ ws.on('open', () => {
24
+ // One-shot: send token inline, no device registration
25
+ ws.send(JSON.stringify({
26
+ type: 'request_pairing_token',
27
+ token: authToken,
28
+ }));
29
+ });
30
+ ws.on('message', (data) => {
31
+ let msg;
32
+ try {
33
+ msg = JSON.parse(data.toString());
34
+ }
35
+ catch {
36
+ return;
37
+ }
38
+ if (msg.type === 'pairing_token_created') {
39
+ clearTimeout(timeout);
40
+ const km = new KeyManager();
41
+ resolve({
42
+ relay: relayUrl,
43
+ pairingToken: msg.token,
44
+ publicKey: km.getCompactPublicKey(),
45
+ expiresIn: msg.expiresIn,
46
+ });
47
+ ws.close();
48
+ return;
49
+ }
50
+ if (msg.type === 'auth_error' || msg.type === 'server_error') {
51
+ clearTimeout(timeout);
52
+ reject(new Error(msg.message));
53
+ ws.close();
54
+ }
55
+ });
56
+ ws.on('error', (err) => {
57
+ clearTimeout(timeout);
58
+ reject(new Error(`Connection failed: ${err.message}`));
59
+ });
60
+ });
61
+ }
62
+ /**
63
+ * Generate the pairing URL that will be encoded in the QR code.
64
+ * Phone camera scans → opens this URL → web app auto-pairs.
65
+ */
66
+ export function buildPairingUrl(info, appBaseUrl) {
67
+ const base = appBaseUrl ?? process.env.KRAKI_APP_URL ?? 'https://kraki.corelli.cloud';
68
+ const params = new URLSearchParams();
69
+ params.set('relay', info.relay);
70
+ params.set('token', info.pairingToken);
71
+ // Don't include public key in URL — it makes the QR code too large.
72
+ // The app will exchange keys through the relay after pairing.
73
+ return `${base}?${params.toString()}`;
74
+ }
75
+ /**
76
+ * Legacy: build compact JSON payload (for manual transfer).
77
+ */
78
+ export function buildPairingPayload(info) {
79
+ return JSON.stringify({
80
+ r: info.relay,
81
+ t: info.pairingToken,
82
+ k: info.publicKey,
83
+ });
84
+ }
85
+ /**
86
+ * Render a QR code to the terminal. Copy link to clipboard.
87
+ */
88
+ export async function renderQrToTerminal(url) {
89
+ // Copy to clipboard silently
90
+ try {
91
+ const platform = process.platform;
92
+ if (platform === 'darwin') {
93
+ execSync('pbcopy', { input: url });
94
+ }
95
+ else if (platform === 'linux') {
96
+ execSync('xclip -selection clipboard', { input: url });
97
+ }
98
+ }
99
+ catch {
100
+ // Clipboard not available — that's fine
101
+ }
102
+ const appBase = process.env.KRAKI_APP_URL ?? 'https://kraki.corelli.cloud';
103
+ const appLink = `\u001b]8;;${appBase}\u0007${appBase.replace(/^https?:\/\//, '')}\u001b]8;;\u0007`;
104
+ try {
105
+ const qr = await import('qrcode-terminal');
106
+ return new Promise((resolve) => {
107
+ (qr.default ?? qr).generate(url, { small: true }, (qrString) => {
108
+ // Indent each line of the QR code
109
+ const indented = qrString.split('\n').map(line => ' ' + line).join('\n');
110
+ resolve([
111
+ '',
112
+ ` Scan with your phone camera, or visit ${appLink} and sign in with GitHub.`,
113
+ ' Link copied to clipboard.',
114
+ '',
115
+ indented,
116
+ ].join('\n'));
117
+ });
118
+ });
119
+ }
120
+ catch {
121
+ return [
122
+ '',
123
+ ` Open on your phone, or visit ${appLink} and sign in with GitHub.`,
124
+ ' Link copied to clipboard.',
125
+ '',
126
+ ` ${url}`,
127
+ '',
128
+ ].join('\n');
129
+ }
130
+ }
131
+ //# sourceMappingURL=pair.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pair.js","sourceRoot":"","sources":["../src/pair.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Parse raw Copilot SDK PermissionRequest into human-readable fields.
3
+ *
4
+ * Shared utility so future adapters (Claude, Codex) can reuse the same logic.
5
+ * Returns proper @kraki/protocol ToolArgs for end-to-end type safety.
6
+ */
7
+ import type { PermissionRequest } from '@github/copilot-sdk';
8
+ import type { ToolArgs } from '@kraki/protocol';
9
+ /** Normalised representation of a permission request with typed tool args. */
10
+ export interface ParsedPermission {
11
+ toolArgs: ToolArgs;
12
+ description: string;
13
+ }
14
+ /**
15
+ * Parse the raw SDK PermissionRequest into typed protocol fields.
16
+ *
17
+ * The Copilot SDK sends permission requests like:
18
+ * { kind: "write", fileName: "/tmp/foo.txt", intention: "Create file", diff: "..." }
19
+ * { kind: "shell", command: "npm test" }
20
+ * { kind: "read", fileName: "src/index.ts" }
21
+ * { kind: "url", url: "https://example.com" }
22
+ *
23
+ * We normalise these into typed ToolArgs + a human-readable description.
24
+ */
25
+ export declare function parsePermission(req: PermissionRequest): ParsedPermission;
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Parse raw Copilot SDK PermissionRequest into human-readable fields.
3
+ *
4
+ * Shared utility so future adapters (Claude, Codex) can reuse the same logic.
5
+ * Returns proper @kraki/protocol ToolArgs for end-to-end type safety.
6
+ */
7
+ /**
8
+ * Parse the raw SDK PermissionRequest into typed protocol fields.
9
+ *
10
+ * The Copilot SDK sends permission requests like:
11
+ * { kind: "write", fileName: "/tmp/foo.txt", intention: "Create file", diff: "..." }
12
+ * { kind: "shell", command: "npm test" }
13
+ * { kind: "read", fileName: "src/index.ts" }
14
+ * { kind: "url", url: "https://example.com" }
15
+ *
16
+ * We normalise these into typed ToolArgs + a human-readable description.
17
+ */
18
+ export function parsePermission(req) {
19
+ const kind = req.kind ?? 'unknown';
20
+ const intention = req.intention ?? '';
21
+ switch (kind) {
22
+ case 'shell': {
23
+ const command = (req.fullCommandText ?? req.command ?? req.cmd ?? req.script ?? '');
24
+ return {
25
+ toolArgs: { toolName: 'shell', args: { command } },
26
+ description: `Run: ${command}`,
27
+ };
28
+ }
29
+ case 'write': {
30
+ const path = (req.fileName ?? req.path ?? '');
31
+ return {
32
+ toolArgs: { toolName: 'write_file', args: { path, content: '' } },
33
+ description: `${intention || 'Write'}: ${path}`,
34
+ };
35
+ }
36
+ case 'read': {
37
+ const path = (req.fileName ?? req.path ?? '');
38
+ return {
39
+ toolArgs: { toolName: 'read_file', args: { path } },
40
+ description: `${intention || 'Read'}: ${path}`,
41
+ };
42
+ }
43
+ case 'url': {
44
+ const url = (req.url ?? '');
45
+ return {
46
+ toolArgs: { toolName: 'fetch_url', args: { url } },
47
+ description: `Fetch: ${url}`,
48
+ };
49
+ }
50
+ case 'mcp': {
51
+ const server = (req.serverName ?? '') || 'unknown';
52
+ const tool = (req.toolName ?? '') || 'unknown';
53
+ return {
54
+ toolArgs: { toolName: 'mcp', args: { server, tool, params: {} } },
55
+ description: `MCP tool: ${tool} on ${server}`,
56
+ };
57
+ }
58
+ default: {
59
+ const { kind: _, toolCallId: __, ...rest } = req;
60
+ return {
61
+ toolArgs: { toolName: intention || kind, args: rest },
62
+ description: intention || `${kind}: ${JSON.stringify(rest).slice(0, 200)}`,
63
+ };
64
+ }
65
+ }
66
+ }
67
+ //# sourceMappingURL=parse-permission.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-permission.js","sourceRoot":"","sources":["../src/parse-permission.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAWH;;;;;;;;;;GAUG;AACH,MAAM,UAAU,eAAe,CAAC,GAAsB;IACpD,MAAM,IAAI,GAAW,GAAG,CAAC,IAAI,IAAI,SAAS,CAAC;IAC3C,MAAM,SAAS,GAAI,GAAG,CAAC,SAAgC,IAAI,EAAE,CAAC;IAE9D,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,MAAM,OAAO,GAAI,CAAC,GAAG,CAAC,eAAe,IAAI,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,IAAI,EAAE,CAAY,CAAC;YAChG,OAAO;gBACL,QAAQ,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,EAAE;gBAClD,WAAW,EAAE,QAAQ,OAAO,EAAE;aAC/B,CAAC;QACJ,CAAC;QAED,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,MAAM,IAAI,GAAI,CAAC,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,IAAI,IAAI,EAAE,CAAY,CAAC;YAC1D,OAAO;gBACL,QAAQ,EAAE,EAAE,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE;gBACjE,WAAW,EAAE,GAAG,SAAS,IAAI,OAAO,KAAK,IAAI,EAAE;aAChD,CAAC;QACJ,CAAC;QAED,KAAK,MAAM,CAAC,CAAC,CAAC;YACZ,MAAM,IAAI,GAAI,CAAC,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,IAAI,IAAI,EAAE,CAAY,CAAC;YAC1D,OAAO;gBACL,QAAQ,EAAE,EAAE,QAAQ,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,EAAE;gBACnD,WAAW,EAAE,GAAG,SAAS,IAAI,MAAM,KAAK,IAAI,EAAE;aAC/C,CAAC;QACJ,CAAC;QAED,KAAK,KAAK,CAAC,CAAC,CAAC;YACX,MAAM,GAAG,GAAI,CAAC,GAAG,CAAC,GAAG,IAAI,EAAE,CAAY,CAAC;YACxC,OAAO;gBACL,QAAQ,EAAE,EAAE,QAAQ,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,EAAE;gBAClD,WAAW,EAAE,UAAU,GAAG,EAAE;aAC7B,CAAC;QACJ,CAAC;QAED,KAAK,KAAK,CAAC,CAAC,CAAC;YACX,MAAM,MAAM,GAAI,CAAC,GAAG,CAAC,UAAU,IAAI,EAAE,CAAY,IAAI,SAAS,CAAC;YAC/D,MAAM,IAAI,GAAI,CAAC,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAY,IAAI,SAAS,CAAC;YAC3D,OAAO;gBACL,QAAQ,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE;gBACjE,WAAW,EAAE,aAAa,IAAI,OAAO,MAAM,EAAE;aAC9C,CAAC;QACJ,CAAC;QAED,OAAO,CAAC,CAAC,CAAC;YACR,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE,GAAG,IAAI,EAAE,GAAG,GAAG,CAAC;YACjD,OAAO;gBACL,QAAQ,EAAE,EAAE,QAAQ,EAAE,SAAS,IAAI,IAAI,EAAE,IAAI,EAAE,IAA+B,EAAE;gBAChF,WAAW,EAAE,SAAS,IAAI,GAAG,IAAI,KAAK,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE;aAC3E,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Relay client — connects the tentacle to the head via WebSocket.
3
+ *
4
+ * Translates adapter events into protocol messages and sends them to the head.
5
+ * Receives consumer actions from the head and routes them to the adapter.
6
+ * Handles auth, reconnection, and session lifecycle.
7
+ */
8
+ import type { DeviceInfo, AuthOkMessage } from '@kraki/protocol';
9
+ import type { AgentAdapter } from './adapters/base.js';
10
+ import type { SessionManager } from './session-manager.js';
11
+ import type { KeyManager } from './key-manager.js';
12
+ export interface RelayClientOptions {
13
+ /** Relay WebSocket URL (e.g., wss://kraki.corelli.cloud) */
14
+ relayUrl: string;
15
+ /** Device info for auth */
16
+ device: DeviceInfo;
17
+ /** GitHub token or channel key */
18
+ token?: string;
19
+ /** Reconnect delay in ms. Default: 3000 */
20
+ reconnectDelay?: number;
21
+ /** Max reconnect attempts. Default: Infinity */
22
+ maxReconnects?: number;
23
+ }
24
+ export type RelayClientState = 'disconnected' | 'connecting' | 'authenticating' | 'connected';
25
+ export declare class RelayClient {
26
+ private ws;
27
+ private adapter;
28
+ private sessionManager;
29
+ private keyManager;
30
+ private options;
31
+ private state;
32
+ private reconnectAttempts;
33
+ private reconnectTimer;
34
+ private authInfo;
35
+ private e2eEnabled;
36
+ /** Cached consumer public keys for E2E encryption */
37
+ private consumerKeys;
38
+ /** Messages queued when E2E is enabled but no consumer keys are available yet */
39
+ private pendingE2eQueue;
40
+ /** Maps pre-generated sessionId → requestId for concurrent create_session correlation */
41
+ private pendingRequestIds;
42
+ private lastServerPingAt;
43
+ private staleCheckTimer;
44
+ /** How long without a server ping before we consider the connection stale (ms) */
45
+ private static readonly STALE_THRESHOLD;
46
+ /** How often to check for stale connection (ms) */
47
+ private static readonly STALE_CHECK_INTERVAL;
48
+ /** Called when relay state changes */
49
+ onStateChange: ((state: RelayClientState) => void) | null;
50
+ /** Called on auth success */
51
+ onAuthenticated: ((info: AuthOkMessage) => void) | null;
52
+ /** Called on fatal error (won't reconnect) */
53
+ onFatalError: ((message: string) => void) | null;
54
+ constructor(adapter: AgentAdapter, sessionManager: SessionManager, options: RelayClientOptions, keyManager?: KeyManager | null);
55
+ /**
56
+ * Connect to the relay. Auto-reconnects on disconnect.
57
+ */
58
+ connect(): void;
59
+ /**
60
+ * Disconnect from the relay. No reconnect.
61
+ */
62
+ disconnect(): void;
63
+ /**
64
+ * Get current connection state.
65
+ */
66
+ getState(): RelayClientState;
67
+ /**
68
+ * Get auth info from last successful connection.
69
+ */
70
+ getAuthInfo(): AuthOkMessage | null;
71
+ private handleMessage;
72
+ private handleConsumerMessage;
73
+ private handleCreateSession;
74
+ private wireAdapterEvents;
75
+ private resumeDisconnectedSessions;
76
+ private send;
77
+ /**
78
+ * Encrypt and send a message to the relay.
79
+ */
80
+ private sendEncrypted;
81
+ /**
82
+ * Flush queued E2E messages once consumer keys become available.
83
+ */
84
+ private flushE2eQueue;
85
+ private updateConsumerKeys;
86
+ private startStaleCheck;
87
+ private stopStaleCheck;
88
+ private scheduleReconnect;
89
+ private setState;
90
+ }