antenna-fyi 1.3.27 ā 1.3.29
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/bin/antenna.js +3 -0
- package/lib/cli.js +83 -25
- package/lib/core.js +39 -8
- package/lib/mcp.js +95 -2
- package/package.json +2 -2
- package/skill/SKILL.md +63 -8
package/bin/antenna.js
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
handleConfig,
|
|
17
17
|
handleStatus,
|
|
18
18
|
handleLinkAccount,
|
|
19
|
+
handleDrift,
|
|
19
20
|
handleInstallSkill,
|
|
20
21
|
handleInstallPlugin,
|
|
21
22
|
handleInstallHermesPlugin,
|
|
@@ -41,6 +42,8 @@ async function main() {
|
|
|
41
42
|
return handleDiscover(f);
|
|
42
43
|
case "event":
|
|
43
44
|
return handleEvent(f);
|
|
45
|
+
case "drift":
|
|
46
|
+
return handleDrift(f);
|
|
44
47
|
case "bind":
|
|
45
48
|
return handleBind(f);
|
|
46
49
|
case "pass":
|
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, endEvent, eventCheckin, joinEvent, eventScan, pass as passUser, uploadEventImage, updateEvent, approveParticipant, rejectParticipant, addCohost, sendEventMessage, getMyEventMessages, getClient, verifyApiKey, linkAccount, initialRecommendations } from "./core.js";
|
|
3
|
+
import { scan, getProfile, setProfile, accept, checkMatches, checkin, createBindToken, discover, createEvent, endEvent, eventCheckin, joinEvent, eventScan, pass as passUser, uploadEventImage, updateEvent, approveParticipant, rejectParticipant, addCohost, sendEventMessage, getMyEventMessages, getClient, verifyApiKey, linkAccount, initialRecommendations, throwDriftBottle, pickDriftBottle, replyDriftBottle, checkDriftBottles, getMyBottles } from "./core.js";
|
|
4
4
|
import { createInterface } from "readline";
|
|
5
5
|
import { existsSync, mkdirSync, copyFileSync, readFileSync, writeFileSync, unlinkSync, renameSync } from "fs";
|
|
6
6
|
import path from "path";
|
|
@@ -46,7 +46,7 @@ export async function handleScan(f) {
|
|
|
46
46
|
console.log(`š” ${result.count} people within ${result.radius_m}m:\n`);
|
|
47
47
|
}
|
|
48
48
|
result.profiles.forEach((p) => {
|
|
49
|
-
console.log(` ${p.
|
|
49
|
+
console.log(` ${p.name}${p.distance_m != null ? ` (${Math.round(p.distance_m)}m)` : ""}`);
|
|
50
50
|
if (p.personal_description) console.log(` ${p.personal_description}`);
|
|
51
51
|
if (p.looking_for) console.log(` ${p.looking_for}`);
|
|
52
52
|
if (p.conversation_style) console.log(` ${p.conversation_style}`);
|
|
@@ -66,12 +66,11 @@ export async function handleScan(f) {
|
|
|
66
66
|
|
|
67
67
|
export async function handleProfile(f) {
|
|
68
68
|
const id = resolveId(f);
|
|
69
|
-
if (!id) return console.error("Usage: antenna profile --id <platform>:<user_id> [--name Yi --
|
|
69
|
+
if (!id) return console.error("Usage: antenna profile --id <platform>:<user_id> [--name Yi --personal-description '...' --looking-for '...' --conversation-style '...' --hide --visible true]");
|
|
70
70
|
if (f.name || f["personal-description"] || f["looking-for"] || f["conversation-style"] || f["more-information"] || f.visible !== undefined || f.hide !== undefined) {
|
|
71
71
|
const visible = f.hide ? false : (f.visible !== undefined ? f.visible === 'true' || f.visible === true : undefined);
|
|
72
72
|
const payload = { device_id: id };
|
|
73
73
|
if (f.name) payload.display_name = f.name;
|
|
74
|
-
if (f.emoji) payload.emoji = f.emoji;
|
|
75
74
|
if (f["personal-description"] !== undefined) payload.line1 = f["personal-description"];
|
|
76
75
|
if (f["looking-for"] !== undefined) payload.line2 = f["looking-for"];
|
|
77
76
|
if (f["conversation-style"] !== undefined) payload.line3 = f["conversation-style"];
|
|
@@ -83,7 +82,7 @@ export async function handleProfile(f) {
|
|
|
83
82
|
} else {
|
|
84
83
|
const data = await getProfile({ device_id: id });
|
|
85
84
|
if (!data) return console.log("No profile yet. Create one with --name and --personal-description");
|
|
86
|
-
console.log(`${data.
|
|
85
|
+
console.log(`${data.display_name || "Anonymous"}`);
|
|
87
86
|
if (data.personal_description) console.log(` ${data.personal_description}`);
|
|
88
87
|
if (data.looking_for) console.log(` Looking for: ${data.looking_for}`);
|
|
89
88
|
if (data.conversation_style) console.log(` Conversation: ${data.conversation_style}`);
|
|
@@ -125,13 +124,13 @@ export async function handleMatches(f) {
|
|
|
125
124
|
return console.log(result.message);
|
|
126
125
|
}
|
|
127
126
|
for (const m of result.mutual_matches) {
|
|
128
|
-
console.log(`š MUTUAL: ${m.
|
|
127
|
+
console.log(`š MUTUAL: ${m.name}`);
|
|
129
128
|
if (m.their_contact) console.log(` Their contact: ${m.their_contact}`);
|
|
130
129
|
if (m.you_shared) console.log(` You shared: ${m.you_shared}`);
|
|
131
130
|
console.log();
|
|
132
131
|
}
|
|
133
132
|
for (const m of result.incoming_accepts) {
|
|
134
|
-
console.log(`š© WANTS TO MEET YOU: ${m.
|
|
133
|
+
console.log(`š© WANTS TO MEET YOU: ${m.name}`);
|
|
135
134
|
if (m.personal_description) console.log(` ${m.personal_description}`);
|
|
136
135
|
console.log(` Accept: antenna accept --id ${f.id} --ref ${m.ref}`);
|
|
137
136
|
console.log();
|
|
@@ -145,7 +144,7 @@ export async function handleDiscover(f) {
|
|
|
145
144
|
if (result.count === 0) return console.log(result.message || "š No global recommendation available right now.");
|
|
146
145
|
console.log(`š Global discover:\n`);
|
|
147
146
|
result.profiles.forEach((p) => {
|
|
148
|
-
console.log(` ${p.
|
|
147
|
+
console.log(` ${p.name}`);
|
|
149
148
|
if (p.personal_description) console.log(` ${p.personal_description}`);
|
|
150
149
|
if (p.looking_for) console.log(` ${p.looking_for}`);
|
|
151
150
|
if (p.conversation_style) console.log(` ${p.conversation_style}`);
|
|
@@ -232,7 +231,7 @@ export async function handleEvent(f) {
|
|
|
232
231
|
const badge = p.checked_in ? " ā
" : "";
|
|
233
232
|
const creatorTag = p.role === "creator" ? " [äø»å]" : "";
|
|
234
233
|
const statusTag = p.status === "pending" ? " š”å¾
å®”ę¹" : "";
|
|
235
|
-
console.log(` ${p.
|
|
234
|
+
console.log(` ${p.name}${creatorTag}${badge}${statusTag}`);
|
|
236
235
|
if (p.personal_description) console.log(` ${p.personal_description}`);
|
|
237
236
|
if (p.application_context) console.log(` š ${p.application_context}`);
|
|
238
237
|
console.log(` ref: ${p.ref}\n`);
|
|
@@ -337,7 +336,6 @@ export async function handleSetup(f) {
|
|
|
337
336
|
if (!id) { rl.close(); return console.error("Device ID is required."); }
|
|
338
337
|
|
|
339
338
|
const name = await ask("Display name: ");
|
|
340
|
-
const emoji = (await ask("Emoji (default š¤): ")) || "š¤";
|
|
341
339
|
const personalDesc = await ask("Personal description ā who you are, what you do: ");
|
|
342
340
|
const lookingFor = await ask("Looking for ā the kind of people you want to meet: ");
|
|
343
341
|
const convStyle = await ask("Conversation style ā the type of conversations you want: ");
|
|
@@ -347,14 +345,14 @@ export async function handleSetup(f) {
|
|
|
347
345
|
const data = await setProfile({
|
|
348
346
|
device_id: id,
|
|
349
347
|
display_name: name || null,
|
|
350
|
-
|
|
348
|
+
|
|
351
349
|
line1: personalDesc || null,
|
|
352
350
|
line2: lookingFor || null,
|
|
353
351
|
line3: convStyle || null,
|
|
354
352
|
});
|
|
355
353
|
|
|
356
354
|
console.log("\nā
Profile saved!\n");
|
|
357
|
-
console.log(` ${
|
|
355
|
+
console.log(` ${name || "Anonymous"}`);
|
|
358
356
|
if (personalDesc) console.log(` ${personalDesc}`);
|
|
359
357
|
if (lookingFor) console.log(` ${lookingFor}`);
|
|
360
358
|
if (convStyle) console.log(` ${convStyle}`);
|
|
@@ -469,7 +467,7 @@ export async function handleStatus(f) {
|
|
|
469
467
|
if (f.id) {
|
|
470
468
|
const profile = await getProfile({ device_id: f.id });
|
|
471
469
|
if (profile) {
|
|
472
|
-
console.log(` Profile: ā
${profile.
|
|
470
|
+
console.log(` Profile: ā
${profile.display_name || "Anonymous"}`);
|
|
473
471
|
} else {
|
|
474
472
|
console.log(" Profile: ā Not created yet");
|
|
475
473
|
}
|
|
@@ -779,7 +777,7 @@ export async function handleWatch(f) {
|
|
|
779
777
|
for (const m of initial.mutual_matches) {
|
|
780
778
|
const key = `mutual:${m._device_id}`;
|
|
781
779
|
notified.add(key);
|
|
782
|
-
_log(` ${m.
|
|
780
|
+
_log(` ${m.name}${m.their_contact ? " ā contact: " + m.their_contact : ""}`);
|
|
783
781
|
}
|
|
784
782
|
saveNotified(notified);
|
|
785
783
|
_log("");
|
|
@@ -789,7 +787,7 @@ export async function handleWatch(f) {
|
|
|
789
787
|
for (const m of initial.incoming_accepts) {
|
|
790
788
|
const key = `incoming:${m._device_id}`;
|
|
791
789
|
notified.add(key);
|
|
792
|
-
_log(` ${m.
|
|
790
|
+
_log(` ${m.name} ā ${m.personal_description || ""}`);
|
|
793
791
|
}
|
|
794
792
|
saveNotified(notified);
|
|
795
793
|
_log("");
|
|
@@ -822,7 +820,6 @@ export async function handleWatch(f) {
|
|
|
822
820
|
|
|
823
821
|
const profile = await getProfile({ device_id: row.device_id_a });
|
|
824
822
|
const name = profile?.display_name || "Someone";
|
|
825
|
-
const emoji = profile?.emoji || "š¤";
|
|
826
823
|
|
|
827
824
|
// Check if mutual
|
|
828
825
|
const matches = await checkMatches({ device_id: id });
|
|
@@ -833,11 +830,11 @@ export async function handleWatch(f) {
|
|
|
833
830
|
notified.add(mutualKey);
|
|
834
831
|
saveNotified(notified);
|
|
835
832
|
const contact = row.contact_info_a;
|
|
836
|
-
pushNotify(`š MUTUAL MATCH! ${
|
|
833
|
+
pushNotify(`š MUTUAL MATCH! ${name} also accepted you!${contact ? " Contact: " + contact : ""}`);
|
|
837
834
|
} else {
|
|
838
835
|
notified.add(key);
|
|
839
836
|
saveNotified(notified);
|
|
840
|
-
pushNotify(`š© ${
|
|
837
|
+
pushNotify(`š© ${name} wants to meet you! Use 'antenna matches --id ${id}' to respond.`);
|
|
841
838
|
}
|
|
842
839
|
}
|
|
843
840
|
|
|
@@ -850,7 +847,7 @@ export async function handleWatch(f) {
|
|
|
850
847
|
if (!notified.has(mutualKey)) {
|
|
851
848
|
notified.add(mutualKey);
|
|
852
849
|
saveNotified(notified);
|
|
853
|
-
pushNotify(`š MUTUAL MATCH! ${mutual.
|
|
850
|
+
pushNotify(`š MUTUAL MATCH! ${mutual.name}!${mutual.their_contact ? " Contact: " + mutual.their_contact : ""}`);
|
|
854
851
|
}
|
|
855
852
|
}
|
|
856
853
|
}
|
|
@@ -897,8 +894,7 @@ export async function handleWatch(f) {
|
|
|
897
894
|
if (!event?.found || event.created_by !== id) return;
|
|
898
895
|
const applicant = await getProfile({ device_id: row.device_id });
|
|
899
896
|
const name = applicant?.display_name || "Someone";
|
|
900
|
-
|
|
901
|
-
pushNotify(`š© ${emoji} ${name} applied to join \"${event.name}\"! Run: antenna event --scan --code ${event.code} --id ${id}`);
|
|
897
|
+
pushNotify(`š© ${name} applied to join \"${event.name}\"! Run: antenna event --scan --code ${event.code} --id ${id}`);
|
|
902
898
|
} catch {}
|
|
903
899
|
}
|
|
904
900
|
)
|
|
@@ -937,7 +933,7 @@ export async function handleWatch(f) {
|
|
|
937
933
|
if (!notified.has(key)) {
|
|
938
934
|
notified.add(key);
|
|
939
935
|
saveNotified(notified);
|
|
940
|
-
pushNotify(`š MUTUAL MATCH! ${m.
|
|
936
|
+
pushNotify(`š MUTUAL MATCH! ${m.name}!${m.their_contact ? " Contact: " + m.their_contact : ""}`);
|
|
941
937
|
}
|
|
942
938
|
}
|
|
943
939
|
for (const m of (result.incoming_accepts || [])) {
|
|
@@ -945,7 +941,7 @@ export async function handleWatch(f) {
|
|
|
945
941
|
if (!notified.has(key)) {
|
|
946
942
|
notified.add(key);
|
|
947
943
|
saveNotified(notified);
|
|
948
|
-
pushNotify(`š© ${m.
|
|
944
|
+
pushNotify(`š© ${m.name} wants to meet you!`);
|
|
949
945
|
}
|
|
950
946
|
}
|
|
951
947
|
} catch { /* silent */ }
|
|
@@ -976,7 +972,7 @@ export async function handleWatch(f) {
|
|
|
976
972
|
notified.add(key);
|
|
977
973
|
saveNotified(notified);
|
|
978
974
|
const role = msg.sender_role === 'creator' ? 'ē»ē»č
' : 'åå';
|
|
979
|
-
pushNotify(`š¢ ę„čŖć${msg.event_name}ć${role} ${msg.
|
|
975
|
+
pushNotify(`š¢ ę„čŖć${msg.event_name}ć${role} ${msg.sender_name}: ${msg.message}`);
|
|
980
976
|
}
|
|
981
977
|
}
|
|
982
978
|
} catch { /* silent */ }
|
|
@@ -991,18 +987,80 @@ export async function handleWatch(f) {
|
|
|
991
987
|
});
|
|
992
988
|
}
|
|
993
989
|
|
|
990
|
+
export async function handleDrift(f) {
|
|
991
|
+
const id = resolveId(f);
|
|
992
|
+
if (!id) return console.error("Usage: antenna drift --id <platform>:<user_id> --throw --message 'hello'");
|
|
993
|
+
|
|
994
|
+
if (f.throw) {
|
|
995
|
+
if (!f.message) return console.error("Usage: antenna drift --throw --message 'your message' --id <platform>:<user_id>");
|
|
996
|
+
const result = await throwDriftBottle({ device_id: id, message: f.message });
|
|
997
|
+
if (result.error) return console.error(`ā ${result.error}`);
|
|
998
|
+
console.log("š¾ ę¼ęµē¶å·²äø¢å
„ęµ·äøļ¼");
|
|
999
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
if (f.pick) {
|
|
1004
|
+
const result = await pickDriftBottle({ device_id: id });
|
|
1005
|
+
if (result.error) return console.error(`ā ${result.error}`);
|
|
1006
|
+
if (!result.bottle_id) return console.log(result.message || "š ęµ·äøę²”ęę¼ęµē¶äŗć");
|
|
1007
|
+
console.log(`š¾ ę”å°äøäøŖę¼ęµē¶ļ¼\n`);
|
|
1008
|
+
console.log(` ${result.message}\n`);
|
|
1009
|
+
console.log(` åå¤: antenna drift --reply --bottle-id ${result.bottle_id} --message 'ä½ ēåå¤' --id ${id}`);
|
|
1010
|
+
return;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
if (f.reply) {
|
|
1014
|
+
if (!f['bottle-id'] || !f.message) return console.error("Usage: antenna drift --reply --bottle-id <uuid> --message 'your reply' --id <platform>:<user_id>");
|
|
1015
|
+
const result = await replyDriftBottle({ bottle_id: f['bottle-id'], device_id: id, reply: f.message });
|
|
1016
|
+
if (result.error) return console.error(`ā ${result.error}`);
|
|
1017
|
+
console.log("š¬ åå¤å·²ę¼åē»äø¢ē¶åēäŗŗļ¼");
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
if (f.check) {
|
|
1022
|
+
const result = await checkDriftBottles({ device_id: id });
|
|
1023
|
+
if (result.error) return console.error(`ā ${result.error}`);
|
|
1024
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1025
|
+
return;
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
if (f['my-bottles']) {
|
|
1029
|
+
const result = await getMyBottles({ device_id: id });
|
|
1030
|
+
if (result.error) return console.error(`ā ${result.error}`);
|
|
1031
|
+
const bottles = Array.isArray(result) ? result : (result.bottles || []);
|
|
1032
|
+
if (bottles.length === 0) return console.log("š ä½ čæę²”äø¢čæę¼ęµē¶ć");
|
|
1033
|
+
console.log(`š¾ ä½ ēę¼ęµē¶ (${bottles.length}):\n`);
|
|
1034
|
+
for (const b of bottles) {
|
|
1035
|
+
const status = b.reply ? "š¬ å·²åå¤" : b.picked_by ? "š 被ę”čµ·" : "š ę¼ęµäø";
|
|
1036
|
+
console.log(` ${status} ā ${b.message}`);
|
|
1037
|
+
if (b.reply) console.log(` ā© ${b.reply}`);
|
|
1038
|
+
console.log();
|
|
1039
|
+
}
|
|
1040
|
+
return;
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
console.log(`Usage:
|
|
1044
|
+
antenna drift --throw --message 'hello' --id <platform>:<user_id>
|
|
1045
|
+
antenna drift --pick --id <platform>:<user_id>
|
|
1046
|
+
antenna drift --reply --bottle-id <uuid> --message 'reply' --id <platform>:<user_id>
|
|
1047
|
+
antenna drift --check --id <platform>:<user_id>
|
|
1048
|
+
antenna drift --my-bottles --id <platform>:<user_id>`);
|
|
1049
|
+
}
|
|
1050
|
+
|
|
994
1051
|
export function printHelp() {
|
|
995
1052
|
console.log(`š” Antenna ā nearby people discovery
|
|
996
1053
|
|
|
997
1054
|
Usage:
|
|
998
1055
|
antenna scan --lat 39.99 --lng 116.48 [--radius 500] (max 1000) [--id <platform>:<user_id>]
|
|
999
1056
|
antenna checkin --id <platform>:<user_id> --lat 39.99 --lng 116.48
|
|
1000
|
-
antenna profile --id <platform>:<user_id> [--name Yi --
|
|
1057
|
+
antenna profile --id <platform>:<user_id> [--name Yi --personal-description '...' --looking-for '...' --conversation-style '...' --hide --visible true]
|
|
1001
1058
|
antenna accept --id <platform>:<user_id> --target <ref_or_device_id> [--contact 'WeChat: yi']
|
|
1002
1059
|
antenna pass --id <platform>:<user_id> --target <ref_or_device_id> (or --ref 1)
|
|
1003
1060
|
antenna matches --id <platform>:<user_id>
|
|
1004
1061
|
antenna discover --id <platform>:<user_id>
|
|
1005
1062
|
antenna event --create --name 'AI Meetup' --starts-at '...' --ends-at '...' [--lat 34.05 --lng -118.25] [--desc '...'] [--og-image 'url'] [--requires-approval] [--screening-questions 'Q1|Q2'] | --join --code abc123 | --scan --code abc123 | --end --code abc123 --id <platform>:<user_id> | --upload-image --code abc123 --file /path/to/image.png | --update --code abc123 --name 'New Name' | --approve --code abc123 --ref 1 | --reject --code abc123 --ref 1 | --add-host --code abc123 --ref 1
|
|
1063
|
+
antenna drift --throw --message 'hello' | --pick | --reply --bottle-id <uuid> --message 'reply' | --check | --my-bottles --id <platform>:<user_id>
|
|
1006
1064
|
antenna watch --id <platform>:<user_id> [--push hermes|openclaw|terminal] Watch for new matches in real-time (Ctrl+C to stop)
|
|
1007
1065
|
antenna bind --id <platform>:<user_id>
|
|
1008
1066
|
antenna serve Start MCP server (stdio transport)
|
package/lib/core.js
CHANGED
|
@@ -121,7 +121,6 @@ export async function scan({ lat, lng, radius_m = 500, device_id, supabaseUrl, s
|
|
|
121
121
|
return {
|
|
122
122
|
ref,
|
|
123
123
|
name: p.display_name || "åæå",
|
|
124
|
-
emoji: p.emoji || "š¤",
|
|
125
124
|
personal_description: p.line1,
|
|
126
125
|
looking_for: p.line2,
|
|
127
126
|
conversation_style: p.line3,
|
|
@@ -247,7 +246,6 @@ export async function getProfile({ device_id, supabaseUrl, supabaseKey }) {
|
|
|
247
246
|
export async function setProfile({
|
|
248
247
|
device_id,
|
|
249
248
|
display_name,
|
|
250
|
-
emoji,
|
|
251
249
|
line1,
|
|
252
250
|
line2,
|
|
253
251
|
line3,
|
|
@@ -297,7 +295,7 @@ export async function setProfile({
|
|
|
297
295
|
const { data, error } = await sb.rpc("upsert_profile", {
|
|
298
296
|
p_device_id: device_id,
|
|
299
297
|
p_display_name: display_name || null,
|
|
300
|
-
p_emoji:
|
|
298
|
+
p_emoji: null,
|
|
301
299
|
p_line1: line1 || null,
|
|
302
300
|
p_line2: line2 || null,
|
|
303
301
|
p_line3: line3 || null,
|
|
@@ -371,6 +369,7 @@ export async function setProfile({
|
|
|
371
369
|
await sb.rpc("upsert_profile", {
|
|
372
370
|
p_device_id: device_id,
|
|
373
371
|
p_matching_context: JSON.stringify(ctx),
|
|
372
|
+
p_visible: profile?.visible ?? true,
|
|
374
373
|
});
|
|
375
374
|
} catch {}
|
|
376
375
|
}
|
|
@@ -501,7 +500,6 @@ export async function checkMatches({ device_id, supabaseUrl, supabaseKey }) {
|
|
|
501
500
|
ref: String(i + 1),
|
|
502
501
|
_device_id: m.target_id,
|
|
503
502
|
name: m.name || "åæå",
|
|
504
|
-
emoji: m.emoji || "š¤",
|
|
505
503
|
personal_description: m.line1,
|
|
506
504
|
looking_for: m.line2,
|
|
507
505
|
conversation_style: m.line3,
|
|
@@ -514,7 +512,6 @@ export async function checkMatches({ device_id, supabaseUrl, supabaseKey }) {
|
|
|
514
512
|
ref: String(incomingOffset + i + 1),
|
|
515
513
|
_device_id: m.target_id,
|
|
516
514
|
name: m.name || "åæå",
|
|
517
|
-
emoji: m.emoji || "š¤",
|
|
518
515
|
personal_description: m.line1,
|
|
519
516
|
looking_for: m.line2,
|
|
520
517
|
conversation_style: m.line3,
|
|
@@ -586,7 +583,6 @@ export async function discover({ device_id, supabaseUrl, supabaseKey }) {
|
|
|
586
583
|
profiles.push({
|
|
587
584
|
ref,
|
|
588
585
|
name: p.display_name || "åæå",
|
|
589
|
-
emoji: p.emoji || "š¤",
|
|
590
586
|
personal_description: p.line1,
|
|
591
587
|
looking_for: p.line2,
|
|
592
588
|
conversation_style: p.line3,
|
|
@@ -661,7 +657,6 @@ export async function initialRecommendations({ device_id, supabaseUrl, supabaseK
|
|
|
661
657
|
profiles.push({
|
|
662
658
|
ref,
|
|
663
659
|
name: p.display_name || "åæå",
|
|
664
|
-
emoji: p.emoji || "š¤",
|
|
665
660
|
personal_description: p.line1,
|
|
666
661
|
looking_for: p.line2,
|
|
667
662
|
conversation_style: p.line3,
|
|
@@ -854,7 +849,6 @@ export async function eventScan({ code, device_id, supabaseUrl, supabaseKey }) {
|
|
|
854
849
|
return {
|
|
855
850
|
ref,
|
|
856
851
|
name: p.display_name || "åæå",
|
|
857
|
-
emoji: p.emoji || "š¤",
|
|
858
852
|
personal_description: p.line1,
|
|
859
853
|
looking_for: p.line2,
|
|
860
854
|
conversation_style: p.line3,
|
|
@@ -994,3 +988,40 @@ export async function linkAccount({ device_id, api_key, supabaseUrl, supabaseKey
|
|
|
994
988
|
message: "蓦å·å·²å
³čļ¼ē°åØä½ åÆä»„åØ antenna.fyi/me ēå°ä½ ēå®ę“ profile åå¹é
č®°å½äŗć",
|
|
995
989
|
};
|
|
996
990
|
}
|
|
991
|
+
|
|
992
|
+
// āāā Drift Bottle (ę¼ęµē¶) āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
993
|
+
|
|
994
|
+
export async function throwDriftBottle({ device_id, message }) {
|
|
995
|
+
const sb = getClient();
|
|
996
|
+
const { data, error } = await sb.rpc("throw_drift_bottle", { p_device_id: device_id, p_message: message });
|
|
997
|
+
if (error) return { error: error.message };
|
|
998
|
+
return data;
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
export async function pickDriftBottle({ device_id }) {
|
|
1002
|
+
const sb = getClient();
|
|
1003
|
+
const { data, error } = await sb.rpc("pick_drift_bottle", { p_device_id: device_id });
|
|
1004
|
+
if (error) return { error: error.message };
|
|
1005
|
+
return data;
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
export async function replyDriftBottle({ bottle_id, device_id, reply }) {
|
|
1009
|
+
const sb = getClient();
|
|
1010
|
+
const { data, error } = await sb.rpc("reply_drift_bottle", { p_bottle_id: bottle_id, p_device_id: device_id, p_reply: reply });
|
|
1011
|
+
if (error) return { error: error.message };
|
|
1012
|
+
return data;
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
export async function checkDriftBottles({ device_id }) {
|
|
1016
|
+
const sb = getClient();
|
|
1017
|
+
const { data, error } = await sb.rpc("check_drift_bottles", { p_device_id: device_id });
|
|
1018
|
+
if (error) return { error: error.message };
|
|
1019
|
+
return data;
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
export async function getMyBottles({ device_id }) {
|
|
1023
|
+
const sb = getClient();
|
|
1024
|
+
const { data, error } = await sb.rpc("get_my_bottles", { p_device_id: device_id });
|
|
1025
|
+
if (error) return { error: error.message };
|
|
1026
|
+
return data;
|
|
1027
|
+
}
|
package/lib/mcp.js
CHANGED
|
@@ -26,6 +26,11 @@ import {
|
|
|
26
26
|
addCohost,
|
|
27
27
|
sendEventMessage,
|
|
28
28
|
linkAccount,
|
|
29
|
+
throwDriftBottle,
|
|
30
|
+
pickDriftBottle,
|
|
31
|
+
replyDriftBottle,
|
|
32
|
+
checkDriftBottles,
|
|
33
|
+
getMyBottles,
|
|
29
34
|
deriveDeviceId,
|
|
30
35
|
PROFILE_FIELDS,
|
|
31
36
|
} from "./core.js";
|
|
@@ -60,14 +65,14 @@ export async function startMcpServer() {
|
|
|
60
65
|
const key = `mutual:${m.device_id}`;
|
|
61
66
|
if (!_notifiedMatches.has(key)) {
|
|
62
67
|
_notifiedMatches.add(key);
|
|
63
|
-
notifications.push(`š ååå¹é
ļ¼${m.
|
|
68
|
+
notifications.push(`š ååå¹é
ļ¼${m.name} ä¹ę„åäŗä½ ļ¼${m.their_contact ? "čē³»ę¹å¼ļ¼" + m.their_contact : ""}`);
|
|
64
69
|
}
|
|
65
70
|
}
|
|
66
71
|
for (const m of (matches.incoming_accepts || [])) {
|
|
67
72
|
const key = `incoming:${m.device_id}`;
|
|
68
73
|
if (!_notifiedMatches.has(key)) {
|
|
69
74
|
_notifiedMatches.add(key);
|
|
70
|
-
notifications.push(`š© ${m.
|
|
75
|
+
notifications.push(`š© ${m.name} ę³č®¤čÆä½ ļ¼ēØ antenna_check_matches ę„ē详ę
ć`);
|
|
71
76
|
}
|
|
72
77
|
}
|
|
73
78
|
|
|
@@ -554,6 +559,94 @@ export async function startMcpServer() {
|
|
|
554
559
|
}
|
|
555
560
|
);
|
|
556
561
|
|
|
562
|
+
// āāā antenna_drift_throw āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
563
|
+
|
|
564
|
+
server.tool(
|
|
565
|
+
"antenna_drift_throw",
|
|
566
|
+
"Throw a drift bottle into the sea. Write a message (max 500 chars), a random stranger will pick it up. Completely anonymous.",
|
|
567
|
+
{
|
|
568
|
+
sender_id: z.string().describe("The sender's user ID"),
|
|
569
|
+
channel: z.string().describe("Channel name"),
|
|
570
|
+
message: z.string().describe("Message to put in the bottle (max 500 chars)"),
|
|
571
|
+
},
|
|
572
|
+
async ({ sender_id, channel, message }) => {
|
|
573
|
+
try {
|
|
574
|
+
const result = await throwDriftBottle({ device_id: deriveDeviceId(sender_id, channel), message });
|
|
575
|
+
return jsonResult(result);
|
|
576
|
+
} catch (e) { return jsonResult({ error: e.message }); }
|
|
577
|
+
}
|
|
578
|
+
);
|
|
579
|
+
|
|
580
|
+
// āāā antenna_drift_pick āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
581
|
+
|
|
582
|
+
server.tool(
|
|
583
|
+
"antenna_drift_pick",
|
|
584
|
+
"Pick up a random drift bottle from the sea. Returns the anonymous message inside. You must reply before picking another.",
|
|
585
|
+
{
|
|
586
|
+
sender_id: z.string().describe("The sender's user ID"),
|
|
587
|
+
channel: z.string().describe("Channel name"),
|
|
588
|
+
},
|
|
589
|
+
async ({ sender_id, channel }) => {
|
|
590
|
+
try {
|
|
591
|
+
const result = await pickDriftBottle({ device_id: deriveDeviceId(sender_id, channel) });
|
|
592
|
+
return jsonResult(result);
|
|
593
|
+
} catch (e) { return jsonResult({ error: e.message }); }
|
|
594
|
+
}
|
|
595
|
+
);
|
|
596
|
+
|
|
597
|
+
// āāā antenna_drift_reply āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
598
|
+
|
|
599
|
+
server.tool(
|
|
600
|
+
"antenna_drift_reply",
|
|
601
|
+
"Reply to a drift bottle you picked up. Your reply will anonymously float back to the original sender.",
|
|
602
|
+
{
|
|
603
|
+
sender_id: z.string().describe("The sender's user ID"),
|
|
604
|
+
channel: z.string().describe("Channel name"),
|
|
605
|
+
bottle_id: z.string().describe("ID of the bottle to reply to"),
|
|
606
|
+
reply: z.string().describe("Reply message (max 500 chars)"),
|
|
607
|
+
},
|
|
608
|
+
async ({ sender_id, channel, bottle_id, reply }) => {
|
|
609
|
+
try {
|
|
610
|
+
const result = await replyDriftBottle({ bottle_id, device_id: deriveDeviceId(sender_id, channel), reply });
|
|
611
|
+
return jsonResult(result);
|
|
612
|
+
} catch (e) { return jsonResult({ error: e.message }); }
|
|
613
|
+
}
|
|
614
|
+
);
|
|
615
|
+
|
|
616
|
+
// āāā antenna_drift_check āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
617
|
+
|
|
618
|
+
server.tool(
|
|
619
|
+
"antenna_drift_check",
|
|
620
|
+
"Check drift bottle status ā any new replies on bottles you threw, or pending bottles you picked up.",
|
|
621
|
+
{
|
|
622
|
+
sender_id: z.string().describe("The sender's user ID"),
|
|
623
|
+
channel: z.string().describe("Channel name"),
|
|
624
|
+
},
|
|
625
|
+
async ({ sender_id, channel }) => {
|
|
626
|
+
try {
|
|
627
|
+
const result = await checkDriftBottles({ device_id: deriveDeviceId(sender_id, channel) });
|
|
628
|
+
return jsonResult(result);
|
|
629
|
+
} catch (e) { return jsonResult({ error: e.message }); }
|
|
630
|
+
}
|
|
631
|
+
);
|
|
632
|
+
|
|
633
|
+
// āāā antenna_drift_my_bottles āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
634
|
+
|
|
635
|
+
server.tool(
|
|
636
|
+
"antenna_drift_my_bottles",
|
|
637
|
+
"View all drift bottles you've thrown. Shows status: š drifting / š picked up / š¬ replied.",
|
|
638
|
+
{
|
|
639
|
+
sender_id: z.string().describe("The sender's user ID"),
|
|
640
|
+
channel: z.string().describe("Channel name"),
|
|
641
|
+
},
|
|
642
|
+
async ({ sender_id, channel }) => {
|
|
643
|
+
try {
|
|
644
|
+
const result = await getMyBottles({ device_id: deriveDeviceId(sender_id, channel) });
|
|
645
|
+
return jsonResult(result);
|
|
646
|
+
} catch (e) { return jsonResult({ error: e.message }); }
|
|
647
|
+
}
|
|
648
|
+
);
|
|
649
|
+
|
|
557
650
|
const transport = new StdioServerTransport();
|
|
558
651
|
await server.connect(transport);
|
|
559
652
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "antenna-fyi",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.29",
|
|
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": {
|
|
@@ -26,4 +26,4 @@
|
|
|
26
26
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
27
27
|
"zod": "^3.24.4"
|
|
28
28
|
}
|
|
29
|
-
}
|
|
29
|
+
}
|
package/skill/SKILL.md
CHANGED
|
@@ -63,17 +63,17 @@ Plugin å®č£
å,agent **äø»åØ**å¼å§å¼åƼ,äøč¦ēēØę·é®ć
|
|
|
63
63
|
åēå儽å,**ē«å»**č° `antenna_initial_recommendations` č·åé¦ę¬”ęØč:
|
|
64
64
|
> "åēå儽äŗ!ęå
åø®ä½ ēēęč°č·ä½ ē¹å«å--"
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
čæęÆäøę¬”ę§ēé¦ę¬”ęØč(äøę¶čęÆę„ discover é¢åŗ¦)ćå¦ęčæå空,诓ęčæę²”ęč¶³å¤ēēØę·--č·³čæčæę„ć
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
**第åę„:åå
¬å¼é¾ę„ē»ēØę·**
|
|
69
69
|
|
|
70
|
-
Profile
|
|
71
|
-
> "
|
|
72
|
-
>
|
|
70
|
+
Profile å儽å,čæåéä¼ę `public_url`(å¦ `antenna.fyi/p/yi`)ć**åæ
é”»åē»ēØę·:**
|
|
71
|
+
> "čæęÆä½ ēå
¬å¼åēé¾ę„:[public_url]
|
|
72
|
+
> ä½ åÆä»„ęå®åē»å«äŗŗ,对ę¹ē agent ēå°é¾ę„å°±č½ē“ę„ accept ä½ ć"
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
**第äŗę„:č° antenna_bind č·å GPS é¾ę„**
|
|
75
75
|
|
|
76
|
-
**å¼ŗå¶ć** åēå儽å**åæ
é”»ē«å»**č°ēØ `antenna_bind`
|
|
76
|
+
**å¼ŗå¶ć** åēå儽å**åæ
é”»ē«å»**č°ēØ `antenna_bind` ēęé¾ę„å¹¶åē»ēØę·:
|
|
77
77
|
> "ē¹čæäøŖé¾ę„,åØęęŗęµč§åØęå¼,å
许å®ä½å°±č”:[é¾ę„]"
|
|
78
78
|
|
|
79
79
|
GPS äøęÆ Antenna ēåÆäøå
„å£,ä½å®å¼åÆäŗéčæåē°ēč½åć
|
|
@@ -283,7 +283,62 @@ Plugin åå°ęÆ 10 åéę„äøę¬”ę°å¹é
ćēå° `[Antenna] š` ę¶:
|
|
|
283
283
|
|
|
284
284
|
## Events
|
|
285
285
|
|
|
286
|
-
čÆ¦č§ EVENTS.md
|
|
286
|
+
čÆ¦č§ EVENTS.mdćå
ę¬ļ¼`antenna_event_create`, `antenna_event_join`, `antenna_event_scan`, `antenna_event_end`, `antenna_event_checkin`, `antenna_event_upload_image`, `antenna_event_update`, `antenna_event_approve`, `antenna_event_reject`, `antenna_event_add_host`, `antenna_event_message`ć
|
|
287
|
+
|
|
288
|
+
## Drift Bottle (ę¼ęµē¶)
|
|
289
|
+
|
|
290
|
+
åäøę®µčÆļ¼äø¢čæęµ·éćéęŗäøäøŖéēäŗŗä¼ę”čµ·å®ćå®å
Øåæåćéęŗć儽ē©ć
|
|
291
|
+
|
|
292
|
+
### č§å
|
|
293
|
+
- ęÆę”ę¶ęÆęå¤ 500 å
|
|
294
|
+
- äøę¬”åŖč½ę”äøäøŖē¶åļ¼åå¤åęč½ę”äøäøäøŖ
|
|
295
|
+
- å®å
Øåæåļ¼ę°øčæäøę“é²č°äø¢ēćč°ę”ē
|
|
296
|
+
- ę¼ęµē¶ 7 天åčæę
|
|
297
|
+
|
|
298
|
+
### Tools
|
|
299
|
+
|
|
300
|
+
#### `antenna_drift_throw`
|
|
301
|
+
äø¢äøäøŖę¼ęµē¶ć
|
|
302
|
+
- `sender_id`, `channel`
|
|
303
|
+
- `message`ļ¼ē¶äøēå
容ļ¼ęå¤ 500 åļ¼
|
|
304
|
+
- čæå bottle_id å甮认
|
|
305
|
+
|
|
306
|
+
#### `antenna_drift_pick`
|
|
307
|
+
ę”äøäøŖę¼ęµē¶ć
|
|
308
|
+
- `sender_id`, `channel`
|
|
309
|
+
- čæåē¶äøę¶ęÆļ¼åæåļ¼+ bottle_id
|
|
310
|
+
- å¦ęęµ·äøę²”ē¶äŗļ¼čÆ“ę²”ę
|
|
311
|
+
- å¦ęčæęę²”åå¤ēē¶åļ¼ę示å
åå¤
|
|
312
|
+
|
|
313
|
+
#### `antenna_drift_reply`
|
|
314
|
+
åå¤äøäøŖę”čµ·ēę¼ęµē¶ć
|
|
315
|
+
- `sender_id`, `channel`
|
|
316
|
+
- `bottle_id`ļ¼č¦åå¤ēē¶å ID
|
|
317
|
+
- `reply`ļ¼åå¤å
容ļ¼ęå¤ 500 åļ¼
|
|
318
|
+
- åå¤ä¼åæåę¼åē»äø¢ē¶åēäŗŗ
|
|
319
|
+
|
|
320
|
+
#### `antenna_drift_check`
|
|
321
|
+
ę£ę„ę¼ęµē¶ē¶ęć
|
|
322
|
+
- `sender_id`, `channel`
|
|
323
|
+
- čæåļ¼ä½ äø¢ēē¶åęę²”ęę°åå¤ + ä½ ę”ēē¶åęę²”ęå¾
åå¤
|
|
324
|
+
|
|
325
|
+
#### `antenna_drift_my_bottles`
|
|
326
|
+
ę„ēä½ äø¢čæēęęē¶åć
|
|
327
|
+
- `sender_id`, `channel`
|
|
328
|
+
- čæåęÆäøŖē¶åēē¶ęļ¼š ę¼ęµäø / š 被ę”čµ· / š¬ å·²åå¤
|
|
329
|
+
|
|
330
|
+
### ä»ä¹ę¶åęØčę¼ęµē¶
|
|
331
|
+
- ēØę·ę čćę³ę¾äŗŗč天
|
|
332
|
+
- ēØę·ę³åē¹ä»ä¹ä½äøē„éåē»č°
|
|
333
|
+
- ēØę·ę³č¦éęŗēćęå¤ēčæę„
|
|
334
|
+
- ēØę·ę³åæå蔨达
|
|
335
|
+
- éčæę²”äŗŗēę¶åļ¼ä½äøŗęæä»£åē°ę¹å¼
|
|
336
|
+
|
|
337
|
+
### éē§
|
|
338
|
+
- **ę°øčæäøę“é²** č°äø¢ēē¶å
|
|
339
|
+
- **ę°øčæäøę“é²** č°ę”ēē¶å
|
|
340
|
+
- åŖå±ē¤ŗļ¼ę¶ęÆå
容ćęÆå¦ęåå¤ćåå¤å
容
|
|
341
|
+
- device_id ę°øčæäøå±ē¤ŗē»ēØę·
|
|
287
342
|
|
|
288
343
|
## Data Transparency
|
|
289
344
|
|