antenna-fyi 1.2.3 ā 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 +38 -11
- package/lib/core.js +20 -1
- package/lib/hermes-plugin/__init__.py +3 -0
- package/lib/hermes-plugin/schemas.py +17 -1
- package/lib/hermes-plugin/tools.py +19 -0
- package/lib/mcp.js +23 -2
- package/package.json +1 -1
- package/skill/SKILL.md +10 -2
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";
|
|
@@ -16,8 +16,13 @@ export function parseFlags(args) {
|
|
|
16
16
|
for (let i = 0; i < args.length; i++) {
|
|
17
17
|
if (args[i].startsWith("--")) {
|
|
18
18
|
const key = args[i].slice(2);
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
const next = args[i + 1];
|
|
20
|
+
if (next && !next.startsWith("--")) {
|
|
21
|
+
flags[key] = next;
|
|
22
|
+
i++;
|
|
23
|
+
} else {
|
|
24
|
+
flags[key] = true;
|
|
25
|
+
}
|
|
21
26
|
}
|
|
22
27
|
}
|
|
23
28
|
return flags;
|
|
@@ -42,8 +47,18 @@ export async function handleScan(f) {
|
|
|
42
47
|
if (p.line1) console.log(` ${p.line1}`);
|
|
43
48
|
if (p.line2) console.log(` ${p.line2}`);
|
|
44
49
|
if (p.line3) console.log(` ${p.line3}`);
|
|
45
|
-
console.log(`
|
|
50
|
+
console.log(` ref: ${p.ref}\n`);
|
|
46
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
|
+
}
|
|
47
62
|
}
|
|
48
63
|
|
|
49
64
|
export async function handleProfile(f) {
|
|
@@ -126,11 +141,22 @@ export async function handleDiscover(f) {
|
|
|
126
141
|
}
|
|
127
142
|
|
|
128
143
|
export async function handleEvent(f) {
|
|
129
|
-
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
|
+
}
|
|
130
156
|
|
|
131
|
-
if (f.create || (!f.join && !f.scan && f.name)) {
|
|
132
|
-
if (!f.name) return console.error("Usage: antenna event --create --name 'AI Meetup'");
|
|
133
|
-
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 });
|
|
134
160
|
console.log(`\nš Event created!\n`);
|
|
135
161
|
console.log(` Name: ${result.name}`);
|
|
136
162
|
console.log(` Code: ${result.code}`);
|
|
@@ -164,9 +190,10 @@ export async function handleEvent(f) {
|
|
|
164
190
|
}
|
|
165
191
|
|
|
166
192
|
console.log(`Usage:
|
|
167
|
-
antenna event --create --name 'AI Meetup' [--id telegram:123]
|
|
193
|
+
antenna event --create --name 'AI Meetup' [--id telegram:123] [--desc 'description'] [--og-image 'url']
|
|
168
194
|
antenna event --join --code abc123 --id telegram:123
|
|
169
|
-
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`);
|
|
170
197
|
}
|
|
171
198
|
|
|
172
199
|
export async function handleBind(f) {
|
|
@@ -361,7 +388,7 @@ Usage:
|
|
|
361
388
|
antenna pass --id telegram:123 --target telegram:789 (or --ref 1)
|
|
362
389
|
antenna matches --id telegram:123
|
|
363
390
|
antenna discover --id telegram:123
|
|
364
|
-
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
|
|
365
392
|
antenna bind --id telegram:123
|
|
366
393
|
antenna serve Start MCP server (stdio transport)
|
|
367
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
package/skill/SKILL.md
CHANGED
|
@@ -66,11 +66,12 @@ cron 设å®åļ¼é®ēØę·äøå„ļ¼
|
|
|
66
66
|
## Tools
|
|
67
67
|
|
|
68
68
|
### `antenna_scan`
|
|
69
|
-
Scan for nearby people
|
|
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.
|