antenna-fyi 1.2.4 → 1.2.5

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/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // antenna CLI command handlers
2
2
 
3
- import { scan, getProfile, setProfile, accept, checkMatches, checkin, createBindToken, discover, createEvent, joinEvent, eventScan, pass as passUser } from "./core.js";
3
+ import { scan, getProfile, setProfile, accept, checkMatches, checkin, createBindToken, discover, createEvent, endEvent, joinEvent, eventScan, pass as passUser } from "./core.js";
4
4
  import { createInterface } from "readline";
5
5
  import { existsSync, mkdirSync, copyFileSync } from "fs";
6
6
  import { join, dirname } from "path";
@@ -47,8 +47,18 @@ export async function handleScan(f) {
47
47
  if (p.line1) console.log(` ${p.line1}`);
48
48
  if (p.line2) console.log(` ${p.line2}`);
49
49
  if (p.line3) console.log(` ${p.line3}`);
50
- console.log(` id: ${p.device_id}\n`);
50
+ console.log(` ref: ${p.ref}\n`);
51
51
  });
52
+
53
+ // Show nearby events
54
+ if (result.nearby_events?.length) {
55
+ console.log(`šŸŽ‰ Nearby events:\n`);
56
+ result.nearby_events.forEach((e) => {
57
+ console.log(` ${e.name} — ${e.participants} people`);
58
+ if (e.description) console.log(` ${e.description}`);
59
+ console.log(` Join: antenna event --join --code ${e.code}\n`);
60
+ });
61
+ }
52
62
  }
53
63
 
54
64
  export async function handleProfile(f) {
@@ -131,11 +141,22 @@ export async function handleDiscover(f) {
131
141
  }
132
142
 
133
143
  export async function handleEvent(f) {
134
- const sub = f._?.[0] || Object.keys(f).find(k => ["create", "join", "scan"].includes(k));
144
+ const sub = f._?.[0] || Object.keys(f).find(k => ["create", "join", "scan", "end"].includes(k));
145
+
146
+ if (f.end) {
147
+ if (!f.code || !f.id) return console.error("Usage: antenna event --end --code abc123 --id telegram:123");
148
+ const result = await endEvent({ code: f.code, device_id: f.id });
149
+ if (result.ended) {
150
+ console.log(`\nāœ… Event ended.\n`);
151
+ } else {
152
+ console.log(`\nāŒ ${result.error || 'Failed to end event'}\n`);
153
+ }
154
+ return;
155
+ }
135
156
 
136
- if (f.create || (!f.join && !f.scan && f.name)) {
137
- if (!f.name) return console.error("Usage: antenna event --create --name 'AI Meetup'");
138
- const result = await createEvent({ name: f.name, device_id: f.id || null, lat: f.lat ? +f.lat : undefined, lng: f.lng ? +f.lng : undefined });
157
+ if (f.create || (!f.join && !f.scan && !f.end && f.name)) {
158
+ if (!f.name) return console.error("Usage: antenna event --create --name 'AI Meetup' [--desc 'description'] [--og-image 'url']");
159
+ const result = await createEvent({ name: f.name, device_id: f.id || null, lat: f.lat ? +f.lat : undefined, lng: f.lng ? +f.lng : undefined, description: f.desc || undefined, og_image: f['og-image'] || undefined });
139
160
  console.log(`\nšŸŽ‰ Event created!\n`);
140
161
  console.log(` Name: ${result.name}`);
141
162
  console.log(` Code: ${result.code}`);
@@ -169,9 +190,10 @@ export async function handleEvent(f) {
169
190
  }
170
191
 
171
192
  console.log(`Usage:
172
- antenna event --create --name 'AI Meetup' [--id telegram:123]
193
+ antenna event --create --name 'AI Meetup' [--id telegram:123] [--desc 'description'] [--og-image 'url']
173
194
  antenna event --join --code abc123 --id telegram:123
174
- antenna event --scan --code abc123 [--id telegram:123]`);
195
+ antenna event --scan --code abc123 [--id telegram:123]
196
+ antenna event --end --code abc123 --id telegram:123`);
175
197
  }
176
198
 
177
199
  export async function handleBind(f) {
@@ -366,7 +388,7 @@ Usage:
366
388
  antenna pass --id telegram:123 --target telegram:789 (or --ref 1)
367
389
  antenna matches --id telegram:123
368
390
  antenna discover --id telegram:123
369
- antenna event --create --name 'AI Meetup' | --join --code abc123 | --scan --code abc123
391
+ antenna event --create --name 'AI Meetup' [--desc '...'] [--og-image 'url'] | --join --code abc123 | --scan --code abc123 | --end --code abc123 --id telegram:123
370
392
  antenna bind --id telegram:123
371
393
  antenna serve Start MCP server (stdio transport)
372
394
  antenna setup Interactive profile setup [--id telegram:123]
package/lib/core.js CHANGED
@@ -161,11 +161,21 @@ export async function scan({ lat, lng, radius_m = 500, device_id, supabaseUrl, s
161
161
  const profs = buildProfiles(others);
162
162
  await saveRefs(_refMap);
163
163
 
164
+ // Fetch nearby events
165
+ let nearby_events = [];
166
+ if (lat != null && lng != null) {
167
+ try {
168
+ const { data: evts } = await sb.rpc("nearby_events", { p_lat: lat, p_lng: lng, p_radius_m: 5000 });
169
+ nearby_events = evts || [];
170
+ } catch { /* best effort */ }
171
+ }
172
+
164
173
  return {
165
174
  count: others.length,
166
175
  radius_m,
167
176
  profiles: profs,
168
177
  _ref_map: _refMap,
178
+ nearby_events,
169
179
  };
170
180
  }
171
181
 
@@ -469,7 +479,7 @@ export async function pass({ device_id, target_device_id, ref, supabaseUrl, supa
469
479
 
470
480
  // ─── events ─────────────────────────────────────────────────────────
471
481
 
472
- export async function createEvent({ name, lat, lng, device_id, starts_at, ends_at, supabaseUrl, supabaseKey }) {
482
+ export async function createEvent({ name, lat, lng, device_id, starts_at, ends_at, description, og_image, supabaseUrl, supabaseKey }) {
473
483
  const sb = getClient(supabaseUrl, supabaseKey);
474
484
  const { data, error } = await sb.rpc("create_event", {
475
485
  p_name: name,
@@ -478,11 +488,20 @@ export async function createEvent({ name, lat, lng, device_id, starts_at, ends_a
478
488
  p_created_by: device_id || null,
479
489
  p_starts_at: starts_at || new Date().toISOString(),
480
490
  p_ends_at: ends_at || new Date(Date.now() + 12 * 60 * 60 * 1000).toISOString(),
491
+ p_description: description || null,
492
+ p_og_image: og_image || null,
481
493
  });
482
494
  if (error) throw new Error(error.message);
483
495
  return data;
484
496
  }
485
497
 
498
+ export async function endEvent({ code, device_id, supabaseUrl, supabaseKey }) {
499
+ const sb = getClient(supabaseUrl, supabaseKey);
500
+ const { data, error } = await sb.rpc("end_event", { p_code: code, p_device_id: device_id });
501
+ if (error) throw new Error(error.message);
502
+ return data;
503
+ }
504
+
486
505
  export async function joinEvent({ code, device_id, supabaseUrl, supabaseKey }) {
487
506
  const sb = getClient(supabaseUrl, supabaseKey);
488
507
  const { data, error } = await sb.rpc("join_event", { p_code: code, p_device_id: device_id });
@@ -18,6 +18,7 @@ from .tools import (
18
18
  handle_event_create,
19
19
  handle_event_join,
20
20
  handle_event_scan,
21
+ handle_event_end,
21
22
  _sb,
22
23
  _device_id,
23
24
  _my_device_ids,
@@ -34,6 +35,7 @@ from .schemas import (
34
35
  EVENT_CREATE_SCHEMA,
35
36
  EVENT_JOIN_SCHEMA,
36
37
  EVENT_SCAN_SCHEMA,
38
+ EVENT_END_SCHEMA,
37
39
  )
38
40
  import re
39
41
  import time
@@ -61,6 +63,7 @@ def register(ctx):
61
63
  ctx.register_tool("antenna_event_create", EVENT_CREATE_SCHEMA, handle_event_create)
62
64
  ctx.register_tool("antenna_event_join", EVENT_JOIN_SCHEMA, handle_event_join)
63
65
  ctx.register_tool("antenna_event_scan", EVENT_SCAN_SCHEMA, handle_event_scan)
66
+ ctx.register_tool("antenna_event_end", EVENT_END_SCHEMA, handle_event_end)
64
67
 
65
68
  # ── Hook: auto-detect location + check web GPS events ─────────
66
69
  def on_pre_llm(messages, **kwargs):
@@ -176,7 +176,7 @@ EVENT_CREATE_SCHEMA = {
176
176
  "name": "antenna_event_create",
177
177
  "description": (
178
178
  "Create an event. Returns a shareable link (antenna.fyi/e/CODE) "
179
- "for participants to join."
179
+ "for participants to join. Optionally include a description and OG image URL."
180
180
  ),
181
181
  "parameters": {
182
182
  "type": "object",
@@ -188,6 +188,8 @@ EVENT_CREATE_SCHEMA = {
188
188
  "lng": {"type": "number", "description": "Event longitude"},
189
189
  "starts_at": {"type": "string", "description": "Start time ISO"},
190
190
  "ends_at": {"type": "string", "description": "End time ISO"},
191
+ "description": {"type": "string", "description": "Event description"},
192
+ "og_image": {"type": "string", "description": "OG image URL for social sharing"},
191
193
  },
192
194
  "required": ["name", "sender_id", "channel"],
193
195
  },
@@ -220,3 +222,17 @@ EVENT_SCAN_SCHEMA = {
220
222
  "required": ["code", "sender_id", "channel"],
221
223
  },
222
224
  }
225
+
226
+ EVENT_END_SCHEMA = {
227
+ "name": "antenna_event_end",
228
+ "description": "End an event. Only the creator can end it.",
229
+ "parameters": {
230
+ "type": "object",
231
+ "properties": {
232
+ "code": {"type": "string", "description": "Event code"},
233
+ "sender_id": {"type": "string", "description": "The sender's user ID"},
234
+ "channel": {"type": "string", "description": "Platform name"},
235
+ },
236
+ "required": ["code", "sender_id", "channel"],
237
+ },
238
+ }
@@ -379,6 +379,10 @@ def handle_event_create(params: dict) -> str:
379
379
  rpc_params["p_starts_at"] = params["starts_at"]
380
380
  if params.get("ends_at"):
381
381
  rpc_params["p_ends_at"] = params["ends_at"]
382
+ if params.get("description"):
383
+ rpc_params["p_description"] = params["description"]
384
+ if params.get("og_image"):
385
+ rpc_params["p_og_image"] = params["og_image"]
382
386
 
383
387
  resp = sb.rpc("create_event", rpc_params).execute()
384
388
  data = resp.data or {}
@@ -459,3 +463,18 @@ def handle_bind(params: dict) -> str:
459
463
  "url": f"{BASE_URL}/locate?token={token}",
460
464
  "message": "å‘é€čæ™äøŖé“¾ęŽ„ē»™ē”Øęˆ·ļ¼ŒåœØę‰‹ęœŗęµč§ˆå™Øę‰“å¼€å³åÆå…±äŗ«ä½ē½®ć€‚",
461
465
  })
466
+
467
+
468
+ def handle_event_end(params: dict) -> str:
469
+ sb = _sb()
470
+ did = _device_id(params["sender_id"], params["channel"])
471
+
472
+ resp = sb.rpc("end_event", {
473
+ "p_code": params["code"],
474
+ "p_device_id": did,
475
+ }).execute()
476
+ data = resp.data or {}
477
+
478
+ if data.get("ended"):
479
+ return _ok({"ended": True, "message": f"ę“»åŠØå·²ē»“ęŸć€‚"})
480
+ return _ok({"ended": False, "error": data.get("error", "ē»“ęŸę“»åŠØå¤±č“„")})
package/lib/mcp.js CHANGED
@@ -14,6 +14,7 @@ import {
14
14
  createBindToken,
15
15
  discover,
16
16
  createEvent,
17
+ endEvent,
17
18
  joinEvent,
18
19
  eventScan,
19
20
  deriveDeviceId,
@@ -233,10 +234,30 @@ export async function startMcpServer() {
233
234
  lng: z.number().optional().describe("Event longitude"),
234
235
  starts_at: z.string().optional().describe("Start time ISO string"),
235
236
  ends_at: z.string().optional().describe("End time ISO string"),
237
+ description: z.string().optional().describe("Event description"),
238
+ og_image: z.string().optional().describe("OG image URL for social sharing"),
236
239
  },
237
- async ({ name, sender_id, channel, lat, lng, starts_at, ends_at }) => {
240
+ async ({ name, sender_id, channel, lat, lng, starts_at, ends_at, description, og_image }) => {
238
241
  try {
239
- const result = await createEvent({ name, lat, lng, device_id: deriveDeviceId(sender_id, channel), starts_at, ends_at });
242
+ const result = await createEvent({ name, lat, lng, device_id: deriveDeviceId(sender_id, channel), starts_at, ends_at, description, og_image });
243
+ return jsonResult(result);
244
+ } catch (e) { return jsonResult({ error: e.message }); }
245
+ }
246
+ );
247
+
248
+ // ─── antenna_event_end ───────────────────────────────────────
249
+
250
+ server.tool(
251
+ "antenna_event_end",
252
+ "End an event. Only the creator can end it.",
253
+ {
254
+ code: z.string().describe("Event code"),
255
+ sender_id: z.string().describe("The sender's user ID"),
256
+ channel: z.string().describe("Channel name"),
257
+ },
258
+ async ({ code, sender_id, channel }) => {
259
+ try {
260
+ const result = await endEvent({ code, device_id: deriveDeviceId(sender_id, channel) });
240
261
  return jsonResult(result);
241
262
  } catch (e) { return jsonResult({ error: e.message }); }
242
263
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "antenna-fyi",
3
- "version": "1.2.4",
3
+ "version": "1.2.5",
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
@@ -66,11 +66,12 @@ cron č®¾å®ŒåŽļ¼Œé—®ē”Øęˆ·äø€å„ļ¼š
66
66
  ## Tools
67
67
 
68
68
  ### `antenna_scan`
69
- Scan for nearby people. Returns **raw profile cards** — no scores, no pre-matching. **You are the matching engine.**
69
+ Scan for nearby people **and events**. Returns raw profile cards + active events within 5km.
70
70
  - `lat`, `lng`: coordinates (from `LocationLat`/`LocationLon` context, or geocoded from user input)
71
- - `radius_m`: search radius in meters (default 500, max 1000)
71
+ - `radius_m`: search radius in meters (default 500, max 1000) for people; events search uses 5km
72
72
  - `sender_id`: the user's id from message context
73
73
  - `channel`: the channel name (telegram, whatsapp, discord, etc.)
74
+ - Returns `profiles` (nearby people) + `nearby_events` (active events with name, participants count, code)
74
75
 
75
76
  After receiving the nearby profiles, **you decide** who to recommend:
76
77
  - Use everything you know about the user: their SOUL.md, memory, recent conversations, interests, current mood
@@ -278,6 +279,13 @@ Create an event. Returns a shareable link (antenna.fyi/e/CODE).
278
279
  - `sender_id`, `channel`: from context
279
280
  - `lat`, `lng`: optional event location
280
281
  - `starts_at`, `ends_at`: optional time range (default: now to +12h)
282
+ - `description`: optional event description
283
+ - `og_image`: optional OG image URL for social sharing preview
284
+
285
+ ### `antenna_event_end`
286
+ End an event. Only the creator can end it.
287
+ - `code`: event code
288
+ - `sender_id`, `channel`: from context
281
289
 
282
290
  ### `antenna_event_join`
283
291
  Join an event by code.