antenna-fyi 1.3.43 → 1.3.44

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
@@ -124,12 +124,14 @@ export async function handleProfile(f) {
124
124
  export async function handleAccept(f) {
125
125
  const id = resolveId(f);
126
126
  if (!id || (!f.target && !f.ref)) return console.error("Usage: antenna accept --id <platform>:<user_id> --ref 1 [--contact 'WeChat: yi']\n antenna accept --id <platform>:<user_id> --target <ref_or_device_id> [--contact 'WeChat: yi']");
127
+ const config = loadConfig();
127
128
  const result = await accept({
128
129
  device_id: id,
129
130
  target_device_id: f.target && f.target.includes(':') ? f.target : null,
130
131
  profile_slug: f.target && !f.target.includes(':') ? f.target : null,
131
132
  ref: f.ref || null,
132
133
  contact_info: f.contact,
134
+ api_key: config.key || null,
133
135
  });
134
136
  console.log("✅ " + result.message);
135
137
  if (result.mutual && result.their_contact) console.log("📇 Their contact: " + result.their_contact);
package/lib/core.js CHANGED
@@ -461,10 +461,18 @@ export async function accept({
461
461
  ref,
462
462
  profile_slug,
463
463
  contact_info,
464
+ api_key,
464
465
  supabaseUrl,
465
466
  supabaseKey,
466
467
  }) {
467
468
  const sb = getClient(supabaseUrl, supabaseKey);
469
+ if (api_key) {
470
+ const auth = await verifyApiKey({ key: api_key, supabaseUrl, supabaseKey });
471
+ if (!auth?.valid) {
472
+ throw new Error(auth?.error || "Invalid Antenna API key");
473
+ }
474
+ device_id = auth.device_id || (auth.user_id ? `user:${auth.user_id}` : device_id);
475
+ }
468
476
 
469
477
  // Resolve ref from DB if target_device_id not provided
470
478
  let targetId = target_device_id;
@@ -509,6 +517,7 @@ export async function accept({
509
517
  return {
510
518
  accepted: true,
511
519
  mutual,
520
+ dashboard_device_id: device_id,
512
521
  their_contact: mutual ? reverse?.contact_info_a || null : null,
513
522
  message: mutual
514
523
  ? "双向匹配成功!🎉"
@@ -91,6 +91,10 @@ ACCEPT_SCHEMA = {
91
91
  "type": "string",
92
92
  "description": "Contact info to share (e.g. 'WeChat: yi')",
93
93
  },
94
+ "api_key": {
95
+ "type": "string",
96
+ "description": "User's Antenna API key from antenna.fyi/me. When provided, accept is written as the dashboard-linked profile, not a temporary sender/channel device.",
97
+ },
94
98
  },
95
99
  "required": ["sender_id", "channel", "chat_id"],
96
100
  },
@@ -50,7 +50,7 @@ def _dashboard_device_id(sb, api_key: str | None) -> tuple[str | None, str | Non
50
50
  data = resp.data or {}
51
51
  if not data.get("valid"):
52
52
  return None, data.get("error") or "Invalid Antenna API key"
53
- device_id = f"user:{data.get('user_id')}" if data.get("user_id") else data.get("device_id")
53
+ device_id = data.get("device_id") or (f"user:{data.get('user_id')}" if data.get("user_id") else None)
54
54
  if not device_id:
55
55
  return None, "API key verified but did not return a dashboard device_id"
56
56
  return device_id, None
@@ -253,6 +253,11 @@ def handle_profile(params: dict) -> str:
253
253
  def handle_accept(params: dict) -> str:
254
254
  sb = _sb()
255
255
  did = _device_id(params["sender_id"], params["channel"], params.get("chat_id"))
256
+ if params.get("api_key"):
257
+ resolved_did, auth_error = _dashboard_device_id(sb, params.get("api_key"))
258
+ if auth_error:
259
+ return _ok({"error": auth_error})
260
+ did = resolved_did or did
256
261
 
257
262
  # Resolve ref to device_id
258
263
  ref = params.get("ref")
@@ -299,11 +304,12 @@ def handle_accept(params: dict) -> str:
299
304
  if reverse:
300
305
  contact = reverse.get("contact_info_a")
301
306
  msg = f"双方都接受了!对方分享的联系方式:{contact}" if contact else "双方都接受了!但对方还没有分享联系方式。"
302
- return _ok({"accepted": True, "mutual": True, "their_contact": contact, "message": msg})
307
+ return _ok({"accepted": True, "mutual": True, "dashboard_device_id": did, "their_contact": contact, "message": msg})
303
308
 
304
309
  return _ok({
305
310
  "accepted": True,
306
311
  "mutual": False,
312
+ "dashboard_device_id": did,
307
313
  "message": "已接受。等对方也接受后,你们就可以交换联系方式了。",
308
314
  })
309
315
 
package/lib/mcp.js CHANGED
@@ -160,12 +160,13 @@ export async function startMcpServer() {
160
160
  target_device_id: z.string().optional().describe("Device ID (use ref or profile_slug instead when possible)"),
161
161
  profile_slug: z.string().optional().describe("Profile slug from a public profile link (e.g. 'yi' from antenna.fyi/p/yi). Resolves to device_id automatically."),
162
162
  contact_info: z.string().optional().describe("Contact info to share"),
163
+ api_key: z.string().optional().describe("User's Antenna API key from antenna.fyi/me. When provided, accept is written as the dashboard-linked profile, not a temporary sender/channel device."),
163
164
  },
164
- async ({ sender_id, channel, ref, target_device_id, profile_slug, contact_info }) => {
165
+ async ({ sender_id, channel, ref, target_device_id, profile_slug, contact_info, api_key }) => {
165
166
  try {
166
167
  const deviceId = deriveDeviceId(sender_id, channel);
167
- const result = await accept({ device_id: deviceId, target_device_id, ref, profile_slug, contact_info });
168
- return jsonResult(await withMatchNotifications(deviceId, result));
168
+ const result = await accept({ device_id: deviceId, target_device_id, ref, profile_slug, contact_info, api_key });
169
+ return jsonResult(await withMatchNotifications(result.dashboard_device_id || deviceId, result));
169
170
  } catch (e) {
170
171
  return jsonResult({ error: e.message });
171
172
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "antenna-fyi",
3
- "version": "1.3.43",
3
+ "version": "1.3.44",
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
@@ -40,7 +40,7 @@ Plugin 安装后,agent **主动**开始引导,不要等用户问。
40
40
 
41
41
  用户给了 API key 后,调 `antenna config --key <key>` 验证。这会返回 `user_id` 和 dashboard 绑定的 `device_id`。
42
42
 
43
- **⚠️ Profile 写入必须通过用户的 Antenna API key。** 调 `antenna_profile(action="set")` 时传 `api_key`,tool 会验证 API key 并使用返回的 dashboard `device_id`(格式 `user:xxx`) 写入。不要自己拼 `channel:sender_id`,不要在用户拿到 API key 前凭空创建 profile。这样 agent 填的内容才会显示在 dashboard。
43
+ **⚠️ Profile 写入和 accept 都必须尽量通过用户的 Antenna API key。** 调 `antenna_profile(action="set")` 时传 `api_key`,tool 会验证 API key 并使用返回的 dashboard-linked `device_id` 写入。调 `antenna_accept` 时如果用户已经有 API key,也传同一个 `api_key`,这样 match 会关联到用户的 dashboard profile,而不是临时 `sender_id/channel` profile。不要自己拼 `channel:sender_id`,不要在用户拿到 API key 前凭空创建 profile。这样 agent 填的内容和 match 才会显示在 dashboard。
44
44
 
45
45
  **第二步:聊天收集 → 生成名片 → 确认**
46
46
 
@@ -109,7 +109,7 @@ Legacy only: 如果用户之前通过旧版 agent 创建过 profile(没有网站
109
109
  - 用户分享位置 → `antenna_scan`
110
110
  - 用户问"附近有谁" → `antenna_scan`
111
111
  - 用户说"我想找一个 xxx 的人" → `antenna_find_people`
112
- - 用户收到 profile 链接(`antenna.fyi/p/xxx`)→ 读取 profile → 判断 → `antenna_accept`
112
+ - 用户收到 profile 链接(`antenna.fyi/p/xxx`)→ 读取 profile → 判断 → `antenna_accept(profile_slug="xxx", api_key="ant_xxx")`
113
113
  - 用户想编辑名片 → `antenna_profile`
114
114
  - 用户说 accept / skip → `antenna_accept` / `antenna_pass`
115
115
  - 用户问匹配状态 → `antenna_check_matches`
@@ -140,7 +140,7 @@ Legacy only: 如果用户之前通过旧版 agent 创建过 profile(没有网站
140
140
  1. 用 `web_fetch` 读取页面--页面里有 `<script id="antenna-profile-data">` JSON,包含完整 profile
141
141
  2. 读取 more_information、interest_tags、个人描述等
142
142
  3. 结合你对用户的了解,判断是否推荐
143
- 4. 如果用户想 accept → 调 `antenna_accept(profile_slug="xxx")`
143
+ 4. 如果用户想 accept → 调 `antenna_accept(profile_slug="xxx", api_key="ant_xxx")`
144
144
 
145
145
  **不需要先 scan。** Profile 链接是独立的发现路径。
146
146
 
@@ -183,6 +183,7 @@ Legacy only: 如果用户之前通过旧版 agent 创建过 profile(没有网站
183
183
  - `profile_slug`:来自 profile 链接(如 `antenna.fyi/p/yi` → `profile_slug="yi"`)
184
184
  - `target_device_id`:内部 ID(尽量用 ref 或 slug)
185
185
  - `contact_info`(可选):分享联系方式
186
+ - `api_key`(强烈建议):用户从 dashboard 拿到的 Antenna API key。传了之后 accept 会写到 dashboard-linked profile;否则只能退回临时 `sender_id/channel` device,dashboard 关联可能不完整。
186
187
 
187
188
  ### `antenna_pass`
188
189
  跳过一个人,不再推荐。