antenna-fyi 1.3.23 → 1.3.25

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 CHANGED
@@ -15,6 +15,7 @@ import {
15
15
  handleSetup,
16
16
  handleConfig,
17
17
  handleStatus,
18
+ handleLinkAccount,
18
19
  handleInstallSkill,
19
20
  handleInstallPlugin,
20
21
  handleInstallHermesPlugin,
@@ -56,6 +57,8 @@ async function main() {
56
57
  return handleConfig(f);
57
58
  case "status":
58
59
  return handleStatus(f);
60
+ case "link-account":
61
+ return handleLinkAccount(f);
59
62
  case "install-skill":
60
63
  return handleInstallSkill();
61
64
  case "install-plugin":
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 } 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 } 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";
@@ -401,9 +401,11 @@ export async function handleConfig(f) {
401
401
  const config = loadConfig();
402
402
  config.key = f.key;
403
403
  config.device_id = result.device_id;
404
+ config.user_id = result.user_id;
404
405
  config.display_name = result.display_name;
405
406
  saveConfig(config);
406
407
  console.log(`\n✅ Authenticated as ${result.display_name || 'Antenna user'}`);
408
+ console.log(` User ID: ${result.user_id}`);
407
409
  console.log(` Device ID: ${result.device_id}`);
408
410
  console.log(` Config saved to ~/.antenna/config.json\n`);
409
411
  } catch (e) {
@@ -425,6 +427,26 @@ export async function handleConfig(f) {
425
427
  }
426
428
  }
427
429
 
430
+ export async function handleLinkAccount(f) {
431
+ const id = resolveId(f);
432
+ const config = loadConfig();
433
+ const apiKey = f['api-key'] || f.apiKey || f.key || config.key;
434
+ if (!id) return console.error("Usage: antenna link-account --id <device_id> --api-key <ant_xxx>\n Or: antenna link-account (uses config device_id + key)");
435
+ if (!apiKey) return console.error("❌ No API key. Run 'antenna config --key <your-key>' first, or pass --api-key <key>.");
436
+ try {
437
+ const result = await linkAccount({ device_id: id, api_key: apiKey });
438
+ if (result.error) {
439
+ console.error(`\n❌ ${result.message || result.error}`);
440
+ } else {
441
+ console.log(`\n✅ ${result.message || 'Account linked.'}`);
442
+ console.log(` Device ID: ${id}`);
443
+ console.log(` API Key: ${apiKey.slice(0, 10)}...\n`);
444
+ }
445
+ } catch (e) {
446
+ console.error(`\n❌ Failed: ${e.message}`);
447
+ }
448
+ }
449
+
428
450
  export async function handleStatus(f) {
429
451
  const supabaseUrl = process.env.ANTENNA_SUPABASE_URL || process.env.ANTENNA_URL || "https://bcudjloikmpcqwcptuyd.supabase.co";
430
452
  console.log("\n📡 Antenna Status\n");
package/lib/core.js CHANGED
@@ -917,13 +917,13 @@ export async function verifyApiKey({ key, supabaseUrl, supabaseKey }) {
917
917
  return data;
918
918
  }
919
919
 
920
- // ─── linkAccount (bind user_id to device_id) ─────────────────────────
920
+ // ─── linkAccount (bind device_id to website account via API key) ─────
921
921
 
922
- export async function linkAccount({ device_id, user_id, supabaseUrl, supabaseKey }) {
922
+ export async function linkAccount({ device_id, api_key, supabaseUrl, supabaseKey }) {
923
923
  const sb = getClient(supabaseUrl, supabaseKey);
924
924
  const { data, error } = await sb.rpc("bind_user_id", {
925
925
  p_device_id: device_id,
926
- p_user_id: user_id,
926
+ p_api_key: api_key,
927
927
  });
928
928
  if (error) throw new Error(error.message);
929
929
  if (data?.error) return data;
@@ -397,9 +397,9 @@ LINK_ACCOUNT_SCHEMA = {
397
397
  "sender_id": {"type": "string", "description": "The sender's user ID"},
398
398
  "channel": {"type": "string", "description": "Platform name"},
399
399
  "chat_id": {"type": "string", "description": "REQUIRED. Pass the chat/channel ID from message context."},
400
- "user_id": {"type": "string", "description": "The user's antenna.fyi account UUID, visible on their dashboard"},
400
+ "api_key": {"type": "string", "description": "The user's Antenna API key (ant_xxx) from antenna.fyi/me"},
401
401
  },
402
- "required": ["sender_id", "channel", "chat_id", "user_id"],
402
+ "required": ["sender_id", "channel", "chat_id", "api_key"],
403
403
  },
404
404
  }
405
405
 
@@ -726,7 +726,7 @@ def handle_link_account(params: dict) -> str:
726
726
 
727
727
  resp = sb.rpc("bind_user_id", {
728
728
  "p_device_id": did,
729
- "p_user_id": params["user_id"],
729
+ "p_api_key": params["api_key"],
730
730
  }).execute()
731
731
 
732
732
  if resp.data and resp.data.get("error"):
package/lib/mcp.js CHANGED
@@ -540,15 +540,15 @@ export async function startMcpServer() {
540
540
 
541
541
  server.tool(
542
542
  "antenna_link_account",
543
- "Link your Antenna agent profile to your antenna.fyi website account. The user needs to provide their user_id from the dashboard (antenna.fyi/me). After linking, the dashboard will show the same profile and match history.",
543
+ "Link your Antenna agent profile to your antenna.fyi website account. Pass the user's API key the server verifies it and extracts the user_id. The agent never needs to know or pass user_id directly.",
544
544
  {
545
545
  sender_id: z.string().describe("The sender's user ID"),
546
546
  channel: z.string().describe("Channel name"),
547
- user_id: z.string().describe("The user's antenna.fyi account UUID, visible on their dashboard"),
547
+ api_key: z.string().describe("The user's Antenna API key (ant_xxx) from antenna.fyi/me"),
548
548
  },
549
- async ({ sender_id, channel, user_id }) => {
549
+ async ({ sender_id, channel, api_key }) => {
550
550
  try {
551
- const result = await linkAccount({ device_id: deriveDeviceId(sender_id, channel), user_id });
551
+ const result = await linkAccount({ device_id: deriveDeviceId(sender_id, channel), api_key });
552
552
  return jsonResult(result);
553
553
  } catch (e) { return jsonResult({ error: e.message }); }
554
554
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "antenna-fyi",
3
- "version": "1.3.23",
3
+ "version": "1.3.25",
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": {
package/skill/SKILL.md CHANGED
@@ -24,33 +24,39 @@ Antenna 帮你的用户发现值得认识的人。不只是"附近的人"--profi
24
24
  - 如果没有,用 CLI:`antenna <command> [flags]`。
25
25
  - **所有 tool 必须传 `chat_id`**--Antenna 用它发通知。
26
26
 
27
- ## Onboarding - 装上后主动引导
27
+ ## Onboarding 装上后主动引导
28
28
 
29
- Plugin 安装后,agent **主动**开始引导,不要等用户问。
29
+ Plugin 安装后,agent **主动**开始引导,不要等用户问。
30
30
 
31
- **前置条件:** 用户需要先在 antenna.fyi 注册账号,从 Dashboard 获取 API key。如果没注册,引导去 antenna.fyi。
31
+ **前置条件:** 用户需要先在 antenna.fyi 注册账号,从 Dashboard 获取 API key。如果没注册,引导去 antenna.fyi。
32
32
 
33
33
  ### 流程
34
34
 
35
- **第一步:打招呼 + 解释**
36
- > "嘿,你装了 Antenna--它能帮你发现有意思的人。先确认一下,你在 antenna.fyi 注册过了吗?拿到 API key 了吗?"
35
+ **第一步:拿到 API key → 配置**
36
+ > "嘿,你装了 Antenna——它能帮你发现有意思的人。先确认一下,你在 antenna.fyi 注册过了吗?拿到 API key 了吗?"
37
37
 
38
- **第二步:聊天收集 生成名片 确认**
38
+ 用户给了 API key 后,调 `antenna config --key <key>` 验证。这会返回 `user_id` 和 `device_id`。
39
39
 
40
- 跟用户聊几句,了解他们是谁、做什么、想认识什么人。然后 agent 自己生成:
41
- - more_information(~200 字,给 agent 匹配用的私密上下文,不展示给别人)
40
+ **⚠️ 之后所有操作必须用 API key 验证返回的 device_id(格式 `user:xxx`)。不要自己拼 `channel:sender_id`。** 这样 agent 创建的 profile 才能在 dashboard 上显示。
41
+
42
+ **第二步:聊天收集 → 生成名片 → 确认**
43
+
44
+ 跟用户聊几句,了解他们是谁、做什么、想认识什么人。然后 agent 自己生成:
45
+ - more_information(~200 字,给 agent 匹配用的私密上下文,不展示给别人)
42
46
  - 从中提炼 line1/2/3 + display_name
43
47
 
44
- 展示预览给用户确认:
45
- > 你的名片:
48
+ 展示预览给用户确认:
49
+ > 你的名片:
46
50
  > Yi
47
- > · Product Designer,做 AI 搜索体验
51
+ > · Product Designer,做 AI 搜索体验
48
52
  > · 对 AI agent、音乐、游泳感兴趣
49
53
  > · 想认识做 AI 产品和独立创作的人
50
54
  >
51
- > 这样可以吗?要改哪里告诉我。
55
+ > 这样可以吗?要改哪里告诉我。
56
+
57
+ 确认后用 config 里的 device_id 调 `antenna_profile(action="set")` 保存。**不要跳过确认。**
52
58
 
53
- 确认后才调 `antenna_profile(action="set")` 保存。**不要跳过确认。**
59
+ **⚠️ sender_id 用 config 里的 device_id,不要用 channel:sender_id。**
54
60
 
55
61
  **第三步:立刻推荐 2-3 个人**
56
62
 
@@ -79,8 +85,8 @@ openclaw cron add --every 1h --message "Check antenna matches: call antenna_chec
79
85
  ### Linking to antenna.fyi account
80
86
 
81
87
  如果用户之前通过 agent 创建过 profile(没有网站账号),现在注册了 antenna.fyi:
82
- 1. 让用户从 antenna.fyi/me 复制 user ID
83
- 2. 调 `antenna_link_account(user_id = "xxx")`
88
+ 1. 让用户从 antenna.fyi/me 复制 API key
89
+ 2. 调 `antenna_link_account(api_key = "ant_xxx")`
84
90
  3. 确认:"关联成功!你现在可以在 dashboard 上看到完整的 profile 和匹配记录了。"
85
91
 
86
92
  这把 agent 创建的 profile(带全部历史)关联到网站账号。
@@ -179,15 +185,15 @@ openclaw cron add --every 1h --message "Check antenna matches: call antenna_chec
179
185
  - 一次性操作
180
186
 
181
187
  ### `antenna_discover`
182
- 全球推荐——每天 1 个最匹配的人。
188
+ 全球推荐--每天 1 个最匹配的人。
183
189
  - `sender_id`, `channel`, `chat_id`
184
190
  - 不需要 GPS
185
- - 如果所有人都推荐过了,返回“等新人加入”
191
+ - 如果所有人都推荐过了,返回"等新人加入"
186
192
 
187
193
  ### `antenna_initial_recommendations`
188
- 首次推荐——注册后立刻看到 2-3 个最匹配的人。
194
+ 首次推荐--注册后立刻看到 2-3 个最匹配的人。
189
195
  - `sender_id`, `channel`, `chat_id`: from context
190
- - One-time only second call returns empty
196
+ - One-time only - second call returns empty
191
197
  - Does NOT consume daily discover quota
192
198
  - Use in onboarding step 3, right after profile save
193
199