antenna-openclaw-plugin 1.3.10 โ†’ 1.3.23

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 (2) hide show
  1. package/index.ts +146 -7
  2. package/package.json +1 -1
package/index.ts CHANGED
@@ -26,6 +26,8 @@ interface Profile {
26
26
  line2: string | null;
27
27
  line3: string | null;
28
28
  emoji: string | null;
29
+ profile_slug: string | null;
30
+ matching_context: string | null;
29
31
  visible: boolean;
30
32
  last_seen_at?: string;
31
33
  }
@@ -315,7 +317,7 @@ export default function register(api: any) {
315
317
  const gProfiles = globalOthers.map((p: any, i: number) => {
316
318
  const ref = String(i + 1);
317
319
  gRefMap[ref] = p.device_id;
318
- return { ref, emoji: p.emoji || "๐Ÿ‘ค", name: p.display_name || "ๅŒฟๅ", line1: p.line1, line2: p.line2, line3: p.line3 };
320
+ return { ref, emoji: p.emoji || "๐Ÿ‘ค", name: p.display_name || "ๅŒฟๅ", line1: p.line1, line2: p.line2, line3: p.line3, more_information: p.matching_context || null, profile_slug: p.profile_slug || null };
319
321
  });
320
322
  (api as any)._antennaRefMap = gRefMap;
321
323
  try { await supabase.rpc("save_scan_refs", { p_owner: deviceId, p_refs: gRefMap }); } catch {}
@@ -342,6 +344,8 @@ export default function register(api: any) {
342
344
  line1: p.line1,
343
345
  line2: p.line2,
344
346
  line3: p.line3,
347
+ more_information: p.matching_context || null,
348
+ profile_slug: p.profile_slug || null,
345
349
  distance_m: p.distance_m ?? p.dist_meters ?? null,
346
350
  };
347
351
  });
@@ -381,7 +385,7 @@ export default function register(api: any) {
381
385
  line2: { type: "string", description: "Second line (what you're into)" },
382
386
  line3: { type: "string", description: "Third line (what you're looking for)" },
383
387
  visible: { type: "boolean", description: "Whether to be visible to others" },
384
- matching_context: { type: "string", description: "Free-form context for AI matching (interests, goals, etc.)" },
388
+ matching_context: { type: "string", description: "More information / free-form context for AI matching (interests, goals, background, etc.)" },
385
389
  },
386
390
  required: ["action", "sender_id", "channel", "chat_id"],
387
391
  },
@@ -475,7 +479,7 @@ export default function register(api: any) {
475
479
  api.registerTool({
476
480
  name: "antenna_accept",
477
481
  description:
478
- "Accept a match. Use 'ref' from scan results (e.g. '1', '2') or target_device_id. Optionally share contact info.",
482
+ "Accept a match. Use 'ref' from scan results (e.g. '1', '2'), target_device_id, or profile_slug (from a public profile link like antenna.fyi/p/SLUG). Optionally share contact info.",
479
483
  parameters: {
480
484
  type: "object",
481
485
  properties: {
@@ -483,7 +487,8 @@ export default function register(api: any) {
483
487
  channel: { type: "string" },
484
488
  chat_id: { type: "string", description: "REQUIRED for notifications. Pass the chat/channel ID from your message context so Antenna can send you match and event notifications." },
485
489
  ref: { type: "string", description: "Ref number from scan results (e.g. '1')" },
486
- target_device_id: { type: "string", description: "Device ID (use ref instead when possible)" },
490
+ target_device_id: { type: "string", description: "Device ID (use ref or profile_slug instead when possible)" },
491
+ profile_slug: { type: "string", description: "Profile slug from a public profile link (e.g. 'yi' from antenna.fyi/p/yi). Resolves to device_id automatically." },
487
492
  contact_info: { type: "string", description: "Optional contact info to share" },
488
493
  },
489
494
  required: ["sender_id", "channel", "chat_id"],
@@ -500,8 +505,16 @@ export default function register(api: any) {
500
505
  const { data: resolved } = await supabase.rpc("resolve_ref", { p_owner: deviceId, p_ref: params.ref });
501
506
  targetId = resolved || (api as any)._antennaRefMap?.[params.ref];
502
507
  }
508
+ // Resolve profile_slug to device_id via get_profile_by_slug RPC
509
+ if (!targetId && params.profile_slug) {
510
+ const { data: slugProfile } = await supabase.rpc("get_profile_by_slug", { p_slug: params.profile_slug });
511
+ const resolved = Array.isArray(slugProfile) ? slugProfile[0] : slugProfile;
512
+ if (resolved?.device_id) {
513
+ targetId = resolved.device_id;
514
+ }
515
+ }
503
516
  if (!targetId) {
504
- return ok({ error: "No target. Ref may have expired โ€” try scanning again." });
517
+ return ok({ error: "No target. Provide ref, target_device_id, or profile_slug." });
505
518
  }
506
519
 
507
520
  const { error } = await supabase.rpc("upsert_match", {
@@ -589,6 +602,45 @@ export default function register(api: any) {
589
602
  },
590
603
  });
591
604
 
605
+ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
606
+ // Tool: antenna_link_account
607
+ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
608
+ api.registerTool({
609
+ name: "antenna_link_account",
610
+ description:
611
+ "Link your Antenna agent profile to your antenna.fyi website account. The user needs to provide their user_id from the dashboard (antenna.fyi/me). After linking, the dashboard will show the same profile and match history.",
612
+ parameters: {
613
+ type: "object",
614
+ properties: {
615
+ sender_id: { type: "string", description: "The sender's user ID" },
616
+ channel: { type: "string", description: "The channel name" },
617
+ chat_id: { type: "string", description: "REQUIRED. Pass the chat/channel ID from your message context." },
618
+ user_id: { type: "string", description: "The user's antenna.fyi account UUID, visible on their dashboard" },
619
+ },
620
+ required: ["sender_id", "channel", "chat_id", "user_id"],
621
+ },
622
+ async execute(_id: string, params: any) {
623
+ const cfg = getConfig(api);
624
+ const supabase = getSupabase(cfg);
625
+ const deviceId = deriveDeviceId(params.sender_id, params.channel, params.chat_id);
626
+
627
+ const { data, error } = await supabase.rpc("bind_user_id", {
628
+ p_device_id: deviceId,
629
+ p_user_id: params.user_id,
630
+ });
631
+ if (error) return ok({ error: error.message });
632
+
633
+ if (data?.error) {
634
+ return ok(data);
635
+ }
636
+
637
+ return ok({
638
+ ...data,
639
+ message: "่ดฆๅทๅทฒๅ…ณ่”๏ผ็Žฐๅœจไฝ ๅฏไปฅๅœจ antenna.fyi/me ็œ‹ๅˆฐไฝ ็š„ๅฎŒๆ•ด profile ๅ’ŒๅŒน้…่ฎฐๅฝ•ไบ†ใ€‚",
640
+ });
641
+ },
642
+ });
643
+
592
644
  // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
593
645
  // Tool: antenna_discover
594
646
  // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
@@ -651,7 +703,7 @@ export default function register(api: any) {
651
703
  } catch { /* best effort */ }
652
704
  }
653
705
 
654
- profiles.push({ ref, emoji: p.emoji || "๐Ÿ‘ค", name: p.display_name || "ๅŒฟๅ", line1: p.line1, line2: p.line2, line3: p.line3, match_reason });
706
+ profiles.push({ ref, emoji: p.emoji || "๐Ÿ‘ค", name: p.display_name || "ๅŒฟๅ", line1: p.line1, line2: p.line2, line3: p.line3, more_information: p.matching_context || null, profile_slug: p.profile_slug || null, match_reason });
655
707
  }
656
708
 
657
709
  // Persist refs + log recommendation
@@ -670,6 +722,93 @@ export default function register(api: any) {
670
722
  },
671
723
  });
672
724
 
725
+ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
726
+ // Tool: antenna_initial_recommendations
727
+ // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
728
+ api.registerTool({
729
+ name: "antenna_initial_recommendations",
730
+ description:
731
+ "Get initial recommendations for a new user โ€” 2-3 people most similar to them. One-time only, does NOT consume daily discover quota. Use right after profile creation in onboarding.",
732
+ parameters: {
733
+ type: "object",
734
+ properties: {
735
+ sender_id: { type: "string", description: "The sender's user ID" },
736
+ channel: { type: "string", description: "The channel name" },
737
+ chat_id: { type: "string", description: "REQUIRED for notifications. Pass the chat/channel ID from your message context so Antenna can send you match and event notifications." },
738
+ },
739
+ required: ["sender_id", "channel", "chat_id"],
740
+ },
741
+ async execute(_id: string, params: any) {
742
+ const cfg = getConfig(api);
743
+ const supabase = getSupabase(cfg);
744
+ const deviceId = deriveDeviceId(params.sender_id, params.channel, params.chat_id);
745
+
746
+ const { data: results, error } = await supabase.rpc("initial_recommendations", {
747
+ p_device_id: deviceId,
748
+ p_limit: 3,
749
+ });
750
+
751
+ if (error) return ok({ error: error.message });
752
+
753
+ if (!results || results.length === 0) {
754
+ return ok({
755
+ count: 0,
756
+ profiles: [],
757
+ initial: true,
758
+ message: "ๆš‚ๆ—ถๆฒกๆœ‰ๆŽจ่๏ผŒ็ญ‰ๆœ‰ๆ›ดๅคšไบบๅŠ ๅ…ฅ๏ผ",
759
+ });
760
+ }
761
+
762
+ const _refMap: Record<string, string> = {};
763
+
764
+ // Get my profile for match reason generation
765
+ const { data: myProfile } = await supabase.rpc("get_profile", { p_device_id: deviceId });
766
+ const myLines = myProfile ? [myProfile.line1, myProfile.line2, myProfile.line3].filter(Boolean).join(". ") : "";
767
+
768
+ const profiles = [];
769
+ for (let i = 0; i < results.length; i++) {
770
+ const p = results[i] as any;
771
+ const ref = String(i + 1);
772
+ _refMap[ref] = p.device_id;
773
+
774
+ const theirLines = [p.line1, p.line2, p.line3].filter(Boolean).join(". ");
775
+ let match_reason: string | null = null;
776
+
777
+ if (myLines && theirLines) {
778
+ try {
779
+ const supabaseUrl = cfg.supabaseUrl || BUILTIN_SUPABASE_URL;
780
+ const supabaseKey = cfg.supabaseKey || BUILTIN_SUPABASE_ANON_KEY;
781
+ const res = await fetch(`${supabaseUrl}/functions/v1/generate-match-reason`, {
782
+ method: "POST",
783
+ headers: { "Content-Type": "application/json", "Authorization": `Bearer ${supabaseKey}` },
784
+ body: JSON.stringify({ my_lines: myLines, their_lines: theirLines }),
785
+ });
786
+ if (res.ok) {
787
+ const data = await res.json();
788
+ match_reason = data?.reason || null;
789
+ }
790
+ } catch { /* best effort */ }
791
+ }
792
+
793
+ profiles.push({ ref, emoji: p.emoji || "๐Ÿ‘ค", name: p.display_name || "ๅŒฟๅ", personal_description: p.line1, looking_for: p.line2, conversation_style: p.line3, more_information: p.matching_context || null, profile_slug: p.profile_slug || null, match_reason });
794
+ }
795
+
796
+ // Persist refs + log recommendations
797
+ (api as any)._antennaRefMap = { ...(api as any)._antennaRefMap, ..._refMap };
798
+ try {
799
+ await supabase.rpc("save_scan_refs", { p_owner: deviceId, p_refs: _refMap });
800
+ } catch { /* best effort */ }
801
+ for (const p of results) {
802
+ await supabase.rpc("log_recommendation", { p_device_id: deviceId, p_recommended_id: (p as any).device_id });
803
+ }
804
+
805
+ return ok({
806
+ count: profiles.length, profiles, initial: true,
807
+ message: "่ฟ™ๆ˜ฏไฝ ็š„้ฆ–ๆฌกๆŽจ่โ€”โ€”ๅŸบไบŽไฝ ็š„ๅ็‰‡๏ผŒ่ฟ™ๅ‡ ไธชไบบ่ทŸไฝ ๆœ€ๅŒน้…ใ€‚",
808
+ });
809
+ },
810
+ });
811
+
673
812
  // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
674
813
  // Tool: antenna_event_create
675
814
  // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
@@ -858,7 +997,7 @@ export default function register(api: any) {
858
997
  const profiles = others.map((p, i) => {
859
998
  const ref = String(i + 1);
860
999
  _refMap[ref] = p.device_id;
861
- return { ref, emoji: p.emoji || "๐Ÿ‘ค", name: p.display_name || "ๅŒฟๅ", line1: p.line1, line2: p.line2, line3: p.line3, checked_in: !!p.checked_in, role: p.role || "participant", status: p.status || "active", application_context: p.application_context || null, source: "event" };
1000
+ return { ref, emoji: p.emoji || "๐Ÿ‘ค", name: p.display_name || "ๅŒฟๅ", line1: p.line1, line2: p.line2, line3: p.line3, more_information: p.matching_context || null, profile_slug: p.profile_slug || null, checked_in: !!p.checked_in, role: p.role || "participant", status: p.status || "active", application_context: p.application_context || null, source: "event" };
862
1001
  });
863
1002
 
864
1003
  (api as any)._antennaRefMap = { ...(api as any)._antennaRefMap, ..._refMap };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "antenna-openclaw-plugin",
3
- "version": "1.3.10",
3
+ "version": "1.3.23",
4
4
  "description": "Antenna \u2014 agent-mediated nearby people discovery for OpenClaw",
5
5
  "openclaw": {
6
6
  "extensions": [