antenna-fyi 1.1.0 → 1.2.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.
package/lib/core.js CHANGED
@@ -474,6 +474,70 @@ export async function pass({ device_id, target_device_id, ref, supabaseUrl, supa
474
474
  return { passed: true, message: "已跳过,下次不会再推荐这个人。" };
475
475
  }
476
476
 
477
+ // ─── events ─────────────────────────────────────────────────────────
478
+
479
+ export async function createEvent({ name, lat, lng, device_id, starts_at, ends_at, supabaseUrl, supabaseKey }) {
480
+ const sb = getClient(supabaseUrl, supabaseKey);
481
+ const { data, error } = await sb.rpc("create_event", {
482
+ p_name: name,
483
+ p_lat: lat || null,
484
+ p_lng: lng || null,
485
+ p_created_by: device_id || null,
486
+ p_starts_at: starts_at || new Date().toISOString(),
487
+ p_ends_at: ends_at || new Date(Date.now() + 12 * 60 * 60 * 1000).toISOString(),
488
+ });
489
+ if (error) throw new Error(error.message);
490
+ return data;
491
+ }
492
+
493
+ export async function joinEvent({ code, device_id, supabaseUrl, supabaseKey }) {
494
+ const sb = getClient(supabaseUrl, supabaseKey);
495
+ const { data, error } = await sb.rpc("join_event", { p_code: code, p_device_id: device_id });
496
+ if (error) throw new Error(error.message);
497
+ return data;
498
+ }
499
+
500
+ export async function eventScan({ code, device_id, supabaseUrl, supabaseKey }) {
501
+ const sb = getClient(supabaseUrl, supabaseKey);
502
+ const { data, error } = await sb.rpc("event_participants_list", { p_code: code, p_device_id: device_id });
503
+ if (error) throw new Error(error.message);
504
+
505
+ const others = data || [];
506
+ const _refMap = {};
507
+ const profiles = others.map((p, i) => {
508
+ const ref = String(i + 1);
509
+ _refMap[ref] = p.device_id;
510
+ return {
511
+ ref,
512
+ name: p.display_name || "匿名",
513
+ emoji: p.emoji || "👤",
514
+ line1: p.line1,
515
+ line2: p.line2,
516
+ line3: p.line3,
517
+ source: "event",
518
+ };
519
+ });
520
+
521
+ // Persist refs
522
+ if (device_id && Object.keys(_refMap).length > 0) {
523
+ try { await sb.rpc("save_scan_refs", { p_owner: device_id, p_refs: JSON.stringify(_refMap) }); } catch {}
524
+ }
525
+
526
+ return {
527
+ count: profiles.length,
528
+ profiles,
529
+ _ref_map: _refMap,
530
+ event: true,
531
+ };
532
+ }
533
+
534
+ export async function getEvent({ code, supabaseUrl, supabaseKey }) {
535
+ const sb = getClient(supabaseUrl, supabaseKey);
536
+ const { data, error } = await sb.rpc("get_event", { p_code: code });
537
+ if (error) throw new Error(error.message);
538
+ return data;
539
+ }
540
+
477
541
  export async function createBindToken({ device_id, supabaseUrl, supabaseKey }) {
478
542
  const sb = getClient(supabaseUrl, supabaseKey);
479
543
  const { data, error } = await sb.rpc("create_bind_token", { p_device_id: device_id });
package/lib/mcp.js CHANGED
@@ -13,6 +13,9 @@ import {
13
13
  checkin,
14
14
  createBindToken,
15
15
  discover,
16
+ createEvent,
17
+ joinEvent,
18
+ eventScan,
16
19
  deriveDeviceId,
17
20
  } from "./core.js";
18
21
 
@@ -216,6 +219,69 @@ export async function startMcpServer() {
216
219
  }
217
220
  );
218
221
 
222
+ // ─── antenna_event_create ──────────────────────────────────
223
+
224
+ server.tool(
225
+ "antenna_event_create",
226
+ "Create an event. Returns a shareable link (antenna.fyi/e/CODE) for participants to join.",
227
+ {
228
+ name: z.string().describe("Event name"),
229
+ sender_id: z.string().describe("Creator's user ID"),
230
+ channel: z.string().describe("Channel name"),
231
+ lat: z.number().optional().describe("Event latitude"),
232
+ lng: z.number().optional().describe("Event longitude"),
233
+ starts_at: z.string().optional().describe("Start time ISO string"),
234
+ ends_at: z.string().optional().describe("End time ISO string"),
235
+ },
236
+ async ({ name, sender_id, channel, lat, lng, starts_at, ends_at }) => {
237
+ try {
238
+ const result = await createEvent({ name, lat, lng, device_id: deriveDeviceId(sender_id, channel), starts_at, ends_at });
239
+ return jsonResult(result);
240
+ } catch (e) { return jsonResult({ error: e.message }); }
241
+ }
242
+ );
243
+
244
+ // ─── antenna_event_join ────────────────────────────────────
245
+
246
+ server.tool(
247
+ "antenna_event_join",
248
+ "Join an event by its code. The code is from the event URL (antenna.fyi/e/CODE).",
249
+ {
250
+ code: z.string().describe("Event code"),
251
+ sender_id: z.string().describe("The sender's user ID"),
252
+ channel: z.string().describe("Channel name"),
253
+ },
254
+ async ({ code, sender_id, channel }) => {
255
+ try {
256
+ const result = await joinEvent({ code, device_id: deriveDeviceId(sender_id, channel) });
257
+ return jsonResult(result);
258
+ } catch (e) { return jsonResult({ error: e.message }); }
259
+ }
260
+ );
261
+
262
+ // ─── antenna_event_scan ────────────────────────────────────
263
+
264
+ server.tool(
265
+ "antenna_event_scan",
266
+ "Scan people in an event. No distance limit — returns all event participants.",
267
+ {
268
+ code: z.string().describe("Event code"),
269
+ sender_id: z.string().describe("The sender's user ID"),
270
+ channel: z.string().describe("Channel name"),
271
+ },
272
+ async ({ code, sender_id, channel }) => {
273
+ try {
274
+ const result = await eventScan({ code, device_id: deriveDeviceId(sender_id, channel) });
275
+ if (result._ref_map) {
276
+ _lastRefMap = { ..._lastRefMap, ...result._ref_map };
277
+ const { _ref_map, ...clean } = result;
278
+ return jsonResult(clean);
279
+ }
280
+ return jsonResult(result);
281
+ } catch (e) { return jsonResult({ error: e.message }); }
282
+ }
283
+ );
284
+
219
285
  const transport = new StdioServerTransport();
220
286
  await server.connect(transport);
221
287
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "antenna-fyi",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Antenna — nearby people discovery. CLI + MCP server + OpenClaw skill & plugin, all in one package.",
5
5
  "type": "module",
6
6
  "bin": {
package/skill/SKILL.md CHANGED
@@ -251,3 +251,21 @@ Plugin 自带后台服务,每 10 分钟轮询一次 Supabase 查新的 mutual
251
251
  3. 如果对方分享了联系方式,一并展示
252
252
 
253
253
  用户不需要主动问,agent 会自动收到通知。
254
+
255
+ ### `antenna_event_create`
256
+ Create an event. Returns a shareable link (antenna.fyi/e/CODE).
257
+ - `name`: event name
258
+ - `sender_id`, `channel`: from context
259
+ - `lat`, `lng`: optional event location
260
+ - `starts_at`, `ends_at`: optional time range (default: now to +12h)
261
+
262
+ ### `antenna_event_join`
263
+ Join an event by code.
264
+ - `code`: from the event URL (antenna.fyi/e/CODE)
265
+ - `sender_id`, `channel`: from context
266
+
267
+ ### `antenna_event_scan`
268
+ Scan people in an event. No distance limit — returns all participants.
269
+ - `code`: event code
270
+ - `sender_id`, `channel`: from context
271
+ - Returns profiles with `source: "event"` tag