@silicaclaw/cli 2026.3.20-2 → 2026.3.20-21

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 (145) hide show
  1. package/CHANGELOG.md +108 -0
  2. package/INSTALL.md +13 -7
  3. package/README.md +60 -12
  4. package/VERSION +1 -1
  5. package/apps/local-console/dist/apps/local-console/src/server.d.ts +139 -3
  6. package/apps/local-console/dist/apps/local-console/src/server.js +1029 -92
  7. package/apps/local-console/dist/packages/core/src/index.d.ts +2 -0
  8. package/apps/local-console/dist/packages/core/src/index.js +2 -0
  9. package/apps/local-console/dist/packages/core/src/privateCrypto.d.ts +17 -0
  10. package/apps/local-console/dist/packages/core/src/privateCrypto.js +40 -0
  11. package/apps/local-console/dist/packages/core/src/privateMessage.d.ts +23 -0
  12. package/apps/local-console/dist/packages/core/src/privateMessage.js +74 -0
  13. package/apps/local-console/dist/packages/core/src/profile.js +2 -0
  14. package/apps/local-console/dist/packages/core/src/publicProfileSummary.d.ts +4 -0
  15. package/apps/local-console/dist/packages/core/src/publicProfileSummary.js +3 -0
  16. package/apps/local-console/dist/packages/core/src/types.d.ts +40 -0
  17. package/apps/local-console/dist/packages/network/src/relayPreview.d.ts +12 -0
  18. package/apps/local-console/dist/packages/network/src/relayPreview.js +108 -8
  19. package/apps/local-console/dist/packages/network/src/types.d.ts +4 -0
  20. package/apps/local-console/dist/packages/storage/src/repos.d.ts +27 -1
  21. package/apps/local-console/dist/packages/storage/src/repos.js +35 -1
  22. package/apps/local-console/public/app/app.js +502 -11
  23. package/apps/local-console/public/app/events.js +21 -0
  24. package/apps/local-console/public/app/network.js +144 -32
  25. package/apps/local-console/public/app/overview.js +57 -27
  26. package/apps/local-console/public/app/social.js +342 -105
  27. package/apps/local-console/public/app/styles.css +149 -43
  28. package/apps/local-console/public/app/template.js +196 -100
  29. package/apps/local-console/public/app/translations.js +438 -316
  30. package/apps/local-console/src/server.ts +1177 -90
  31. package/apps/public-explorer/public/app/template.js +2 -2
  32. package/apps/public-explorer/public/app/translations.js +36 -36
  33. package/docs/NEW_USER_OPERATIONS.md +5 -5
  34. package/docs/OPENCLAW_BRIDGE.md +7 -7
  35. package/docs/OPENCLAW_BRIDGE_ZH.md +6 -6
  36. package/node_modules/@silicaclaw/core/dist/packages/core/src/index.d.ts +2 -0
  37. package/node_modules/@silicaclaw/core/dist/packages/core/src/index.js +2 -0
  38. package/node_modules/@silicaclaw/core/dist/packages/core/src/privateCrypto.d.ts +17 -0
  39. package/node_modules/@silicaclaw/core/dist/packages/core/src/privateCrypto.js +40 -0
  40. package/node_modules/@silicaclaw/core/dist/packages/core/src/privateMessage.d.ts +23 -0
  41. package/node_modules/@silicaclaw/core/dist/packages/core/src/privateMessage.js +74 -0
  42. package/node_modules/@silicaclaw/core/dist/packages/core/src/profile.js +2 -0
  43. package/node_modules/@silicaclaw/core/dist/packages/core/src/publicProfileSummary.d.ts +4 -0
  44. package/node_modules/@silicaclaw/core/dist/packages/core/src/publicProfileSummary.js +3 -0
  45. package/node_modules/@silicaclaw/core/dist/packages/core/src/types.d.ts +40 -0
  46. package/node_modules/@silicaclaw/core/package.json +2 -2
  47. package/node_modules/@silicaclaw/core/src/index.ts +2 -0
  48. package/node_modules/@silicaclaw/core/src/privateCrypto.ts +57 -0
  49. package/node_modules/@silicaclaw/core/src/privateMessage.ts +101 -0
  50. package/node_modules/@silicaclaw/core/src/profile.ts +2 -0
  51. package/node_modules/@silicaclaw/core/src/publicProfileSummary.ts +7 -0
  52. package/node_modules/@silicaclaw/core/src/types.ts +44 -0
  53. package/node_modules/@silicaclaw/network/dist/packages/network/src/relayPreview.d.ts +12 -0
  54. package/node_modules/@silicaclaw/network/dist/packages/network/src/relayPreview.js +108 -8
  55. package/node_modules/@silicaclaw/network/dist/packages/network/src/types.d.ts +4 -0
  56. package/node_modules/@silicaclaw/network/src/relayPreview.ts +120 -10
  57. package/node_modules/@silicaclaw/network/src/types.ts +2 -0
  58. package/node_modules/@silicaclaw/storage/dist/packages/core/src/index.d.ts +2 -0
  59. package/node_modules/@silicaclaw/storage/dist/packages/core/src/index.js +2 -0
  60. package/node_modules/@silicaclaw/storage/dist/packages/core/src/privateCrypto.d.ts +17 -0
  61. package/node_modules/@silicaclaw/storage/dist/packages/core/src/privateCrypto.js +40 -0
  62. package/node_modules/@silicaclaw/storage/dist/packages/core/src/privateMessage.d.ts +23 -0
  63. package/node_modules/@silicaclaw/storage/dist/packages/core/src/privateMessage.js +74 -0
  64. package/node_modules/@silicaclaw/storage/dist/packages/core/src/profile.js +2 -0
  65. package/node_modules/@silicaclaw/storage/dist/packages/core/src/publicProfileSummary.d.ts +4 -0
  66. package/node_modules/@silicaclaw/storage/dist/packages/core/src/publicProfileSummary.js +3 -0
  67. package/node_modules/@silicaclaw/storage/dist/packages/core/src/types.d.ts +40 -0
  68. package/node_modules/@silicaclaw/storage/dist/packages/storage/src/repos.d.ts +27 -1
  69. package/node_modules/@silicaclaw/storage/dist/packages/storage/src/repos.js +35 -1
  70. package/node_modules/@silicaclaw/storage/package.json +2 -2
  71. package/node_modules/@silicaclaw/storage/src/repos.ts +59 -1
  72. package/openclaw-skills/silicaclaw-bridge-setup/SKILL.md +18 -0
  73. package/openclaw-skills/silicaclaw-bridge-setup/VERSION +1 -1
  74. package/openclaw-skills/silicaclaw-bridge-setup/manifest.json +2 -2
  75. package/openclaw-skills/silicaclaw-broadcast/SKILL.md +18 -0
  76. package/openclaw-skills/silicaclaw-broadcast/VERSION +1 -1
  77. package/openclaw-skills/silicaclaw-broadcast/manifest.json +2 -2
  78. package/openclaw-skills/silicaclaw-network-config/SKILL.md +158 -0
  79. package/openclaw-skills/silicaclaw-network-config/VERSION +1 -0
  80. package/openclaw-skills/silicaclaw-network-config/agents/openai.yaml +6 -0
  81. package/openclaw-skills/silicaclaw-network-config/manifest.json +27 -0
  82. package/openclaw-skills/silicaclaw-network-config/references/network-modes.md +22 -0
  83. package/openclaw-skills/silicaclaw-network-config/references/owner-dialogue-cheatsheet-zh.md +47 -0
  84. package/openclaw-skills/silicaclaw-network-config/references/public-discovery.md +22 -0
  85. package/openclaw-skills/silicaclaw-owner-push/SKILL.md +18 -0
  86. package/openclaw-skills/silicaclaw-owner-push/VERSION +1 -1
  87. package/openclaw-skills/silicaclaw-owner-push/manifest.json +2 -2
  88. package/openclaw-skills/silicaclaw-owner-push/references/runtime-setup.md +3 -0
  89. package/openclaw-skills/silicaclaw-owner-push/scripts/owner-push-forwarder.mjs +151 -9
  90. package/package.json +1 -1
  91. package/packages/core/dist/packages/core/src/index.d.ts +2 -0
  92. package/packages/core/dist/packages/core/src/index.js +2 -0
  93. package/packages/core/dist/packages/core/src/privateCrypto.d.ts +17 -0
  94. package/packages/core/dist/packages/core/src/privateCrypto.js +40 -0
  95. package/packages/core/dist/packages/core/src/privateMessage.d.ts +23 -0
  96. package/packages/core/dist/packages/core/src/privateMessage.js +74 -0
  97. package/packages/core/dist/packages/core/src/profile.js +2 -0
  98. package/packages/core/dist/packages/core/src/publicProfileSummary.d.ts +4 -0
  99. package/packages/core/dist/packages/core/src/publicProfileSummary.js +3 -0
  100. package/packages/core/dist/packages/core/src/types.d.ts +40 -0
  101. package/packages/core/package.json +2 -2
  102. package/packages/core/src/index.ts +2 -0
  103. package/packages/core/src/privateCrypto.ts +57 -0
  104. package/packages/core/src/privateMessage.ts +101 -0
  105. package/packages/core/src/profile.ts +2 -0
  106. package/packages/core/src/publicProfileSummary.ts +7 -0
  107. package/packages/core/src/types.ts +44 -0
  108. package/packages/network/dist/packages/network/src/relayPreview.d.ts +12 -0
  109. package/packages/network/dist/packages/network/src/relayPreview.js +108 -8
  110. package/packages/network/dist/packages/network/src/types.d.ts +4 -0
  111. package/packages/network/src/relayPreview.ts +120 -10
  112. package/packages/network/src/types.ts +2 -0
  113. package/packages/storage/dist/packages/core/src/index.d.ts +2 -0
  114. package/packages/storage/dist/packages/core/src/index.js +2 -0
  115. package/packages/storage/dist/packages/core/src/privateCrypto.d.ts +17 -0
  116. package/packages/storage/dist/packages/core/src/privateCrypto.js +40 -0
  117. package/packages/storage/dist/packages/core/src/privateMessage.d.ts +23 -0
  118. package/packages/storage/dist/packages/core/src/privateMessage.js +74 -0
  119. package/packages/storage/dist/packages/core/src/profile.js +2 -0
  120. package/packages/storage/dist/packages/core/src/publicProfileSummary.d.ts +4 -0
  121. package/packages/storage/dist/packages/core/src/publicProfileSummary.js +3 -0
  122. package/packages/storage/dist/packages/core/src/types.d.ts +40 -0
  123. package/packages/storage/dist/packages/storage/src/repos.d.ts +27 -1
  124. package/packages/storage/dist/packages/storage/src/repos.js +35 -1
  125. package/packages/storage/package.json +2 -2
  126. package/packages/storage/src/repos.ts +59 -1
  127. package/scripts/silicaclaw-cli.mjs +4 -1
  128. package/scripts/silicaclaw-gateway.mjs +114 -2
  129. package/scripts/validate-openclaw-skill.mjs +19 -0
  130. package/node_modules/@silicaclaw/storage/dist/index.d.ts +0 -3
  131. package/node_modules/@silicaclaw/storage/dist/index.js +0 -19
  132. package/node_modules/@silicaclaw/storage/dist/jsonRepo.d.ts +0 -7
  133. package/node_modules/@silicaclaw/storage/dist/jsonRepo.js +0 -29
  134. package/node_modules/@silicaclaw/storage/dist/repos.d.ts +0 -61
  135. package/node_modules/@silicaclaw/storage/dist/repos.js +0 -67
  136. package/node_modules/@silicaclaw/storage/dist/socialRuntimeRepo.d.ts +0 -5
  137. package/node_modules/@silicaclaw/storage/dist/socialRuntimeRepo.js +0 -57
  138. package/packages/storage/dist/index.d.ts +0 -3
  139. package/packages/storage/dist/index.js +0 -19
  140. package/packages/storage/dist/jsonRepo.d.ts +0 -7
  141. package/packages/storage/dist/jsonRepo.js +0 -29
  142. package/packages/storage/dist/repos.d.ts +0 -61
  143. package/packages/storage/dist/repos.js +0 -67
  144. package/packages/storage/dist/socialRuntimeRepo.d.ts +0 -5
  145. package/packages/storage/dist/socialRuntimeRepo.js +0 -57
@@ -0,0 +1,57 @@
1
+ import nacl from "tweetnacl";
2
+ import { fromBase64, toBase64 } from "./crypto";
3
+ import { PrivateEncryptionKeyPair } from "./types";
4
+
5
+ export function createPrivateEncryptionKeyPair(now = Date.now()): PrivateEncryptionKeyPair {
6
+ const pair = nacl.box.keyPair();
7
+ return {
8
+ public_key: toBase64(pair.publicKey),
9
+ private_key: toBase64(pair.secretKey),
10
+ created_at: now,
11
+ };
12
+ }
13
+
14
+ export function encryptPrivatePayload(input: {
15
+ plaintext: string;
16
+ recipient_public_key: string;
17
+ sender_keypair?: PrivateEncryptionKeyPair | null;
18
+ }): {
19
+ ciphertext: string;
20
+ nonce: string;
21
+ sender_encryption_public_key: string;
22
+ } {
23
+ const sender = input.sender_keypair || createPrivateEncryptionKeyPair();
24
+ const nonce = nacl.randomBytes(nacl.box.nonceLength);
25
+ const message = Buffer.from(String(input.plaintext || ""), "utf8");
26
+ const ciphertext = nacl.box(
27
+ new Uint8Array(message),
28
+ nonce,
29
+ fromBase64(input.recipient_public_key),
30
+ fromBase64(sender.private_key),
31
+ );
32
+ return {
33
+ ciphertext: toBase64(ciphertext),
34
+ nonce: toBase64(nonce),
35
+ sender_encryption_public_key: sender.public_key,
36
+ };
37
+ }
38
+
39
+ export function decryptPrivatePayload(input: {
40
+ ciphertext: string;
41
+ nonce: string;
42
+ sender_encryption_public_key: string;
43
+ recipient_private_key: string;
44
+ }): string | null {
45
+ try {
46
+ const opened = nacl.box.open(
47
+ fromBase64(input.ciphertext),
48
+ fromBase64(input.nonce),
49
+ fromBase64(input.sender_encryption_public_key),
50
+ fromBase64(input.recipient_private_key),
51
+ );
52
+ if (!opened) return null;
53
+ return Buffer.from(opened).toString("utf8");
54
+ } catch {
55
+ return null;
56
+ }
57
+ }
@@ -0,0 +1,101 @@
1
+ import {
2
+ AgentIdentity,
3
+ PrivateMessageReceiptRecord,
4
+ PrivateMessageRecord,
5
+ } from "./types";
6
+ import { fromBase64, hashPublicKey, signPayload, verifyPayload } from "./crypto";
7
+
8
+ function unsignedPrivateMessage(
9
+ record: PrivateMessageRecord
10
+ ): Omit<PrivateMessageRecord, "signature"> {
11
+ const { signature: _signature, ...rest } = record;
12
+ return rest;
13
+ }
14
+
15
+ function unsignedPrivateMessageReceipt(
16
+ record: PrivateMessageReceiptRecord
17
+ ): Omit<PrivateMessageReceiptRecord, "signature"> {
18
+ const { signature: _signature, ...rest } = record;
19
+ return rest;
20
+ }
21
+
22
+ export function signPrivateMessage(input: {
23
+ identity: AgentIdentity;
24
+ message_id: string;
25
+ conversation_id: string;
26
+ to_agent_id: string;
27
+ sender_encryption_public_key: string;
28
+ recipient_encryption_public_key: string;
29
+ ciphertext: string;
30
+ nonce: string;
31
+ created_at?: number;
32
+ }): PrivateMessageRecord {
33
+ const payload: Omit<PrivateMessageRecord, "signature"> = {
34
+ type: "private.message",
35
+ message_id: input.message_id,
36
+ conversation_id: input.conversation_id,
37
+ from_agent_id: input.identity.agent_id,
38
+ to_agent_id: input.to_agent_id,
39
+ sender_public_key: input.identity.public_key,
40
+ sender_encryption_public_key: input.sender_encryption_public_key,
41
+ recipient_encryption_public_key: input.recipient_encryption_public_key,
42
+ cipher_scheme: "nacl-box-v1",
43
+ ciphertext: input.ciphertext,
44
+ nonce: input.nonce,
45
+ created_at: input.created_at ?? Date.now(),
46
+ };
47
+
48
+ return {
49
+ ...payload,
50
+ signature: signPayload(payload, input.identity.private_key),
51
+ };
52
+ }
53
+
54
+ export function verifyPrivateMessage(record: PrivateMessageRecord): boolean {
55
+ try {
56
+ if (hashPublicKey(fromBase64(record.sender_public_key)) !== record.from_agent_id) {
57
+ return false;
58
+ }
59
+ return verifyPayload(unsignedPrivateMessage(record), record.signature, record.sender_public_key);
60
+ } catch {
61
+ return false;
62
+ }
63
+ }
64
+
65
+ export function signPrivateMessageReceipt(input: {
66
+ identity: AgentIdentity;
67
+ receipt_id: string;
68
+ message_id: string;
69
+ conversation_id: string;
70
+ to_agent_id: string;
71
+ status: "received" | "read";
72
+ created_at?: number;
73
+ }): PrivateMessageReceiptRecord {
74
+ const payload: Omit<PrivateMessageReceiptRecord, "signature"> = {
75
+ type: "private.message.receipt",
76
+ receipt_id: input.receipt_id,
77
+ message_id: input.message_id,
78
+ conversation_id: input.conversation_id,
79
+ from_agent_id: input.identity.agent_id,
80
+ to_agent_id: input.to_agent_id,
81
+ sender_public_key: input.identity.public_key,
82
+ status: input.status,
83
+ created_at: input.created_at ?? Date.now(),
84
+ };
85
+
86
+ return {
87
+ ...payload,
88
+ signature: signPayload(payload, input.identity.private_key),
89
+ };
90
+ }
91
+
92
+ export function verifyPrivateMessageReceipt(record: PrivateMessageReceiptRecord): boolean {
93
+ try {
94
+ if (hashPublicKey(fromBase64(record.sender_public_key)) !== record.from_agent_id) {
95
+ return false;
96
+ }
97
+ return verifyPayload(unsignedPrivateMessageReceipt(record), record.signature, record.sender_public_key);
98
+ } catch {
99
+ return false;
100
+ }
101
+ }
@@ -13,6 +13,7 @@ export function signProfile(input: ProfileInput, identity: AgentIdentity): Publi
13
13
  bio: input.bio,
14
14
  tags: input.tags,
15
15
  avatar_url: input.avatar_url,
16
+ private_encryption_public_key: input.private_encryption_public_key,
16
17
  public_enabled: input.public_enabled,
17
18
  updated_at: Date.now(),
18
19
  };
@@ -34,6 +35,7 @@ export function createDefaultProfileInput(agentId: string): ProfileInput {
34
35
  bio: "",
35
36
  tags: [],
36
37
  avatar_url: "",
38
+ private_encryption_public_key: "",
37
39
  public_enabled: false,
38
40
  };
39
41
  }
@@ -10,9 +10,11 @@ export type ProfileVisibility = {
10
10
 
11
11
  export type PublicProfileSummary = {
12
12
  agent_id: string;
13
+ is_self: boolean;
13
14
  display_name: string;
14
15
  bio: string;
15
16
  avatar_url?: string;
17
+ private_encryption_public_key?: string;
16
18
  public_enabled: boolean;
17
19
  updated_at: number;
18
20
  online: boolean;
@@ -33,6 +35,7 @@ export type PublicProfileSummary = {
33
35
  display_name: string;
34
36
  bio: string;
35
37
  avatar_url?: string;
38
+ private_encryption_public_key?: string;
36
39
  tags: string[];
37
40
  public_enabled: boolean;
38
41
  profile_version: string;
@@ -73,6 +76,7 @@ export function deriveCapabilitiesSummary(tags: string[]): string[] {
73
76
 
74
77
  export function buildPublicProfileSummary(args: {
75
78
  profile: PublicProfile;
79
+ is_self?: boolean;
76
80
  online: boolean;
77
81
  last_seen_at: number | null;
78
82
  network_mode?: string;
@@ -130,9 +134,11 @@ export function buildPublicProfileSummary(args: {
130
134
 
131
135
  return {
132
136
  agent_id: args.profile.agent_id,
137
+ is_self: Boolean(args.is_self),
133
138
  display_name: args.profile.display_name,
134
139
  bio: args.profile.bio,
135
140
  avatar_url: args.profile.avatar_url,
141
+ private_encryption_public_key: args.profile.private_encryption_public_key,
136
142
  public_enabled: args.profile.public_enabled,
137
143
  updated_at: args.profile.updated_at,
138
144
  online: args.online,
@@ -153,6 +159,7 @@ export function buildPublicProfileSummary(args: {
153
159
  display_name: args.profile.display_name,
154
160
  bio: args.profile.bio,
155
161
  avatar_url: args.profile.avatar_url,
162
+ private_encryption_public_key: args.profile.private_encryption_public_key,
156
163
  tags,
157
164
  public_enabled: args.profile.public_enabled,
158
165
  profile_version: args.profile_version ?? "v1",
@@ -5,12 +5,19 @@ export type AgentIdentity = {
5
5
  created_at: number;
6
6
  };
7
7
 
8
+ export type PrivateEncryptionKeyPair = {
9
+ public_key: string;
10
+ private_key: string;
11
+ created_at: number;
12
+ };
13
+
8
14
  export type PublicProfile = {
9
15
  agent_id: string;
10
16
  display_name: string;
11
17
  bio: string;
12
18
  tags: string[];
13
19
  avatar_url?: string;
20
+ private_encryption_public_key?: string;
14
21
  public_enabled: boolean;
15
22
  updated_at: number;
16
23
  signature: string;
@@ -58,6 +65,43 @@ export type SocialMessageObservationRecord = {
58
65
  signature: string;
59
66
  };
60
67
 
68
+ export type PrivateMessageRecord = {
69
+ type: "private.message";
70
+ message_id: string;
71
+ conversation_id: string;
72
+ from_agent_id: string;
73
+ to_agent_id: string;
74
+ sender_public_key: string;
75
+ sender_encryption_public_key: string;
76
+ recipient_encryption_public_key: string;
77
+ cipher_scheme: "nacl-box-v1";
78
+ ciphertext: string;
79
+ nonce: string;
80
+ created_at: number;
81
+ signature: string;
82
+ };
83
+
84
+ export type PrivateMessageReceiptRecord = {
85
+ type: "private.message.receipt";
86
+ receipt_id: string;
87
+ message_id: string;
88
+ conversation_id: string;
89
+ from_agent_id: string;
90
+ to_agent_id: string;
91
+ sender_public_key: string;
92
+ status: "received" | "read";
93
+ created_at: number;
94
+ signature: string;
95
+ };
96
+
97
+ export type PrivateConversationSummary = {
98
+ conversation_id: string;
99
+ peer_agent_id: string;
100
+ last_message_at: number | null;
101
+ last_message_preview: string;
102
+ unread_count: number;
103
+ };
104
+
61
105
  export type DirectoryState = {
62
106
  profiles: Record<string, PublicProfile>;
63
107
  presence: Record<string, number>;
@@ -22,6 +22,10 @@ type RelayPeer = {
22
22
  last_seen_at: number;
23
23
  messages_seen: number;
24
24
  reconnect_attempts: number;
25
+ meta?: {
26
+ signal_queue_size?: number;
27
+ relay_queue_size?: number;
28
+ };
25
29
  };
26
30
  type RelayDiagnostics = {
27
31
  adapter: "relay-preview";
@@ -126,6 +130,7 @@ export declare class RelayPreviewAdapter implements NetworkAdapter {
126
130
  private started;
127
131
  private poller;
128
132
  private handlers;
133
+ private directHandlers;
129
134
  private peers;
130
135
  private seenMessageIds;
131
136
  private activeEndpoint;
@@ -150,10 +155,16 @@ export declare class RelayPreviewAdapter implements NetworkAdapter {
150
155
  stop(): Promise<void>;
151
156
  publish(topic: string, data: any): Promise<void>;
152
157
  subscribe(topic: string, handler: (data: any) => void): void;
158
+ sendDirect(peerId: string, topic: string, data: any): Promise<void>;
159
+ subscribeDirect(topic: string, handler: (data: any, meta?: {
160
+ peerId?: string;
161
+ }) => void): void;
153
162
  getDiagnostics(): RelayDiagnostics;
154
163
  private pollOnce;
155
164
  private refreshPeers;
156
165
  private onEnvelope;
166
+ private onDirectEnvelope;
167
+ private dispatchEnvelope;
157
168
  private recordDiscovery;
158
169
  private joinRoom;
159
170
  private maybeRefreshJoin;
@@ -162,5 +173,6 @@ export declare class RelayPreviewAdapter implements NetworkAdapter {
162
173
  private requestJson;
163
174
  private updatePeersFromList;
164
175
  private scheduleNextPoll;
176
+ private ensurePollingAlive;
165
177
  }
166
178
  export {};
@@ -31,6 +31,7 @@ class RelayPreviewAdapter {
31
31
  started = false;
32
32
  poller = null;
33
33
  handlers = new Map();
34
+ directHandlers = new Map();
34
35
  peers = new Map();
35
36
  seenMessageIds = new Set();
36
37
  activeEndpoint = "";
@@ -109,7 +110,6 @@ class RelayPreviewAdapter {
109
110
  try {
110
111
  await this.joinRoom("start");
111
112
  this.started = true;
112
- await this.refreshPeers();
113
113
  await this.pollOnce();
114
114
  this.scheduleNextPoll(this.pollIntervalMs);
115
115
  this.recordDiscovery("signaling_connected", { endpoint: this.activeEndpoint });
@@ -166,6 +166,42 @@ class RelayPreviewAdapter {
166
166
  }
167
167
  this.handlers.get(key)?.add(handler);
168
168
  }
169
+ async sendDirect(peerId, topic, data) {
170
+ if (!this.started)
171
+ return;
172
+ const targetPeerId = String(peerId || "").trim();
173
+ if (!targetPeerId)
174
+ return;
175
+ await this.maybeRefreshJoin("direct_send");
176
+ const envelope = {
177
+ version: 1,
178
+ message_id: (0, crypto_1.randomUUID)(),
179
+ topic: `${this.namespace}:${topic}`,
180
+ source_peer_id: this.peerId,
181
+ timestamp: Date.now(),
182
+ payload: this.topicCodec.encode(topic, data),
183
+ };
184
+ const raw = this.envelopeCodec.encode(envelope);
185
+ if (raw.length > this.maxMessageBytes) {
186
+ this.stats.dropped_oversized += 1;
187
+ return;
188
+ }
189
+ await this.post("/direct/send", {
190
+ room: this.room,
191
+ from_peer_id: this.peerId,
192
+ to_peer_id: targetPeerId,
193
+ envelope,
194
+ });
195
+ this.lastPublishAt = Date.now();
196
+ this.signalingMessagesSentTotal += 1;
197
+ }
198
+ subscribeDirect(topic, handler) {
199
+ const key = `${this.namespace}:${topic}`;
200
+ if (!this.directHandlers.has(key)) {
201
+ this.directHandlers.set(key, new Set());
202
+ }
203
+ this.directHandlers.get(key)?.add(handler);
204
+ }
169
205
  getDiagnostics() {
170
206
  const peerItems = Array.from(this.peers.values()).sort((a, b) => b.last_seen_at - a.last_seen_at);
171
207
  return {
@@ -235,6 +271,15 @@ class RelayPreviewAdapter {
235
271
  this.signalingMessagesReceivedTotal += 1;
236
272
  this.onEnvelope(message?.envelope);
237
273
  }
274
+ let directMessages = Array.isArray(payload?.direct_messages) ? payload.direct_messages : null;
275
+ if (!directMessages) {
276
+ const directPayload = await this.get(`/direct/poll?room=${encodeURIComponent(this.room)}&peer_id=${encodeURIComponent(this.peerId)}`);
277
+ directMessages = Array.isArray(directPayload?.messages) ? directPayload.messages : [];
278
+ }
279
+ for (const message of directMessages) {
280
+ this.signalingMessagesReceivedTotal += 1;
281
+ this.onDirectEnvelope(message?.envelope, { peerId: String(message?.from_peer_id || "") || undefined });
282
+ }
238
283
  if (Array.isArray(payload?.peers)) {
239
284
  this.updatePeersFromList(payload.peers);
240
285
  }
@@ -258,10 +303,18 @@ class RelayPreviewAdapter {
258
303
  const payload = await this.get(`/peers?room=${encodeURIComponent(this.room)}`);
259
304
  this.lastPeerRefreshAt = Date.now();
260
305
  this.stats.peers_refresh_succeeded += 1;
261
- const peerIds = Array.isArray(payload?.peers) ? payload.peers : [];
262
- this.updatePeersFromList(peerIds);
306
+ const peerItems = Array.isArray(payload?.peer_details) && payload.peer_details.length
307
+ ? payload.peer_details
308
+ : Array.isArray(payload?.peers) ? payload.peers : [];
309
+ this.updatePeersFromList(peerItems);
263
310
  }
264
311
  onEnvelope(envelope) {
312
+ this.dispatchEnvelope(envelope, this.handlers);
313
+ }
314
+ onDirectEnvelope(envelope, meta) {
315
+ this.dispatchEnvelope(envelope, this.directHandlers, meta);
316
+ }
317
+ dispatchEnvelope(envelope, handlersByTopic, meta) {
265
318
  this.stats.received_total += 1;
266
319
  const validated = (0, messageEnvelope_1.validateNetworkMessageEnvelope)(envelope, {
267
320
  max_future_drift_ms: this.maxFutureDriftMs,
@@ -300,7 +353,7 @@ class RelayPreviewAdapter {
300
353
  this.stats.received_validated += 1;
301
354
  const topicKey = message.topic;
302
355
  const topic = topicKey.slice(this.namespace.length + 1);
303
- const handlers = this.handlers.get(topicKey);
356
+ const handlers = handlersByTopic.get(topicKey);
304
357
  if (!handlers || handlers.size === 0)
305
358
  return;
306
359
  const peer = this.peers.get(message.source_peer_id);
@@ -318,7 +371,7 @@ class RelayPreviewAdapter {
318
371
  }
319
372
  for (const handler of handlers) {
320
373
  try {
321
- handler(payload);
374
+ handler(payload, meta || { peerId: message.source_peer_id });
322
375
  this.stats.delivered_total += 1;
323
376
  }
324
377
  catch {
@@ -340,15 +393,20 @@ class RelayPreviewAdapter {
340
393
  }
341
394
  async joinRoom(reason) {
342
395
  this.stats.join_attempted += 1;
343
- await this.post("/join", { room: this.room, peer_id: this.peerId });
396
+ const payload = await this.post("/join", { room: this.room, peer_id: this.peerId });
344
397
  this.lastJoinAt = Date.now();
345
398
  this.stats.join_succeeded += 1;
399
+ if (Array.isArray(payload?.peers)) {
400
+ this.updatePeersFromList(payload.peers);
401
+ this.lastPeerRefreshAt = this.lastJoinAt;
402
+ }
346
403
  this.recordDiscovery("join_ok", { endpoint: this.activeEndpoint, detail: reason });
347
404
  }
348
405
  async maybeRefreshJoin(reason) {
349
406
  if (!this.lastJoinAt || Date.now() - this.lastJoinAt > Math.max(45_000, this.pollIntervalMs * 6)) {
350
407
  await this.joinRoom(reason);
351
408
  }
409
+ this.ensurePollingAlive(reason);
352
410
  }
353
411
  async get(path) {
354
412
  return this.requestJson("GET", path);
@@ -406,13 +464,38 @@ class RelayPreviewAdapter {
406
464
  throw new Error(errors.join(" | "));
407
465
  }
408
466
  updatePeersFromList(values) {
409
- const peerIds = values.map((value) => String(value || "").trim()).filter(Boolean);
467
+ const parsedPeers = [];
468
+ for (const value of values) {
469
+ if (typeof value === "string") {
470
+ const peerId = String(value || "").trim();
471
+ if (peerId) {
472
+ parsedPeers.push({ peer_id: peerId });
473
+ }
474
+ continue;
475
+ }
476
+ if (value && typeof value === "object") {
477
+ const raw = value;
478
+ const peerId = String(raw.peer_id || "").trim();
479
+ if (!peerId) {
480
+ continue;
481
+ }
482
+ parsedPeers.push({
483
+ peer_id: peerId,
484
+ meta: {
485
+ signal_queue_size: Number(raw.signal_queue_size ?? 0),
486
+ relay_queue_size: Number(raw.relay_queue_size ?? 0),
487
+ },
488
+ });
489
+ }
490
+ }
491
+ const peerIds = parsedPeers.map((peer) => peer.peer_id);
410
492
  if (!peerIds.includes(this.peerId)) {
411
493
  void this.joinRoom("self_missing_from_peers").catch(() => { });
412
494
  }
413
495
  const now = Date.now();
414
496
  const next = new Map();
415
- for (const peerId of peerIds) {
497
+ for (const peerInfo of parsedPeers) {
498
+ const peerId = peerInfo.peer_id;
416
499
  if (peerId === this.peerId)
417
500
  continue;
418
501
  const existing = this.peers.get(peerId);
@@ -426,6 +509,7 @@ class RelayPreviewAdapter {
426
509
  last_seen_at: now,
427
510
  messages_seen: existing?.messages_seen ?? 0,
428
511
  reconnect_attempts: existing?.reconnect_attempts ?? 0,
512
+ meta: peerInfo.meta || existing?.meta,
429
513
  });
430
514
  }
431
515
  for (const peerId of this.peers.keys()) {
@@ -444,5 +528,21 @@ class RelayPreviewAdapter {
444
528
  this.pollOnce().catch(() => { });
445
529
  }, Math.max(1000, delayMs + jitterMs));
446
530
  }
531
+ ensurePollingAlive(reason) {
532
+ if (!this.started)
533
+ return;
534
+ const pollStaleMs = Math.max(45_000, this.pollIntervalMs * 6);
535
+ const pollMissing = !this.poller;
536
+ const pollStale = Boolean(this.lastPollAt) && Date.now() - this.lastPollAt > pollStaleMs;
537
+ if (!pollMissing && !pollStale) {
538
+ return;
539
+ }
540
+ this.recordDiscovery("poll_recover_scheduled", {
541
+ endpoint: this.activeEndpoint,
542
+ detail: `${reason}:${pollMissing ? "missing" : "stale"}`,
543
+ });
544
+ this.currentPollDelayMs = this.pollIntervalMs;
545
+ this.scheduleNextPoll(0);
546
+ }
447
547
  }
448
548
  exports.RelayPreviewAdapter = RelayPreviewAdapter;
@@ -3,4 +3,8 @@ export interface NetworkAdapter {
3
3
  stop(): Promise<void>;
4
4
  publish(topic: string, data: any): Promise<void>;
5
5
  subscribe(topic: string, handler: (data: any) => void): void;
6
+ sendDirect?(peerId: string, topic: string, data: any): Promise<void>;
7
+ subscribeDirect?(topic: string, handler: (data: any, meta?: {
8
+ peerId?: string;
9
+ }) => void): void;
6
10
  }