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 +2 -0
- package/lib/core.js +9 -0
- package/lib/hermes-plugin/schemas.py +4 -0
- package/lib/hermes-plugin/tools.py +8 -2
- package/lib/mcp.js +4 -3
- package/package.json +1 -1
- package/skill/SKILL.md +4 -3
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
|
|
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
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
|
|
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
|
跳过一个人,不再推荐。
|