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 +64 -0
- package/lib/mcp.js +66 -0
- package/package.json +1 -1
- package/skill/SKILL.md +18 -0
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
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
|