@firstperson/firstperson 2026.1.36 → 2026.1.37

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@firstperson/firstperson",
3
- "version": "2026.1.36",
3
+ "version": "2026.1.37",
4
4
  "type": "module",
5
5
  "description": "OpenClaw channel plugin for the First Person iOS app",
6
6
  "main": "index.ts",
package/src/channel.ts CHANGED
@@ -318,7 +318,7 @@ export const firstPersonPlugin: ChannelPlugin<ResolvedFirstPersonAccount> = {
318
318
  lastError: null,
319
319
  });
320
320
 
321
- const { startRelayConnection, sendStatusUpdate } = await import("./relay-client.js");
321
+ const { startRelayConnection, sendStatusUpdate, pushProfileToRelay } = await import("./relay-client.js");
322
322
 
323
323
  return startRelayConnection({
324
324
  relayUrl: account.relayUrl,
@@ -335,6 +335,22 @@ export const firstPersonPlugin: ChannelPlugin<ResolvedFirstPersonAccount> = {
335
335
  lastError: null,
336
336
  });
337
337
  log?.info(`[firstperson:${account.accountId}] relay connected`);
338
+
339
+ // Push agent profile to relay on connection
340
+ const globalRuntime = getFirstPersonRuntime();
341
+ const identity = (globalRuntime as any).agent?.identity?.get?.();
342
+ if (identity) {
343
+ pushProfileToRelay({
344
+ relayUrl: account.relayUrl,
345
+ token: account.token!,
346
+ identity,
347
+ log,
348
+ }).catch((err) => {
349
+ log?.warn(`[firstperson:${account.accountId}] profile push failed: ${err.message}`);
350
+ });
351
+ } else {
352
+ log?.info(`[firstperson:${account.accountId}] no agent identity available, skipping profile push`);
353
+ }
338
354
  },
339
355
  onDisconnected: (error) => {
340
356
  setStatus({
@@ -1,5 +1,62 @@
1
1
  import WebSocket from "ws";
2
2
 
3
+ /**
4
+ * Agent identity from OpenClaw runtime
5
+ */
6
+ export interface AgentIdentity {
7
+ agentId: string;
8
+ name?: string;
9
+ avatar?: string;
10
+ }
11
+
12
+ /**
13
+ * Push the agent's profile to the relay.
14
+ * Called on startup after relay connection is established.
15
+ */
16
+ export async function pushProfileToRelay(params: {
17
+ relayUrl: string;
18
+ token: string;
19
+ identity: AgentIdentity;
20
+ log?: Logger;
21
+ }): Promise<void> {
22
+ const { relayUrl, token, identity, log } = params;
23
+
24
+ // Convert WebSocket URL to HTTP URL for API call
25
+ const httpUrl = relayUrl.replace(/^wss:\/\//, "https://").replace(/^ws:\/\//, "http://");
26
+ const profileUrl = `${httpUrl}/api/v1/profile`;
27
+
28
+ // Map OpenClaw identity to relay profile format
29
+ // Only include avatar_url if it's actually an HTTPS URL (relay validates this)
30
+ const avatarUrl = identity.avatar?.startsWith("https://") ? identity.avatar : "";
31
+
32
+ const profile = {
33
+ name: identity.name ?? identity.agentId,
34
+ bio: "",
35
+ avatar_url: avatarUrl,
36
+ };
37
+
38
+ try {
39
+ const response = await fetch(profileUrl, {
40
+ method: "PUT",
41
+ headers: {
42
+ "Content-Type": "application/json",
43
+ Authorization: `Bearer ${token}`,
44
+ },
45
+ body: JSON.stringify(profile),
46
+ });
47
+
48
+ if (!response.ok) {
49
+ const errorText = await response.text().catch(() => "unknown error");
50
+ log?.warn(`[relay-client] Failed to push profile: ${response.status} ${errorText}`);
51
+ return;
52
+ }
53
+
54
+ log?.info(`[relay-client] Profile pushed to relay: ${profile.name}`);
55
+ } catch (err) {
56
+ log?.warn(`[relay-client] Failed to push profile: ${(err as Error).message}`);
57
+ }
58
+ }
59
+
3
60
  export interface Logger {
4
61
  info: (msg: string) => void;
5
62
  warn: (msg: string) => void;