antenna-fyi 1.3.12 → 1.3.13

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/lib/core.js CHANGED
@@ -189,6 +189,10 @@ export const PROFILE_FIELDS = {
189
189
  line2: { label: "想认识的人", description: "The kind of people you want to meet", maxLength: 140 },
190
190
  line3: { label: "想要的交流方式", description: "The type of conversations you want", maxLength: 160 },
191
191
  matching_context: { label: "匹配上下文", description: "Agent-generated rich context for better matching (not shown to others)", maxLength: 1000 },
192
+ interest_tags: { label: "兴趣标签", description: "Interest/topic tags shown on the card (up to 8)", maxItems: 8 },
193
+ city: { label: "国家/地区", description: "Country or region" },
194
+ links: { label: "社交链接", description: "Social links shown on the card footer (up to 3)", maxItems: 3 },
195
+ is_active: { label: "状态", description: "Whether the profile is active or quiet" },
192
196
  };
193
197
 
194
198
  // ─── getProfile ──────────────────────────────────────────────────────
@@ -197,7 +201,21 @@ export async function getProfile({ device_id, supabaseUrl, supabaseKey }) {
197
201
  const sb = getClient(supabaseUrl, supabaseKey);
198
202
  const { data, error } = await sb.rpc("get_profile", { p_device_id: device_id });
199
203
  if (error) throw new Error(error.message);
200
- return data || null;
204
+ if (!data) return null;
205
+
206
+ // Unpack matching_context JSON into top-level fields for easier agent consumption
207
+ if (data.matching_context) {
208
+ try {
209
+ const ctx = JSON.parse(data.matching_context);
210
+ data.interest_tags = ctx.interestTags || [];
211
+ data.city = ctx.city || null;
212
+ data.links = ctx.links || [];
213
+ data.is_active = typeof ctx.isActive === "boolean" ? ctx.isActive : true;
214
+ data.archetype_override = ctx.archetypeOverride || null;
215
+ data.context = ctx.context || null;
216
+ } catch {}
217
+ }
218
+ return data;
201
219
  }
202
220
 
203
221
  // ─── setProfile ──────────────────────────────────────────────────────
@@ -211,10 +229,42 @@ export async function setProfile({
211
229
  line3,
212
230
  matching_context,
213
231
  visible = true,
232
+ interest_tags,
233
+ city,
234
+ links,
235
+ profile_slug,
236
+ is_active,
237
+ archetype_override,
214
238
  supabaseUrl,
215
239
  supabaseKey,
216
240
  }) {
217
241
  const sb = getClient(supabaseUrl, supabaseKey);
242
+
243
+ // Pack structured fields into matching_context JSON
244
+ let contextJson = matching_context;
245
+ if (interest_tags || city || links || is_active !== undefined || archetype_override !== undefined) {
246
+ let existing = {};
247
+ // If matching_context is already JSON, parse and merge
248
+ if (matching_context) {
249
+ try { existing = JSON.parse(matching_context); } catch { existing = { context: matching_context }; }
250
+ } else {
251
+ // Read existing matching_context from DB to merge
252
+ try {
253
+ const current = await getProfile({ device_id, supabaseUrl, supabaseKey });
254
+ if (current?.matching_context) {
255
+ try { existing = JSON.parse(current.matching_context); } catch { existing = {}; }
256
+ }
257
+ } catch {}
258
+ }
259
+ if (interest_tags) existing.interestTags = interest_tags;
260
+ if (city) existing.city = city;
261
+ if (links) existing.links = links;
262
+ if (is_active !== undefined) existing.isActive = is_active;
263
+ if (archetype_override !== undefined) existing.archetypeOverride = archetype_override;
264
+ existing.version = existing.version || 1;
265
+ contextJson = JSON.stringify(existing);
266
+ }
267
+
218
268
  const { data, error } = await sb.rpc("upsert_profile", {
219
269
  p_device_id: device_id,
220
270
  p_display_name: display_name || null,
@@ -223,7 +273,7 @@ export async function setProfile({
223
273
  p_line2: line2 || null,
224
274
  p_line3: line3 || null,
225
275
  p_visible: visible,
226
- p_matching_context: matching_context || null,
276
+ p_matching_context: contextJson || null,
227
277
  });
228
278
  if (error) throw new Error(error.message);
229
279
 
package/lib/mcp.js CHANGED
@@ -116,9 +116,13 @@ export async function startMcpServer() {
116
116
  line2: z.string().optional().describe("Looking for — the kind of people you want to meet (max 140 chars)"),
117
117
  line3: z.string().optional().describe("Conversation style — the type of conversations you want (max 160 chars)"),
118
118
  matching_context: z.string().optional().describe("Agent-generated rich context for embedding-based matching (not shown to others, max 1000 chars). Generate this FIRST, then derive the three descriptions from it."),
119
+ interest_tags: z.array(z.string()).optional().describe("Interest/topic tags shown on the card (up to 8)"),
120
+ city: z.string().optional().describe("Country or region (e.g. 'United States', 'Beijing')"),
121
+ links: z.array(z.string()).optional().describe("Social links shown on the card footer (up to 3)"),
122
+ is_active: z.boolean().optional().describe("Whether the profile is active or quiet"),
119
123
  visible: z.boolean().optional().default(true),
120
124
  },
121
- async ({ action, sender_id, channel, display_name, emoji, line1, line2, line3, matching_context, visible }) => {
125
+ async ({ action, sender_id, channel, display_name, emoji, line1, line2, line3, matching_context, interest_tags, city, links, is_active, visible }) => {
122
126
  const deviceId = deriveDeviceId(sender_id, channel);
123
127
  try {
124
128
  if (action === "get") {
@@ -128,7 +132,7 @@ export async function startMcpServer() {
128
132
  : { profile: null, message: "还没有名片。跟用户聊聊他们是谁、做什么、想认识什么人,然后帮他们创建。", fields: PROFILE_FIELDS };
129
133
  return jsonResult(await withMatchNotifications(deviceId, result));
130
134
  }
131
- const data = await setProfile({ device_id: deviceId, display_name, emoji, line1, line2, line3, matching_context, visible });
135
+ const data = await setProfile({ device_id: deviceId, display_name, emoji, line1, line2, line3, matching_context, interest_tags, city, links, is_active, visible });
132
136
  return jsonResult(await withMatchNotifications(deviceId, { saved: true, profile: data }));
133
137
  } catch (e) {
134
138
  return jsonResult({ error: e.message });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "antenna-fyi",
3
- "version": "1.3.12",
3
+ "version": "1.3.13",
4
4
  "description": "Antenna \u2014 nearby people discovery. CLI + MCP server + OpenClaw skill & plugin, all in one package.",
5
5
  "type": "module",
6
6
  "bin": {